1 /** @file
2   Set a IDT entry for debug purpose
3 
4   Set a IDT entry for interrupt vector 3 for debug purpose for x64 platform
5 
6 Copyright (c) 2006 - 2019, Intel Corporation. All rights reserved.<BR>
7 Copyright (c) 2017, AMD Incorporated. All rights reserved.<BR>
8 
9 
10 SPDX-License-Identifier: BSD-2-Clause-Patent
11 
12 **/
13 #include "ScriptExecute.h"
14 
15 //
16 // 8 extra pages for PF handler.
17 //
18 #define EXTRA_PAGE_TABLE_PAGES      8
19 
20 #define IA32_PG_P                   BIT0
21 #define IA32_PG_RW                  BIT1
22 #define IA32_PG_PS                  BIT7
23 
24 UINT64                              mPhyMask;
25 VOID                                *mOriginalHandler;
26 UINTN                               mPageFaultBuffer;
27 UINTN                               mPageFaultIndex = 0;
28 //
29 // Store the uplink information for each page being used.
30 //
31 UINT64                              *mPageFaultUplink[EXTRA_PAGE_TABLE_PAGES];
32 
33 /**
34   Page fault handler.
35 
36 **/
37 VOID
38 EFIAPI
39 PageFaultHandlerHook (
40   VOID
41   );
42 
43 /**
44   Hook IDT with our page fault handler so that the on-demand paging works on page fault.
45 
46   @param  IdtEntry  a pointer to IDT entry
47 
48 **/
49 VOID
HookPageFaultHandler(IN IA32_IDT_GATE_DESCRIPTOR * IdtEntry)50 HookPageFaultHandler (
51   IN IA32_IDT_GATE_DESCRIPTOR                   *IdtEntry
52   )
53 {
54   UINT32         RegEax;
55   UINT8          PhysicalAddressBits;
56   UINTN          PageFaultHandlerHookAddress;
57 
58   AsmCpuid (0x80000000, &RegEax, NULL, NULL, NULL);
59   if (RegEax >= 0x80000008) {
60     AsmCpuid (0x80000008, &RegEax, NULL, NULL, NULL);
61     PhysicalAddressBits = (UINT8) RegEax;
62   } else {
63     PhysicalAddressBits = 36;
64   }
65   mPhyMask = LShiftU64 (1, PhysicalAddressBits) - 1;
66   mPhyMask &= (1ull << 48) - SIZE_4KB;
67 
68   //
69   // Set Page Fault entry to catch >4G access
70   //
71   PageFaultHandlerHookAddress = (UINTN)PageFaultHandlerHook;
72   mOriginalHandler = (VOID *)(UINTN)(LShiftU64 (IdtEntry->Bits.OffsetUpper, 32) + IdtEntry->Bits.OffsetLow + (IdtEntry->Bits.OffsetHigh << 16));
73   IdtEntry->Bits.OffsetLow      = (UINT16)PageFaultHandlerHookAddress;
74   IdtEntry->Bits.Selector       = (UINT16)AsmReadCs ();
75   IdtEntry->Bits.Reserved_0     = 0;
76   IdtEntry->Bits.GateType       = IA32_IDT_GATE_TYPE_INTERRUPT_32;
77   IdtEntry->Bits.OffsetHigh     = (UINT16)(PageFaultHandlerHookAddress >> 16);
78   IdtEntry->Bits.OffsetUpper    = (UINT32)(PageFaultHandlerHookAddress >> 32);
79   IdtEntry->Bits.Reserved_1     = 0;
80 
81   if (mPage1GSupport) {
82     mPageFaultBuffer = (UINTN)(AsmReadCr3 () & mPhyMask) + EFI_PAGES_TO_SIZE(2);
83   }else {
84     mPageFaultBuffer = (UINTN)(AsmReadCr3 () & mPhyMask) + EFI_PAGES_TO_SIZE(6);
85   }
86   ZeroMem (mPageFaultUplink, sizeof (mPageFaultUplink));
87 }
88 
89 /**
90   The function will check if current waking vector is long mode.
91 
92   @param  AcpiS3Context                 a pointer to a structure of ACPI_S3_CONTEXT
93 
94   @retval TRUE   Current context need long mode waking vector.
95   @retval FALSE  Current context need not long mode waking vector.
96 **/
97 BOOLEAN
IsLongModeWakingVector(IN ACPI_S3_CONTEXT * AcpiS3Context)98 IsLongModeWakingVector (
99   IN ACPI_S3_CONTEXT                *AcpiS3Context
100   )
101 {
102   EFI_ACPI_4_0_FIRMWARE_ACPI_CONTROL_STRUCTURE  *Facs;
103 
104   Facs = (EFI_ACPI_4_0_FIRMWARE_ACPI_CONTROL_STRUCTURE *) ((UINTN) (AcpiS3Context->AcpiFacsTable));
105   if ((Facs == NULL) ||
106       (Facs->Signature != EFI_ACPI_4_0_FIRMWARE_ACPI_CONTROL_STRUCTURE_SIGNATURE) ||
107       ((Facs->FirmwareWakingVector == 0) && (Facs->XFirmwareWakingVector == 0)) ) {
108     // Something wrong with FACS
109     return FALSE;
110   }
111   if (Facs->XFirmwareWakingVector != 0) {
112     if ((Facs->Version == EFI_ACPI_4_0_FIRMWARE_ACPI_CONTROL_STRUCTURE_VERSION) &&
113         ((Facs->Flags & EFI_ACPI_4_0_64BIT_WAKE_SUPPORTED_F) != 0) &&
114         ((Facs->OspmFlags & EFI_ACPI_4_0_OSPM_64BIT_WAKE__F) != 0)) {
115       // Both BIOS and OS wants 64bit vector
116       if (FeaturePcdGet (PcdDxeIplSwitchToLongMode)) {
117         return TRUE;
118       }
119     }
120   }
121   return FALSE;
122 }
123 
124 /**
125   Set a IDT entry for interrupt vector 3 for debug purpose.
126 
127   @param  AcpiS3Context  a pointer to a structure of ACPI_S3_CONTEXT
128 
129 **/
130 VOID
SetIdtEntry(IN ACPI_S3_CONTEXT * AcpiS3Context)131 SetIdtEntry (
132   IN ACPI_S3_CONTEXT     *AcpiS3Context
133   )
134 {
135   IA32_IDT_GATE_DESCRIPTOR                      *IdtEntry;
136   IA32_DESCRIPTOR                               *IdtDescriptor;
137   UINTN                                         S3DebugBuffer;
138   EFI_STATUS                                    Status;
139 
140   //
141   // Restore IDT for debug
142   //
143   IdtDescriptor = (IA32_DESCRIPTOR *) (UINTN) (AcpiS3Context->IdtrProfile);
144   AsmWriteIdtr (IdtDescriptor);
145 
146   //
147   // Setup the default CPU exception handlers
148   //
149   Status = InitializeCpuExceptionHandlers (NULL);
150   ASSERT_EFI_ERROR (Status);
151 
152   DEBUG_CODE (
153     //
154     // Update IDT entry INT3 if the instruction is valid in it
155     //
156     S3DebugBuffer = (UINTN) (AcpiS3Context->S3DebugBufferAddress);
157     if (*(UINTN *)S3DebugBuffer != (UINTN) -1) {
158       IdtEntry = (IA32_IDT_GATE_DESCRIPTOR *)(IdtDescriptor->Base + (3 * sizeof (IA32_IDT_GATE_DESCRIPTOR)));
159       IdtEntry->Bits.OffsetLow      = (UINT16)S3DebugBuffer;
160       IdtEntry->Bits.Selector       = (UINT16)AsmReadCs ();
161       IdtEntry->Bits.Reserved_0     = 0;
162       IdtEntry->Bits.GateType       = IA32_IDT_GATE_TYPE_INTERRUPT_32;
163       IdtEntry->Bits.OffsetHigh     = (UINT16)(S3DebugBuffer >> 16);
164       IdtEntry->Bits.OffsetUpper    = (UINT32)(S3DebugBuffer >> 32);
165       IdtEntry->Bits.Reserved_1     = 0;
166     }
167   );
168 
169   //
170   // If both BIOS and OS wants long mode waking vector,
171   // S3ResumePei should have established 1:1 Virtual to Physical identity mapping page table,
172   // no need to hook page fault handler.
173   //
174   if (!IsLongModeWakingVector (AcpiS3Context)) {
175     IdtEntry = (IA32_IDT_GATE_DESCRIPTOR *)(IdtDescriptor->Base + (14 * sizeof (IA32_IDT_GATE_DESCRIPTOR)));
176     HookPageFaultHandler (IdtEntry);
177   }
178 }
179 
180 /**
181   Acquire page for page fault.
182 
183   @param[in, out] Uplink        Pointer to up page table entry.
184 
185 **/
186 VOID
AcquirePage(IN OUT UINT64 * Uplink)187 AcquirePage (
188   IN OUT UINT64                 *Uplink
189   )
190 {
191   UINTN             Address;
192 
193   Address = mPageFaultBuffer + EFI_PAGES_TO_SIZE (mPageFaultIndex);
194   ZeroMem ((VOID *) Address, EFI_PAGES_TO_SIZE (1));
195 
196   //
197   // Cut the previous uplink if it exists and wasn't overwritten.
198   //
199   if ((mPageFaultUplink[mPageFaultIndex] != NULL) &&
200      ((*mPageFaultUplink[mPageFaultIndex] & ~mAddressEncMask & mPhyMask) == Address)) {
201     *mPageFaultUplink[mPageFaultIndex] = 0;
202   }
203 
204   //
205   // Link & Record the current uplink.
206   //
207   *Uplink = Address | mAddressEncMask | IA32_PG_P | IA32_PG_RW;
208   mPageFaultUplink[mPageFaultIndex] = Uplink;
209 
210   mPageFaultIndex = (mPageFaultIndex + 1) % EXTRA_PAGE_TABLE_PAGES;
211 }
212 
213 /**
214   The page fault handler that on-demand read >4G memory/MMIO.
215 
216   @retval TRUE     The page fault is correctly handled.
217   @retval FALSE    The page fault is not handled and is passed through to original handler.
218 
219 **/
220 BOOLEAN
221 EFIAPI
PageFaultHandler(VOID)222 PageFaultHandler (
223   VOID
224   )
225 {
226   UINT64         *PageTable;
227   UINT64         PFAddress;
228   UINTN          PTIndex;
229 
230   PFAddress = AsmReadCr2 ();
231   DEBUG ((DEBUG_INFO, "BootScript - PageFaultHandler: Cr2 - %lx\n", PFAddress));
232 
233   if (PFAddress >= mPhyMask + SIZE_4KB) {
234     return FALSE;
235   }
236   PFAddress &= mPhyMask;
237 
238   PageTable = (UINT64*)(UINTN)(AsmReadCr3 () & mPhyMask);
239 
240   PTIndex = BitFieldRead64 (PFAddress, 39, 47);
241   // PML4E
242   if ((PageTable[PTIndex] & IA32_PG_P) == 0) {
243     AcquirePage (&PageTable[PTIndex]);
244   }
245   PageTable = (UINT64*)(UINTN)(PageTable[PTIndex] & ~mAddressEncMask & mPhyMask);
246   PTIndex = BitFieldRead64 (PFAddress, 30, 38);
247   // PDPTE
248   if (mPage1GSupport) {
249     PageTable[PTIndex] = ((PFAddress | mAddressEncMask) & ~((1ull << 30) - 1)) | IA32_PG_P | IA32_PG_RW | IA32_PG_PS;
250   } else {
251     if ((PageTable[PTIndex] & IA32_PG_P) == 0) {
252       AcquirePage (&PageTable[PTIndex]);
253     }
254     PageTable = (UINT64*)(UINTN)(PageTable[PTIndex] & ~mAddressEncMask & mPhyMask);
255     PTIndex = BitFieldRead64 (PFAddress, 21, 29);
256     // PD
257     PageTable[PTIndex] = ((PFAddress | mAddressEncMask) & ~((1ull << 21) - 1)) | IA32_PG_P | IA32_PG_RW | IA32_PG_PS;
258   }
259 
260   return TRUE;
261 }
262