2 The X64 entrypoint is used to process capsule in long mode.
4 Copyright (c) 2011 - 2015, Intel Corporation. All rights reserved.<BR>
5 This program and the accompanying materials
6 are licensed and made available under the terms and conditions of the BSD License
7 which accompanies this distribution. The full text of the license may be found at
8 http://opensource.org/licenses/bsd-license.php
10 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
11 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
15 #include <Library/DebugLib.h>
16 #include <Library/BaseMemoryLib.h>
17 #include <Library/CpuExceptionHandlerLib.h>
18 #include <Library/DebugAgentLib.h>
19 #include "CommonHeader.h"
21 #define EXCEPTION_VECTOR_NUMBER 0x22
23 #define IA32_PG_P BIT0
24 #define IA32_PG_RW BIT1
25 #define IA32_PG_PS BIT7
27 typedef struct _PAGE_FAULT_CONTEXT
{
28 BOOLEAN Page1GSupport
;
30 UINTN PageFaultBuffer
;
33 // Store the uplink information for each page being used.
35 UINT64
*PageFaultUplink
[EXTRA_PAGE_TABLE_PAGES
];
36 VOID
*OriginalHandler
;
39 typedef struct _PAGE_FAULT_IDT_TABLE
{
40 PAGE_FAULT_CONTEXT PageFaultContext
;
41 IA32_IDT_GATE_DESCRIPTOR IdtEntryTable
[EXCEPTION_VECTOR_NUMBER
];
42 } PAGE_FAULT_IDT_TABLE
;
50 PageFaultHandlerHook (
55 Hook IDT with our page fault handler so that the on-demand paging works on page fault.
57 @param[in, out] IdtEntry Pointer to IDT entry.
58 @param[in, out] PageFaultContext Pointer to page fault context.
62 HookPageFaultHandler (
63 IN OUT IA32_IDT_GATE_DESCRIPTOR
*IdtEntry
,
64 IN OUT PAGE_FAULT_CONTEXT
*PageFaultContext
68 UINT8 PhysicalAddressBits
;
69 UINTN PageFaultHandlerHookAddress
;
71 AsmCpuid (0x80000000, &RegEax
, NULL
, NULL
, NULL
);
72 if (RegEax
>= 0x80000008) {
73 AsmCpuid (0x80000008, &RegEax
, NULL
, NULL
, NULL
);
74 PhysicalAddressBits
= (UINT8
) RegEax
;
76 PhysicalAddressBits
= 36;
78 PageFaultContext
->PhyMask
= LShiftU64 (1, PhysicalAddressBits
) - 1;
79 PageFaultContext
->PhyMask
&= (1ull << 48) - SIZE_4KB
;
82 // Set Page Fault entry to catch >4G access
84 PageFaultHandlerHookAddress
= (UINTN
)PageFaultHandlerHook
;
85 PageFaultContext
->OriginalHandler
= (VOID
*)(UINTN
)(LShiftU64 (IdtEntry
->Bits
.OffsetUpper
, 32) + IdtEntry
->Bits
.OffsetLow
+ (IdtEntry
->Bits
.OffsetHigh
<< 16));
86 IdtEntry
->Bits
.OffsetLow
= (UINT16
)PageFaultHandlerHookAddress
;
87 IdtEntry
->Bits
.Selector
= (UINT16
)AsmReadCs ();
88 IdtEntry
->Bits
.Reserved_0
= 0;
89 IdtEntry
->Bits
.GateType
= IA32_IDT_GATE_TYPE_INTERRUPT_32
;
90 IdtEntry
->Bits
.OffsetHigh
= (UINT16
)(PageFaultHandlerHookAddress
>> 16);
91 IdtEntry
->Bits
.OffsetUpper
= (UINT32
)(PageFaultHandlerHookAddress
>> 32);
92 IdtEntry
->Bits
.Reserved_1
= 0;
94 if (PageFaultContext
->Page1GSupport
) {
95 PageFaultContext
->PageFaultBuffer
= (UINTN
)(AsmReadCr3 () & PageFaultContext
->PhyMask
) + EFI_PAGES_TO_SIZE(2);
97 PageFaultContext
->PageFaultBuffer
= (UINTN
)(AsmReadCr3 () & PageFaultContext
->PhyMask
) + EFI_PAGES_TO_SIZE(6);
99 PageFaultContext
->PageFaultIndex
= 0;
100 ZeroMem (PageFaultContext
->PageFaultUplink
, sizeof (PageFaultContext
->PageFaultUplink
));
104 Acquire page for page fault.
106 @param[in, out] PageFaultContext Pointer to page fault context.
107 @param[in, out] Uplink Pointer to up page table entry.
112 IN OUT PAGE_FAULT_CONTEXT
*PageFaultContext
,
113 IN OUT UINT64
*Uplink
118 Address
= PageFaultContext
->PageFaultBuffer
+ EFI_PAGES_TO_SIZE (PageFaultContext
->PageFaultIndex
);
119 ZeroMem ((VOID
*) Address
, EFI_PAGES_TO_SIZE (1));
122 // Cut the previous uplink if it exists and wasn't overwritten.
124 if ((PageFaultContext
->PageFaultUplink
[PageFaultContext
->PageFaultIndex
] != NULL
) && ((*PageFaultContext
->PageFaultUplink
[PageFaultContext
->PageFaultIndex
] & PageFaultContext
->PhyMask
) == Address
)) {
125 *PageFaultContext
->PageFaultUplink
[PageFaultContext
->PageFaultIndex
] = 0;
129 // Link & Record the current uplink.
131 *Uplink
= Address
| IA32_PG_P
| IA32_PG_RW
;
132 PageFaultContext
->PageFaultUplink
[PageFaultContext
->PageFaultIndex
] = Uplink
;
134 PageFaultContext
->PageFaultIndex
= (PageFaultContext
->PageFaultIndex
+ 1) % EXTRA_PAGE_TABLE_PAGES
;
138 The page fault handler that on-demand read >4G memory/MMIO.
140 @retval NULL The page fault is correctly handled.
141 @retval OriginalHandler The page fault is not handled and is passed through to original handler.
150 IA32_DESCRIPTOR Idtr
;
151 PAGE_FAULT_CONTEXT
*PageFaultContext
;
158 // Get the IDT Descriptor.
160 AsmReadIdtr ((IA32_DESCRIPTOR
*) &Idtr
);
162 // Then get page fault context by IDT Descriptor.
164 PageFaultContext
= (PAGE_FAULT_CONTEXT
*) (UINTN
) (Idtr
.Base
- sizeof (PAGE_FAULT_CONTEXT
));
165 PhyMask
= PageFaultContext
->PhyMask
;
167 PFAddress
= AsmReadCr2 ();
168 DEBUG ((EFI_D_ERROR
, "CapsuleX64 - PageFaultHandler: Cr2 - %lx\n", PFAddress
));
170 if (PFAddress
>= PhyMask
+ SIZE_4KB
) {
171 return PageFaultContext
->OriginalHandler
;
173 PFAddress
&= PhyMask
;
175 PageTable
= (UINT64
*)(UINTN
)(AsmReadCr3 () & PhyMask
);
177 PTIndex
= BitFieldRead64 (PFAddress
, 39, 47);
179 if ((PageTable
[PTIndex
] & IA32_PG_P
) == 0) {
180 AcquirePage (PageFaultContext
, &PageTable
[PTIndex
]);
182 PageTable
= (UINT64
*)(UINTN
)(PageTable
[PTIndex
] & PhyMask
);
183 PTIndex
= BitFieldRead64 (PFAddress
, 30, 38);
185 if (PageFaultContext
->Page1GSupport
) {
186 PageTable
[PTIndex
] = (PFAddress
& ~((1ull << 30) - 1)) | IA32_PG_P
| IA32_PG_RW
| IA32_PG_PS
;
188 if ((PageTable
[PTIndex
] & IA32_PG_P
) == 0) {
189 AcquirePage (PageFaultContext
, &PageTable
[PTIndex
]);
191 PageTable
= (UINT64
*)(UINTN
)(PageTable
[PTIndex
] & PhyMask
);
192 PTIndex
= BitFieldRead64 (PFAddress
, 21, 29);
194 PageTable
[PTIndex
] = (PFAddress
& ~((1ull << 21) - 1)) | IA32_PG_P
| IA32_PG_RW
| IA32_PG_PS
;
202 The X64 entrypoint is used to process capsule in long mode then
203 return to 32-bit protected mode.
205 @param EntrypointContext Pointer to the context of long mode.
206 @param ReturnContext Pointer to the context of 32-bit protected mode.
208 @retval This function should never return actually.
214 SWITCH_32_TO_64_CONTEXT
*EntrypointContext
,
215 SWITCH_64_TO_32_CONTEXT
*ReturnContext
219 IA32_DESCRIPTOR Ia32Idtr
;
220 IA32_DESCRIPTOR X64Idtr
;
221 PAGE_FAULT_IDT_TABLE PageFaultIdtTable
;
222 IA32_IDT_GATE_DESCRIPTOR
*IdtEntry
;
225 // Save the IA32 IDT Descriptor
227 AsmReadIdtr ((IA32_DESCRIPTOR
*) &Ia32Idtr
);
230 // Setup X64 IDT table
232 ZeroMem (PageFaultIdtTable
.IdtEntryTable
, sizeof (IA32_IDT_GATE_DESCRIPTOR
) * EXCEPTION_VECTOR_NUMBER
);
233 X64Idtr
.Base
= (UINTN
) PageFaultIdtTable
.IdtEntryTable
;
234 X64Idtr
.Limit
= (UINT16
) (sizeof (IA32_IDT_GATE_DESCRIPTOR
) * EXCEPTION_VECTOR_NUMBER
- 1);
235 AsmWriteIdtr ((IA32_DESCRIPTOR
*) &X64Idtr
);
238 // Setup the default CPU exception handlers
240 Status
= InitializeCpuExceptionHandlers (NULL
);
241 ASSERT_EFI_ERROR (Status
);
244 // Hook page fault handler to handle >4G request.
246 PageFaultIdtTable
.PageFaultContext
.Page1GSupport
= EntrypointContext
->Page1GSupport
;
247 IdtEntry
= (IA32_IDT_GATE_DESCRIPTOR
*) (X64Idtr
.Base
+ (14 * sizeof (IA32_IDT_GATE_DESCRIPTOR
)));
248 HookPageFaultHandler (IdtEntry
, &(PageFaultIdtTable
.PageFaultContext
));
251 // Initialize Debug Agent to support source level debug
253 InitializeDebugAgent (DEBUG_AGENT_INIT_THUNK_PEI_IA32TOX64
, (VOID
*) &Ia32Idtr
, NULL
);
256 // Call CapsuleDataCoalesce to process capsule.
258 Status
= CapsuleDataCoalesce (
260 (EFI_PHYSICAL_ADDRESS
*) (UINTN
) EntrypointContext
->BlockListAddr
,
261 (VOID
**) (UINTN
) EntrypointContext
->MemoryBase64Ptr
,
262 (UINTN
*) (UINTN
) EntrypointContext
->MemorySize64Ptr
265 ReturnContext
->ReturnStatus
= Status
;
268 // Disable interrupt of Debug timer, since the new IDT table cannot work in long mode
270 SaveAndSetDebugTimerInterrupt (FALSE
);
272 // Restore IA32 IDT table
274 AsmWriteIdtr ((IA32_DESCRIPTOR
*) &Ia32Idtr
);
277 // Finish to coalesce capsule, and return to 32-bit mode.
280 ReturnContext
->ReturnCs
,
281 (UINT32
) ReturnContext
->ReturnEntryPoint
,
282 (UINT32
) (UINTN
) EntrypointContext
,
283 (UINT32
) (UINTN
) ReturnContext
,
284 (UINT32
) (EntrypointContext
->StackBufferBase
+ EntrypointContext
->StackBufferLength
)
288 // Should never be here.