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
SmmInitPageTable(VOID)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
SmiDefaultPFHandler(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
SmiPFHandler(IN EFI_EXCEPTION_TYPE InterruptType,IN EFI_SYSTEM_CONTEXT SystemContext)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
SetPageTableAttributes(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