Home | History | Annotate | Download | only in X64
      1 /** @file
      2   The X64 entrypoint is used to process capsule in long mode.
      3 
      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
      9 
     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.
     12 
     13 **/
     14 
     15 #include <Library/DebugLib.h>
     16 #include <Library/BaseMemoryLib.h>
     17 #include <Library/CpuExceptionHandlerLib.h>
     18 #include <Library/DebugAgentLib.h>
     19 #include "CommonHeader.h"
     20 
     21 #define EXCEPTION_VECTOR_NUMBER     0x22
     22 
     23 #define IA32_PG_P                   BIT0
     24 #define IA32_PG_RW                  BIT1
     25 #define IA32_PG_PS                  BIT7
     26 
     27 typedef struct _PAGE_FAULT_CONTEXT {
     28   BOOLEAN                       Page1GSupport;
     29   UINT64                        PhyMask;
     30   UINTN                         PageFaultBuffer;
     31   UINTN                         PageFaultIndex;
     32   //
     33   // Store the uplink information for each page being used.
     34   //
     35   UINT64                        *PageFaultUplink[EXTRA_PAGE_TABLE_PAGES];
     36   VOID                          *OriginalHandler;
     37 } PAGE_FAULT_CONTEXT;
     38 
     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;
     43 
     44 /**
     45   Page fault handler.
     46 
     47 **/
     48 VOID
     49 EFIAPI
     50 PageFaultHandlerHook (
     51   VOID
     52   );
     53 
     54 /**
     55   Hook IDT with our page fault handler so that the on-demand paging works on page fault.
     56 
     57   @param[in, out] IdtEntry          Pointer to IDT entry.
     58   @param[in, out] PageFaultContext  Pointer to page fault context.
     59 
     60 **/
     61 VOID
     62 HookPageFaultHandler (
     63   IN OUT IA32_IDT_GATE_DESCRIPTOR   *IdtEntry,
     64   IN OUT PAGE_FAULT_CONTEXT         *PageFaultContext
     65   )
     66 {
     67   UINT32            RegEax;
     68   UINT8             PhysicalAddressBits;
     69   UINTN             PageFaultHandlerHookAddress;
     70 
     71   AsmCpuid (0x80000000, &RegEax, NULL, NULL, NULL);
     72   if (RegEax >= 0x80000008) {
     73     AsmCpuid (0x80000008, &RegEax, NULL, NULL, NULL);
     74     PhysicalAddressBits = (UINT8) RegEax;
     75   } else {
     76     PhysicalAddressBits = 36;
     77   }
     78   PageFaultContext->PhyMask = LShiftU64 (1, PhysicalAddressBits) - 1;
     79   PageFaultContext->PhyMask &= (1ull << 48) - SIZE_4KB;
     80 
     81   //
     82   // Set Page Fault entry to catch >4G access
     83   //
     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;
     93 
     94   if (PageFaultContext->Page1GSupport) {
     95     PageFaultContext->PageFaultBuffer = (UINTN)(AsmReadCr3 () & PageFaultContext->PhyMask) + EFI_PAGES_TO_SIZE(2);
     96   }else {
     97     PageFaultContext->PageFaultBuffer = (UINTN)(AsmReadCr3 () & PageFaultContext->PhyMask) + EFI_PAGES_TO_SIZE(6);
     98   }
     99   PageFaultContext->PageFaultIndex = 0;
    100   ZeroMem (PageFaultContext->PageFaultUplink, sizeof (PageFaultContext->PageFaultUplink));
    101 }
    102 
    103 /**
    104   Acquire page for page fault.
    105 
    106   @param[in, out] PageFaultContext  Pointer to page fault context.
    107   @param[in, out] Uplink            Pointer to up page table entry.
    108 
    109 **/
    110 VOID
    111 AcquirePage (
    112   IN OUT PAGE_FAULT_CONTEXT     *PageFaultContext,
    113   IN OUT UINT64                 *Uplink
    114   )
    115 {
    116   UINTN             Address;
    117 
    118   Address = PageFaultContext->PageFaultBuffer + EFI_PAGES_TO_SIZE (PageFaultContext->PageFaultIndex);
    119   ZeroMem ((VOID *) Address, EFI_PAGES_TO_SIZE (1));
    120 
    121   //
    122   // Cut the previous uplink if it exists and wasn't overwritten.
    123   //
    124   if ((PageFaultContext->PageFaultUplink[PageFaultContext->PageFaultIndex] != NULL) && ((*PageFaultContext->PageFaultUplink[PageFaultContext->PageFaultIndex] & PageFaultContext->PhyMask) == Address)) {
    125     *PageFaultContext->PageFaultUplink[PageFaultContext->PageFaultIndex] = 0;
    126   }
    127 
    128   //
    129   // Link & Record the current uplink.
    130   //
    131   *Uplink = Address | IA32_PG_P | IA32_PG_RW;
    132   PageFaultContext->PageFaultUplink[PageFaultContext->PageFaultIndex] = Uplink;
    133 
    134   PageFaultContext->PageFaultIndex = (PageFaultContext->PageFaultIndex + 1) % EXTRA_PAGE_TABLE_PAGES;
    135 }
    136 
    137 /**
    138   The page fault handler that on-demand read >4G memory/MMIO.
    139 
    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.
    142 
    143 **/
    144 VOID *
    145 EFIAPI
    146 PageFaultHandler (
    147   VOID
    148   )
    149 {
    150   IA32_DESCRIPTOR           Idtr;
    151   PAGE_FAULT_CONTEXT        *PageFaultContext;
    152   UINT64                    PhyMask;
    153   UINT64                    *PageTable;
    154   UINT64                    PFAddress;
    155   UINTN                     PTIndex;
    156 
    157   //
    158   // Get the IDT Descriptor.
    159   //
    160   AsmReadIdtr ((IA32_DESCRIPTOR *) &Idtr);
    161   //
    162   // Then get page fault context by IDT Descriptor.
    163   //
    164   PageFaultContext = (PAGE_FAULT_CONTEXT *) (UINTN) (Idtr.Base - sizeof (PAGE_FAULT_CONTEXT));
    165   PhyMask = PageFaultContext->PhyMask;
    166 
    167   PFAddress = AsmReadCr2 ();
    168   DEBUG ((EFI_D_ERROR, "CapsuleX64 - PageFaultHandler: Cr2 - %lx\n", PFAddress));
    169 
    170   if (PFAddress >= PhyMask + SIZE_4KB) {
    171     return PageFaultContext->OriginalHandler;
    172   }
    173   PFAddress &= PhyMask;
    174 
    175   PageTable = (UINT64*)(UINTN)(AsmReadCr3 () & PhyMask);
    176 
    177   PTIndex = BitFieldRead64 (PFAddress, 39, 47);
    178   // PML4E
    179   if ((PageTable[PTIndex] & IA32_PG_P) == 0) {
    180     AcquirePage (PageFaultContext, &PageTable[PTIndex]);
    181   }
    182   PageTable = (UINT64*)(UINTN)(PageTable[PTIndex] & PhyMask);
    183   PTIndex = BitFieldRead64 (PFAddress, 30, 38);
    184   // PDPTE
    185   if (PageFaultContext->Page1GSupport) {
    186     PageTable[PTIndex] = (PFAddress & ~((1ull << 30) - 1)) | IA32_PG_P | IA32_PG_RW | IA32_PG_PS;
    187   } else {
    188     if ((PageTable[PTIndex] & IA32_PG_P) == 0) {
    189       AcquirePage (PageFaultContext, &PageTable[PTIndex]);
    190     }
    191     PageTable = (UINT64*)(UINTN)(PageTable[PTIndex] & PhyMask);
    192     PTIndex = BitFieldRead64 (PFAddress, 21, 29);
    193     // PD
    194     PageTable[PTIndex] = (PFAddress & ~((1ull << 21) - 1)) | IA32_PG_P | IA32_PG_RW | IA32_PG_PS;
    195   }
    196 
    197   return NULL;
    198 }
    199 
    200 
    201 /**
    202   The X64 entrypoint is used to process capsule in long mode then
    203   return to 32-bit protected mode.
    204 
    205   @param  EntrypointContext   Pointer to the context of long mode.
    206   @param  ReturnContext       Pointer to the context of 32-bit protected mode.
    207 
    208   @retval This function should never return actually.
    209 
    210 **/
    211 EFI_STATUS
    212 EFIAPI
    213 _ModuleEntryPoint (
    214   SWITCH_32_TO_64_CONTEXT       *EntrypointContext,
    215   SWITCH_64_TO_32_CONTEXT       *ReturnContext
    216 )
    217 {
    218   EFI_STATUS                    Status;
    219   IA32_DESCRIPTOR               Ia32Idtr;
    220   IA32_DESCRIPTOR               X64Idtr;
    221   PAGE_FAULT_IDT_TABLE          PageFaultIdtTable;
    222   IA32_IDT_GATE_DESCRIPTOR      *IdtEntry;
    223 
    224   //
    225   // Save the IA32 IDT Descriptor
    226   //
    227   AsmReadIdtr ((IA32_DESCRIPTOR *) &Ia32Idtr);
    228 
    229   //
    230   // Setup X64 IDT table
    231   //
    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);
    236 
    237   //
    238   // Setup the default CPU exception handlers
    239   //
    240   Status = InitializeCpuExceptionHandlers (NULL);
    241   ASSERT_EFI_ERROR (Status);
    242 
    243   //
    244   // Hook page fault handler to handle >4G request.
    245   //
    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));
    249 
    250   //
    251   // Initialize Debug Agent to support source level debug
    252   //
    253   InitializeDebugAgent (DEBUG_AGENT_INIT_THUNK_PEI_IA32TOX64, (VOID *) &Ia32Idtr, NULL);
    254 
    255   //
    256   // Call CapsuleDataCoalesce to process capsule.
    257   //
    258   Status = CapsuleDataCoalesce (
    259              NULL,
    260              (EFI_PHYSICAL_ADDRESS *) (UINTN) EntrypointContext->BlockListAddr,
    261              (VOID **) (UINTN) EntrypointContext->MemoryBase64Ptr,
    262              (UINTN *) (UINTN) EntrypointContext->MemorySize64Ptr
    263              );
    264 
    265   ReturnContext->ReturnStatus = Status;
    266 
    267   //
    268   // Disable interrupt of Debug timer, since the new IDT table cannot work in long mode
    269   //
    270   SaveAndSetDebugTimerInterrupt (FALSE);
    271   //
    272   // Restore IA32 IDT table
    273   //
    274   AsmWriteIdtr ((IA32_DESCRIPTOR *) &Ia32Idtr);
    275 
    276   //
    277   // Finish to coalesce capsule, and return to 32-bit mode.
    278   //
    279   AsmDisablePaging64 (
    280     ReturnContext->ReturnCs,
    281     (UINT32) ReturnContext->ReturnEntryPoint,
    282     (UINT32) (UINTN) EntrypointContext,
    283     (UINT32) (UINTN) ReturnContext,
    284     (UINT32) (EntrypointContext->StackBufferBase + EntrypointContext->StackBufferLength)
    285     );
    286 
    287   //
    288   // Should never be here.
    289   //
    290   ASSERT (FALSE);
    291   return EFI_SUCCESS;
    292 }