2 The X64 entrypoint is used to process capsule in long mode.
4 Copyright (c) 2011 - 2016, Intel Corporation. All rights reserved.<BR>
5 Copyright (c) 2017, AMD Incorporated. All rights reserved.<BR>
7 This program and the accompanying materials
8 are licensed and made available under the terms and conditions of the BSD License
9 which accompanies this distribution. The full text of the license may be found at
10 http://opensource.org/licenses/bsd-license.php
12 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
13 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
17 #include <Library/DebugLib.h>
18 #include <Library/BaseMemoryLib.h>
19 #include <Library/CpuExceptionHandlerLib.h>
20 #include <Library/DebugAgentLib.h>
21 #include "CommonHeader.h"
23 #define EXCEPTION_VECTOR_NUMBER 0x22
25 #define IA32_PG_P BIT0
26 #define IA32_PG_RW BIT1
27 #define IA32_PG_PS BIT7
29 typedef struct _PAGE_FAULT_CONTEXT
{
30 BOOLEAN Page1GSupport
;
32 UINTN PageFaultBuffer
;
34 UINT64 AddressEncMask
;
36 // Store the uplink information for each page being used.
38 UINT64
*PageFaultUplink
[EXTRA_PAGE_TABLE_PAGES
];
39 VOID
*OriginalHandler
;
42 typedef struct _PAGE_FAULT_IDT_TABLE
{
43 PAGE_FAULT_CONTEXT PageFaultContext
;
44 IA32_IDT_GATE_DESCRIPTOR IdtEntryTable
[EXCEPTION_VECTOR_NUMBER
];
45 } PAGE_FAULT_IDT_TABLE
;
53 PageFaultHandlerHook (
58 Hook IDT with our page fault handler so that the on-demand paging works on page fault.
60 @param[in, out] IdtEntry Pointer to IDT entry.
61 @param[in, out] PageFaultContext Pointer to page fault context.
65 HookPageFaultHandler (
66 IN OUT IA32_IDT_GATE_DESCRIPTOR
*IdtEntry
,
67 IN OUT PAGE_FAULT_CONTEXT
*PageFaultContext
71 UINT8 PhysicalAddressBits
;
72 UINTN PageFaultHandlerHookAddress
;
74 AsmCpuid (0x80000000, &RegEax
, NULL
, NULL
, NULL
);
75 if (RegEax
>= 0x80000008) {
76 AsmCpuid (0x80000008, &RegEax
, NULL
, NULL
, NULL
);
77 PhysicalAddressBits
= (UINT8
) RegEax
;
79 PhysicalAddressBits
= 36;
81 PageFaultContext
->PhyMask
= LShiftU64 (1, PhysicalAddressBits
) - 1;
82 PageFaultContext
->PhyMask
&= (1ull << 48) - SIZE_4KB
;
85 // Set Page Fault entry to catch >4G access
87 PageFaultHandlerHookAddress
= (UINTN
)PageFaultHandlerHook
;
88 PageFaultContext
->OriginalHandler
= (VOID
*)(UINTN
)(LShiftU64 (IdtEntry
->Bits
.OffsetUpper
, 32) + IdtEntry
->Bits
.OffsetLow
+ (IdtEntry
->Bits
.OffsetHigh
<< 16));
89 IdtEntry
->Bits
.OffsetLow
= (UINT16
)PageFaultHandlerHookAddress
;
90 IdtEntry
->Bits
.Selector
= (UINT16
)AsmReadCs ();
91 IdtEntry
->Bits
.Reserved_0
= 0;
92 IdtEntry
->Bits
.GateType
= IA32_IDT_GATE_TYPE_INTERRUPT_32
;
93 IdtEntry
->Bits
.OffsetHigh
= (UINT16
)(PageFaultHandlerHookAddress
>> 16);
94 IdtEntry
->Bits
.OffsetUpper
= (UINT32
)(PageFaultHandlerHookAddress
>> 32);
95 IdtEntry
->Bits
.Reserved_1
= 0;
97 if (PageFaultContext
->Page1GSupport
) {
98 PageFaultContext
->PageFaultBuffer
= (UINTN
)(AsmReadCr3 () & PageFaultContext
->PhyMask
) + EFI_PAGES_TO_SIZE(2);
100 PageFaultContext
->PageFaultBuffer
= (UINTN
)(AsmReadCr3 () & PageFaultContext
->PhyMask
) + EFI_PAGES_TO_SIZE(6);
102 PageFaultContext
->PageFaultIndex
= 0;
103 ZeroMem (PageFaultContext
->PageFaultUplink
, sizeof (PageFaultContext
->PageFaultUplink
));
107 Acquire page for page fault.
109 @param[in, out] PageFaultContext Pointer to page fault context.
110 @param[in, out] Uplink Pointer to up page table entry.
115 IN OUT PAGE_FAULT_CONTEXT
*PageFaultContext
,
116 IN OUT UINT64
*Uplink
120 UINT64 AddressEncMask
;
122 Address
= PageFaultContext
->PageFaultBuffer
+ EFI_PAGES_TO_SIZE (PageFaultContext
->PageFaultIndex
);
123 ZeroMem ((VOID
*) Address
, EFI_PAGES_TO_SIZE (1));
125 AddressEncMask
= PageFaultContext
->AddressEncMask
;
128 // Cut the previous uplink if it exists and wasn't overwritten.
130 if ((PageFaultContext
->PageFaultUplink
[PageFaultContext
->PageFaultIndex
] != NULL
) &&
131 ((*PageFaultContext
->PageFaultUplink
[PageFaultContext
->PageFaultIndex
] & ~AddressEncMask
& PageFaultContext
->PhyMask
) == Address
)) {
132 *PageFaultContext
->PageFaultUplink
[PageFaultContext
->PageFaultIndex
] = 0;
136 // Link & Record the current uplink.
138 *Uplink
= Address
| AddressEncMask
| IA32_PG_P
| IA32_PG_RW
;
139 PageFaultContext
->PageFaultUplink
[PageFaultContext
->PageFaultIndex
] = Uplink
;
141 PageFaultContext
->PageFaultIndex
= (PageFaultContext
->PageFaultIndex
+ 1) % EXTRA_PAGE_TABLE_PAGES
;
145 The page fault handler that on-demand read >4G memory/MMIO.
147 @retval NULL The page fault is correctly handled.
148 @retval OriginalHandler The page fault is not handled and is passed through to original handler.
157 IA32_DESCRIPTOR Idtr
;
158 PAGE_FAULT_CONTEXT
*PageFaultContext
;
163 UINT64 AddressEncMask
;
166 // Get the IDT Descriptor.
168 AsmReadIdtr ((IA32_DESCRIPTOR
*) &Idtr
);
170 // Then get page fault context by IDT Descriptor.
172 PageFaultContext
= (PAGE_FAULT_CONTEXT
*) (UINTN
) (Idtr
.Base
- sizeof (PAGE_FAULT_CONTEXT
));
173 PhyMask
= PageFaultContext
->PhyMask
;
174 AddressEncMask
= PageFaultContext
->AddressEncMask
;
176 PFAddress
= AsmReadCr2 ();
177 DEBUG ((EFI_D_ERROR
, "CapsuleX64 - PageFaultHandler: Cr2 - %lx\n", PFAddress
));
179 if (PFAddress
>= PhyMask
+ SIZE_4KB
) {
180 return PageFaultContext
->OriginalHandler
;
182 PFAddress
&= PhyMask
;
184 PageTable
= (UINT64
*)(UINTN
)(AsmReadCr3 () & PhyMask
);
186 PTIndex
= BitFieldRead64 (PFAddress
, 39, 47);
188 if ((PageTable
[PTIndex
] & IA32_PG_P
) == 0) {
189 AcquirePage (PageFaultContext
, &PageTable
[PTIndex
]);
191 PageTable
= (UINT64
*)(UINTN
)(PageTable
[PTIndex
] & ~AddressEncMask
& PhyMask
);
192 PTIndex
= BitFieldRead64 (PFAddress
, 30, 38);
194 if (PageFaultContext
->Page1GSupport
) {
195 PageTable
[PTIndex
] = ((PFAddress
| AddressEncMask
) & ~((1ull << 30) - 1)) | IA32_PG_P
| IA32_PG_RW
| IA32_PG_PS
;
197 if ((PageTable
[PTIndex
] & IA32_PG_P
) == 0) {
198 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
;
211 The X64 entrypoint is used to process capsule in long mode then
212 return to 32-bit protected mode.
214 @param EntrypointContext Pointer to the context of long mode.
215 @param ReturnContext Pointer to the context of 32-bit protected mode.
217 @retval This function should never return actually.
223 SWITCH_32_TO_64_CONTEXT
*EntrypointContext
,
224 SWITCH_64_TO_32_CONTEXT
*ReturnContext
228 IA32_DESCRIPTOR Ia32Idtr
;
229 IA32_DESCRIPTOR X64Idtr
;
230 PAGE_FAULT_IDT_TABLE PageFaultIdtTable
;
231 IA32_IDT_GATE_DESCRIPTOR
*IdtEntry
;
234 // Save the IA32 IDT Descriptor
236 AsmReadIdtr ((IA32_DESCRIPTOR
*) &Ia32Idtr
);
239 // Setup X64 IDT table
241 ZeroMem (PageFaultIdtTable
.IdtEntryTable
, sizeof (IA32_IDT_GATE_DESCRIPTOR
) * EXCEPTION_VECTOR_NUMBER
);
242 X64Idtr
.Base
= (UINTN
) PageFaultIdtTable
.IdtEntryTable
;
243 X64Idtr
.Limit
= (UINT16
) (sizeof (IA32_IDT_GATE_DESCRIPTOR
) * EXCEPTION_VECTOR_NUMBER
- 1);
244 AsmWriteIdtr ((IA32_DESCRIPTOR
*) &X64Idtr
);
247 // Setup the default CPU exception handlers
249 Status
= InitializeCpuExceptionHandlers (NULL
);
250 ASSERT_EFI_ERROR (Status
);
253 // Hook page fault handler to handle >4G request.
255 PageFaultIdtTable
.PageFaultContext
.Page1GSupport
= EntrypointContext
->Page1GSupport
;
256 PageFaultIdtTable
.PageFaultContext
.AddressEncMask
= EntrypointContext
->AddressEncMask
;
257 IdtEntry
= (IA32_IDT_GATE_DESCRIPTOR
*) (X64Idtr
.Base
+ (14 * sizeof (IA32_IDT_GATE_DESCRIPTOR
)));
258 HookPageFaultHandler (IdtEntry
, &(PageFaultIdtTable
.PageFaultContext
));
261 // Initialize Debug Agent to support source level debug
263 InitializeDebugAgent (DEBUG_AGENT_INIT_THUNK_PEI_IA32TOX64
, (VOID
*) &Ia32Idtr
, NULL
);
266 // Call CapsuleDataCoalesce to process capsule.
268 Status
= CapsuleDataCoalesce (
270 (EFI_PHYSICAL_ADDRESS
*) (UINTN
) EntrypointContext
->BlockListAddr
,
271 (MEMORY_RESOURCE_DESCRIPTOR
*) (UINTN
) EntrypointContext
->MemoryResource
,
272 (VOID
**) (UINTN
) EntrypointContext
->MemoryBase64Ptr
,
273 (UINTN
*) (UINTN
) EntrypointContext
->MemorySize64Ptr
276 ReturnContext
->ReturnStatus
= Status
;
280 "%a() Stack Base: 0x%lx, Stack Size: 0x%lx\n",
282 EntrypointContext
->StackBufferBase
,
283 EntrypointContext
->StackBufferLength
287 // Disable interrupt of Debug timer, since the new IDT table cannot work in long mode
289 SaveAndSetDebugTimerInterrupt (FALSE
);
291 // Restore IA32 IDT table
293 AsmWriteIdtr ((IA32_DESCRIPTOR
*) &Ia32Idtr
);
296 // Finish to coalesce capsule, and return to 32-bit mode.
299 ReturnContext
->ReturnCs
,
300 (UINT32
) ReturnContext
->ReturnEntryPoint
,
301 (UINT32
) (UINTN
) EntrypointContext
,
302 (UINT32
) (UINTN
) ReturnContext
,
303 (UINT32
) (EntrypointContext
->StackBufferBase
+ EntrypointContext
->StackBufferLength
)
307 // Should never be here.