2 The X64 entrypoint is used to process capsule in long mode.
4 Copyright (c) 2011 - 2018, Intel Corporation. All rights reserved.<BR>
5 Copyright (c) 2017, AMD Incorporated. All rights reserved.<BR>
7 SPDX-License-Identifier: BSD-2-Clause-Patent
11 #include <Library/DebugLib.h>
12 #include <Library/BaseMemoryLib.h>
13 #include <Library/CpuExceptionHandlerLib.h>
14 #include <Library/DebugAgentLib.h>
15 #include "CommonHeader.h"
17 #define EXCEPTION_VECTOR_NUMBER 0x22
19 #define IA32_PG_P BIT0
20 #define IA32_PG_RW BIT1
21 #define IA32_PG_PS BIT7
23 typedef struct _PAGE_FAULT_CONTEXT
{
24 BOOLEAN Page1GSupport
;
26 UINTN PageFaultBuffer
;
28 UINT64 AddressEncMask
;
30 // Store the uplink information for each page being used.
32 UINT64
*PageFaultUplink
[EXTRA_PAGE_TABLE_PAGES
];
33 VOID
*OriginalHandler
;
36 typedef struct _PAGE_FAULT_IDT_TABLE
{
37 PAGE_FAULT_CONTEXT PageFaultContext
;
38 IA32_IDT_GATE_DESCRIPTOR IdtEntryTable
[EXCEPTION_VECTOR_NUMBER
];
39 } PAGE_FAULT_IDT_TABLE
;
47 PageFaultHandlerHook (
52 Hook IDT with our page fault handler so that the on-demand paging works on page fault.
54 @param[in, out] IdtEntry Pointer to IDT entry.
55 @param[in, out] PageFaultContext Pointer to page fault context.
59 HookPageFaultHandler (
60 IN OUT IA32_IDT_GATE_DESCRIPTOR
*IdtEntry
,
61 IN OUT PAGE_FAULT_CONTEXT
*PageFaultContext
65 UINT8 PhysicalAddressBits
;
66 UINTN PageFaultHandlerHookAddress
;
68 AsmCpuid (0x80000000, &RegEax
, NULL
, NULL
, NULL
);
69 if (RegEax
>= 0x80000008) {
70 AsmCpuid (0x80000008, &RegEax
, NULL
, NULL
, NULL
);
71 PhysicalAddressBits
= (UINT8
)RegEax
;
73 PhysicalAddressBits
= 36;
76 PageFaultContext
->PhyMask
= LShiftU64 (1, PhysicalAddressBits
) - 1;
77 PageFaultContext
->PhyMask
&= (1ull << 48) - SIZE_4KB
;
80 // Set Page Fault entry to catch >4G access
82 PageFaultHandlerHookAddress
= (UINTN
)PageFaultHandlerHook
;
83 PageFaultContext
->OriginalHandler
= (VOID
*)(UINTN
)(LShiftU64 (IdtEntry
->Bits
.OffsetUpper
, 32) + IdtEntry
->Bits
.OffsetLow
+ (IdtEntry
->Bits
.OffsetHigh
<< 16));
84 IdtEntry
->Bits
.OffsetLow
= (UINT16
)PageFaultHandlerHookAddress
;
85 IdtEntry
->Bits
.Selector
= (UINT16
)AsmReadCs ();
86 IdtEntry
->Bits
.Reserved_0
= 0;
87 IdtEntry
->Bits
.GateType
= IA32_IDT_GATE_TYPE_INTERRUPT_32
;
88 IdtEntry
->Bits
.OffsetHigh
= (UINT16
)(PageFaultHandlerHookAddress
>> 16);
89 IdtEntry
->Bits
.OffsetUpper
= (UINT32
)(PageFaultHandlerHookAddress
>> 32);
90 IdtEntry
->Bits
.Reserved_1
= 0;
92 if (PageFaultContext
->Page1GSupport
) {
93 PageFaultContext
->PageFaultBuffer
= (UINTN
)(AsmReadCr3 () & PageFaultContext
->PhyMask
) + EFI_PAGES_TO_SIZE (2);
95 PageFaultContext
->PageFaultBuffer
= (UINTN
)(AsmReadCr3 () & PageFaultContext
->PhyMask
) + EFI_PAGES_TO_SIZE (6);
98 PageFaultContext
->PageFaultIndex
= 0;
99 ZeroMem (PageFaultContext
->PageFaultUplink
, sizeof (PageFaultContext
->PageFaultUplink
));
103 Acquire page for page fault.
105 @param[in, out] PageFaultContext Pointer to page fault context.
106 @param[in, out] Uplink Pointer to up page table entry.
111 IN OUT PAGE_FAULT_CONTEXT
*PageFaultContext
,
112 IN OUT UINT64
*Uplink
116 UINT64 AddressEncMask
;
118 Address
= PageFaultContext
->PageFaultBuffer
+ EFI_PAGES_TO_SIZE (PageFaultContext
->PageFaultIndex
);
119 ZeroMem ((VOID
*)Address
, EFI_PAGES_TO_SIZE (1));
121 AddressEncMask
= PageFaultContext
->AddressEncMask
;
124 // Cut the previous uplink if it exists and wasn't overwritten.
126 if ((PageFaultContext
->PageFaultUplink
[PageFaultContext
->PageFaultIndex
] != NULL
) &&
127 ((*PageFaultContext
->PageFaultUplink
[PageFaultContext
->PageFaultIndex
] & ~AddressEncMask
& PageFaultContext
->PhyMask
) == Address
))
129 *PageFaultContext
->PageFaultUplink
[PageFaultContext
->PageFaultIndex
] = 0;
133 // Link & Record the current uplink.
135 *Uplink
= Address
| AddressEncMask
| IA32_PG_P
| IA32_PG_RW
;
136 PageFaultContext
->PageFaultUplink
[PageFaultContext
->PageFaultIndex
] = Uplink
;
138 PageFaultContext
->PageFaultIndex
= (PageFaultContext
->PageFaultIndex
+ 1) % EXTRA_PAGE_TABLE_PAGES
;
142 The page fault handler that on-demand read >4G memory/MMIO.
144 @retval NULL The page fault is correctly handled.
145 @retval OriginalHandler The page fault is not handled and is passed through to original handler.
154 IA32_DESCRIPTOR Idtr
;
155 PAGE_FAULT_CONTEXT
*PageFaultContext
;
160 UINT64 AddressEncMask
;
163 // Get the IDT Descriptor.
165 AsmReadIdtr ((IA32_DESCRIPTOR
*)&Idtr
);
167 // Then get page fault context by IDT Descriptor.
169 PageFaultContext
= (PAGE_FAULT_CONTEXT
*)(UINTN
)(Idtr
.Base
- sizeof (PAGE_FAULT_CONTEXT
));
170 PhyMask
= PageFaultContext
->PhyMask
;
171 AddressEncMask
= PageFaultContext
->AddressEncMask
;
173 PFAddress
= AsmReadCr2 ();
174 DEBUG ((DEBUG_ERROR
, "CapsuleX64 - PageFaultHandler: Cr2 - %lx\n", PFAddress
));
176 if (PFAddress
>= PhyMask
+ SIZE_4KB
) {
177 return PageFaultContext
->OriginalHandler
;
180 PFAddress
&= PhyMask
;
182 PageTable
= (UINT64
*)(UINTN
)(AsmReadCr3 () & PhyMask
);
184 PTIndex
= BitFieldRead64 (PFAddress
, 39, 47);
186 if ((PageTable
[PTIndex
] & IA32_PG_P
) == 0) {
187 AcquirePage (PageFaultContext
, &PageTable
[PTIndex
]);
190 PageTable
= (UINT64
*)(UINTN
)(PageTable
[PTIndex
] & ~AddressEncMask
& PhyMask
);
191 PTIndex
= BitFieldRead64 (PFAddress
, 30, 38);
193 if (PageFaultContext
->Page1GSupport
) {
194 PageTable
[PTIndex
] = ((PFAddress
| AddressEncMask
) & ~((1ull << 30) - 1)) | IA32_PG_P
| IA32_PG_RW
| IA32_PG_PS
;
196 if ((PageTable
[PTIndex
] & IA32_PG_P
) == 0) {
197 AcquirePage (PageFaultContext
, &PageTable
[PTIndex
]);
200 PageTable
= (UINT64
*)(UINTN
)(PageTable
[PTIndex
] & ~AddressEncMask
& PhyMask
);
201 PTIndex
= BitFieldRead64 (PFAddress
, 21, 29);
203 PageTable
[PTIndex
] = ((PFAddress
| AddressEncMask
) & ~((1ull << 21) - 1)) | IA32_PG_P
| IA32_PG_RW
| IA32_PG_PS
;
210 The X64 entrypoint is used to process capsule in long mode then
211 return to 32-bit protected mode.
213 @param EntrypointContext Pointer to the context of long mode.
214 @param ReturnContext Pointer to the context of 32-bit protected mode.
216 @retval This function should never return actually.
222 SWITCH_32_TO_64_CONTEXT
*EntrypointContext
,
223 SWITCH_64_TO_32_CONTEXT
*ReturnContext
227 IA32_DESCRIPTOR Ia32Idtr
;
228 IA32_DESCRIPTOR X64Idtr
;
229 PAGE_FAULT_IDT_TABLE PageFaultIdtTable
;
230 IA32_IDT_GATE_DESCRIPTOR
*IdtEntry
;
233 // Save the IA32 IDT Descriptor
235 AsmReadIdtr ((IA32_DESCRIPTOR
*)&Ia32Idtr
);
238 // Setup X64 IDT table
240 ZeroMem (PageFaultIdtTable
.IdtEntryTable
, sizeof (IA32_IDT_GATE_DESCRIPTOR
) * EXCEPTION_VECTOR_NUMBER
);
241 X64Idtr
.Base
= (UINTN
)PageFaultIdtTable
.IdtEntryTable
;
242 X64Idtr
.Limit
= (UINT16
)(sizeof (IA32_IDT_GATE_DESCRIPTOR
) * EXCEPTION_VECTOR_NUMBER
- 1);
243 AsmWriteIdtr ((IA32_DESCRIPTOR
*)&X64Idtr
);
246 // Setup the default CPU exception handlers
248 Status
= InitializeCpuExceptionHandlers (NULL
);
249 ASSERT_EFI_ERROR (Status
);
252 // Hook page fault handler to handle >4G request.
254 PageFaultIdtTable
.PageFaultContext
.Page1GSupport
= EntrypointContext
->Page1GSupport
;
255 PageFaultIdtTable
.PageFaultContext
.AddressEncMask
= EntrypointContext
->AddressEncMask
;
256 IdtEntry
= (IA32_IDT_GATE_DESCRIPTOR
*)(X64Idtr
.Base
+ (14 * sizeof (IA32_IDT_GATE_DESCRIPTOR
)));
257 HookPageFaultHandler (IdtEntry
, &(PageFaultIdtTable
.PageFaultContext
));
260 // Initialize Debug Agent to support source level debug
262 InitializeDebugAgent (DEBUG_AGENT_INIT_THUNK_PEI_IA32TOX64
, (VOID
*)&Ia32Idtr
, NULL
);
265 // Call CapsuleDataCoalesce to process capsule.
267 Status
= CapsuleDataCoalesce (
269 (EFI_PHYSICAL_ADDRESS
*)(UINTN
)EntrypointContext
->BlockListAddr
,
270 (MEMORY_RESOURCE_DESCRIPTOR
*)(UINTN
)EntrypointContext
->MemoryResource
,
271 (VOID
**)(UINTN
)EntrypointContext
->MemoryBase64Ptr
,
272 (UINTN
*)(UINTN
)EntrypointContext
->MemorySize64Ptr
275 ReturnContext
->ReturnStatus
= Status
;
279 "%a() Stack Base: 0x%lx, Stack Size: 0x%lx\n",
281 EntrypointContext
->StackBufferBase
,
282 EntrypointContext
->StackBufferLength
286 // Disable interrupt of Debug timer, since the new IDT table cannot work in long mode
288 SaveAndSetDebugTimerInterrupt (FALSE
);
290 // Restore IA32 IDT table
292 AsmWriteIdtr ((IA32_DESCRIPTOR
*)&Ia32Idtr
);
295 // Finish to coalesce capsule, and return to 32-bit mode.
298 ReturnContext
->ReturnCs
,
299 (UINT32
)ReturnContext
->ReturnEntryPoint
,
300 (UINT32
)(UINTN
)EntrypointContext
,
301 (UINT32
)(UINTN
)ReturnContext
,
302 (UINT32
)(EntrypointContext
->StackBufferBase
+ EntrypointContext
->StackBufferLength
)
306 // Should never be here.