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