Home | History | Annotate | Download | only in Ia32
      1 /** @file
      2 Page table manipulation functions for IA-32 processors
      3 
      4 Copyright (c) 2009 - 2016, 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 "PiSmmCpuDxeSmm.h"
     16 
     17 /**
     18   Create PageTable for SMM use.
     19 
     20   @return     PageTable Address
     21 
     22 **/
     23 UINT32
     24 SmmInitPageTable (
     25   VOID
     26   )
     27 {
     28   UINTN                             PageFaultHandlerHookAddress;
     29   IA32_IDT_GATE_DESCRIPTOR          *IdtEntry;
     30   EFI_STATUS                        Status;
     31 
     32   //
     33   // Initialize spin lock
     34   //
     35   InitializeSpinLock (mPFLock);
     36 
     37   if (FeaturePcdGet (PcdCpuSmmProfileEnable)) {
     38     //
     39     // Set own Page Fault entry instead of the default one, because SMM Profile
     40     // feature depends on IRET instruction to do Single Step
     41     //
     42     PageFaultHandlerHookAddress = (UINTN)PageFaultIdtHandlerSmmProfile;
     43     IdtEntry  = (IA32_IDT_GATE_DESCRIPTOR *) gcSmiIdtr.Base;
     44     IdtEntry += EXCEPT_IA32_PAGE_FAULT;
     45     IdtEntry->Bits.OffsetLow      = (UINT16)PageFaultHandlerHookAddress;
     46     IdtEntry->Bits.Reserved_0     = 0;
     47     IdtEntry->Bits.GateType       = IA32_IDT_GATE_TYPE_INTERRUPT_32;
     48     IdtEntry->Bits.OffsetHigh     = (UINT16)(PageFaultHandlerHookAddress >> 16);
     49   } else {
     50     //
     51     // Register SMM Page Fault Handler
     52     //
     53     Status = SmmRegisterExceptionHandler (&mSmmCpuService, EXCEPT_IA32_PAGE_FAULT, SmiPFHandler);
     54     ASSERT_EFI_ERROR (Status);
     55   }
     56 
     57   //
     58   // Additional SMM IDT initialization for SMM stack guard
     59   //
     60   if (FeaturePcdGet (PcdCpuSmmStackGuard)) {
     61     InitializeIDTSmmStackGuard ();
     62   }
     63   return Gen4GPageTable (TRUE);
     64 }
     65 
     66 /**
     67   Page Fault handler for SMM use.
     68 
     69 **/
     70 VOID
     71 SmiDefaultPFHandler (
     72   VOID
     73   )
     74 {
     75   CpuDeadLoop ();
     76 }
     77 
     78 /**
     79   ThePage Fault handler wrapper for SMM use.
     80 
     81   @param  InterruptType    Defines the type of interrupt or exception that
     82                            occurred on the processor.This parameter is processor architecture specific.
     83   @param  SystemContext    A pointer to the processor context when
     84                            the interrupt occurred on the processor.
     85 **/
     86 VOID
     87 EFIAPI
     88 SmiPFHandler (
     89     IN EFI_EXCEPTION_TYPE   InterruptType,
     90     IN EFI_SYSTEM_CONTEXT   SystemContext
     91   )
     92 {
     93   UINTN             PFAddress;
     94   UINTN             GuardPageAddress;
     95   UINTN             CpuIndex;
     96 
     97   ASSERT (InterruptType == EXCEPT_IA32_PAGE_FAULT);
     98 
     99   AcquireSpinLock (mPFLock);
    100 
    101   PFAddress = AsmReadCr2 ();
    102 
    103   //
    104   // If a page fault occurs in SMRAM range, it might be in a SMM stack guard page,
    105   // or SMM page protection violation.
    106   //
    107   if ((PFAddress >= mCpuHotPlugData.SmrrBase) &&
    108       (PFAddress < (mCpuHotPlugData.SmrrBase + mCpuHotPlugData.SmrrSize))) {
    109     CpuIndex = GetCpuIndex ();
    110     GuardPageAddress = (mSmmStackArrayBase + EFI_PAGE_SIZE + CpuIndex * mSmmStackSize);
    111     if ((FeaturePcdGet (PcdCpuSmmStackGuard)) &&
    112         (PFAddress >= GuardPageAddress) &&
    113         (PFAddress < (GuardPageAddress + EFI_PAGE_SIZE))) {
    114       DEBUG ((DEBUG_ERROR, "SMM stack overflow!\n"));
    115     } else {
    116       DEBUG ((DEBUG_ERROR, "SMM exception data - 0x%x(", SystemContext.SystemContextIa32->ExceptionData));
    117       DEBUG ((DEBUG_ERROR, "I:%x, R:%x, U:%x, W:%x, P:%x",
    118         (SystemContext.SystemContextIa32->ExceptionData & IA32_PF_EC_ID) != 0,
    119         (SystemContext.SystemContextIa32->ExceptionData & IA32_PF_EC_RSVD) != 0,
    120         (SystemContext.SystemContextIa32->ExceptionData & IA32_PF_EC_US) != 0,
    121         (SystemContext.SystemContextIa32->ExceptionData & IA32_PF_EC_WR) != 0,
    122         (SystemContext.SystemContextIa32->ExceptionData & IA32_PF_EC_P) != 0
    123         ));
    124       DEBUG ((DEBUG_ERROR, ")\n"));
    125       if ((SystemContext.SystemContextIa32->ExceptionData & IA32_PF_EC_ID) != 0) {
    126         DEBUG ((DEBUG_ERROR, "SMM exception at execution (0x%x)\n", PFAddress));
    127         DEBUG_CODE (
    128           DumpModuleInfoByIp (*(UINTN *)(UINTN)SystemContext.SystemContextIa32->Esp);
    129         );
    130       } else {
    131         DEBUG ((DEBUG_ERROR, "SMM exception at access (0x%x)\n", PFAddress));
    132         DEBUG_CODE (
    133           DumpModuleInfoByIp ((UINTN)SystemContext.SystemContextIa32->Eip);
    134         );
    135       }
    136     }
    137     CpuDeadLoop ();
    138   }
    139 
    140   //
    141   // If a page fault occurs in SMM range
    142   //
    143   if ((PFAddress < mCpuHotPlugData.SmrrBase) ||
    144       (PFAddress >= mCpuHotPlugData.SmrrBase + mCpuHotPlugData.SmrrSize)) {
    145     if ((SystemContext.SystemContextIa32->ExceptionData & IA32_PF_EC_ID) != 0) {
    146       DEBUG ((DEBUG_ERROR, "Code executed on IP(0x%x) out of SMM range after SMM is locked!\n", PFAddress));
    147       DEBUG_CODE (
    148         DumpModuleInfoByIp (*(UINTN *)(UINTN)SystemContext.SystemContextIa32->Esp);
    149       );
    150       CpuDeadLoop ();
    151     }
    152     if (IsSmmCommBufferForbiddenAddress (PFAddress)) {
    153       DEBUG ((DEBUG_ERROR, "Access SMM communication forbidden address (0x%x)!\n", PFAddress));
    154       DEBUG_CODE (
    155         DumpModuleInfoByIp ((UINTN)SystemContext.SystemContextIa32->Eip);
    156       );
    157       CpuDeadLoop ();
    158     }
    159   }
    160 
    161   if (FeaturePcdGet (PcdCpuSmmProfileEnable)) {
    162     SmmProfilePFHandler (
    163       SystemContext.SystemContextIa32->Eip,
    164       SystemContext.SystemContextIa32->ExceptionData
    165       );
    166   } else {
    167     SmiDefaultPFHandler ();
    168   }
    169 
    170   ReleaseSpinLock (mPFLock);
    171 }
    172 
    173 /**
    174   This function sets memory attribute for page table.
    175 **/
    176 VOID
    177 SetPageTableAttributes (
    178   VOID
    179   )
    180 {
    181   UINTN                 Index2;
    182   UINTN                 Index3;
    183   UINT64                *L1PageTable;
    184   UINT64                *L2PageTable;
    185   UINT64                *L3PageTable;
    186   BOOLEAN               IsSplitted;
    187   BOOLEAN               PageTableSplitted;
    188 
    189   DEBUG ((DEBUG_INFO, "SetPageTableAttributes\n"));
    190 
    191   //
    192   // Disable write protection, because we need mark page table to be write protected.
    193   // We need *write* page table memory, to mark itself to be *read only*.
    194   //
    195   AsmWriteCr0 (AsmReadCr0() & ~CR0_WP);
    196 
    197   do {
    198     DEBUG ((DEBUG_INFO, "Start...\n"));
    199     PageTableSplitted = FALSE;
    200 
    201     L3PageTable = (UINT64 *)GetPageTableBase ();
    202 
    203     SmmSetMemoryAttributesEx ((EFI_PHYSICAL_ADDRESS)(UINTN)L3PageTable, SIZE_4KB, EFI_MEMORY_RO, &IsSplitted);
    204     PageTableSplitted = (PageTableSplitted || IsSplitted);
    205 
    206     for (Index3 = 0; Index3 < 4; Index3++) {
    207       L2PageTable = (UINT64 *)(UINTN)(L3PageTable[Index3] & PAGING_4K_ADDRESS_MASK_64);
    208       if (L2PageTable == NULL) {
    209         continue;
    210       }
    211 
    212       SmmSetMemoryAttributesEx ((EFI_PHYSICAL_ADDRESS)(UINTN)L2PageTable, SIZE_4KB, EFI_MEMORY_RO, &IsSplitted);
    213       PageTableSplitted = (PageTableSplitted || IsSplitted);
    214 
    215       for (Index2 = 0; Index2 < SIZE_4KB/sizeof(UINT64); Index2++) {
    216         if ((L2PageTable[Index2] & IA32_PG_PS) != 0) {
    217           // 2M
    218           continue;
    219         }
    220         L1PageTable = (UINT64 *)(UINTN)(L2PageTable[Index2] & PAGING_4K_ADDRESS_MASK_64);
    221         if (L1PageTable == NULL) {
    222           continue;
    223         }
    224         SmmSetMemoryAttributesEx ((EFI_PHYSICAL_ADDRESS)(UINTN)L1PageTable, SIZE_4KB, EFI_MEMORY_RO, &IsSplitted);
    225         PageTableSplitted = (PageTableSplitted || IsSplitted);
    226       }
    227     }
    228   } while (PageTableSplitted);
    229 
    230   //
    231   // Enable write protection, after page table updated.
    232   //
    233   AsmWriteCr0 (AsmReadCr0() | CR0_WP);
    234 
    235   return ;
    236 }
    237