]> git.proxmox.com Git - mirror_edk2.git/blob - UefiCpuPkg/PiSmmCpuDxeSmm/Ia32/PageTbl.c
UefiCpuPkg/PiSmmCpuDxeSmm: Add paging protection.
[mirror_edk2.git] / UefiCpuPkg / PiSmmCpuDxeSmm / Ia32 / PageTbl.c
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
31 //
32 // Initialize spin lock
33 //
34 InitializeSpinLock (mPFLock);
35
36 if (FeaturePcdGet (PcdCpuSmmProfileEnable)) {
37 //
38 // Set own Page Fault entry instead of the default one, because SMM Profile
39 // feature depends on IRET instruction to do Single Step
40 //
41 PageFaultHandlerHookAddress = (UINTN)PageFaultIdtHandlerSmmProfile;
42 IdtEntry = (IA32_IDT_GATE_DESCRIPTOR *) gcSmiIdtr.Base;
43 IdtEntry += EXCEPT_IA32_PAGE_FAULT;
44 IdtEntry->Bits.OffsetLow = (UINT16)PageFaultHandlerHookAddress;
45 IdtEntry->Bits.Reserved_0 = 0;
46 IdtEntry->Bits.GateType = IA32_IDT_GATE_TYPE_INTERRUPT_32;
47 IdtEntry->Bits.OffsetHigh = (UINT16)(PageFaultHandlerHookAddress >> 16);
48 } else {
49 //
50 // Register SMM Page Fault Handler
51 //
52 SmmRegisterExceptionHandler (&mSmmCpuService, EXCEPT_IA32_PAGE_FAULT, SmiPFHandler);
53 }
54
55 //
56 // Additional SMM IDT initialization for SMM stack guard
57 //
58 if (FeaturePcdGet (PcdCpuSmmStackGuard)) {
59 InitializeIDTSmmStackGuard ();
60 }
61 return Gen4GPageTable (TRUE);
62 }
63
64 /**
65 Page Fault handler for SMM use.
66
67 **/
68 VOID
69 SmiDefaultPFHandler (
70 VOID
71 )
72 {
73 CpuDeadLoop ();
74 }
75
76 /**
77 ThePage Fault handler wrapper for SMM use.
78
79 @param InterruptType Defines the type of interrupt or exception that
80 occurred on the processor.This parameter is processor architecture specific.
81 @param SystemContext A pointer to the processor context when
82 the interrupt occurred on the processor.
83 **/
84 VOID
85 EFIAPI
86 SmiPFHandler (
87 IN EFI_EXCEPTION_TYPE InterruptType,
88 IN EFI_SYSTEM_CONTEXT SystemContext
89 )
90 {
91 UINTN PFAddress;
92
93 ASSERT (InterruptType == EXCEPT_IA32_PAGE_FAULT);
94
95 AcquireSpinLock (mPFLock);
96
97 PFAddress = AsmReadCr2 ();
98
99 if ((FeaturePcdGet (PcdCpuSmmStackGuard)) &&
100 (PFAddress >= mCpuHotPlugData.SmrrBase) &&
101 (PFAddress < (mCpuHotPlugData.SmrrBase + mCpuHotPlugData.SmrrSize))) {
102 DEBUG ((DEBUG_ERROR, "SMM stack overflow!\n"));
103 CpuDeadLoop ();
104 }
105
106 //
107 // If a page fault occurs in SMM range
108 //
109 if ((PFAddress < mCpuHotPlugData.SmrrBase) ||
110 (PFAddress >= mCpuHotPlugData.SmrrBase + mCpuHotPlugData.SmrrSize)) {
111 if ((SystemContext.SystemContextIa32->ExceptionData & IA32_PF_EC_ID) != 0) {
112 DEBUG ((DEBUG_ERROR, "Code executed on IP(0x%x) out of SMM range after SMM is locked!\n", PFAddress));
113 DEBUG_CODE (
114 DumpModuleInfoByIp (*(UINTN *)(UINTN)SystemContext.SystemContextIa32->Esp);
115 );
116 CpuDeadLoop ();
117 }
118 }
119
120 if (FeaturePcdGet (PcdCpuSmmProfileEnable)) {
121 SmmProfilePFHandler (
122 SystemContext.SystemContextIa32->Eip,
123 SystemContext.SystemContextIa32->ExceptionData
124 );
125 } else {
126 SmiDefaultPFHandler ();
127 }
128
129 ReleaseSpinLock (mPFLock);
130 }
131
132 /**
133 This function sets memory attribute for page table.
134 **/
135 VOID
136 SetPageTableAttributes (
137 VOID
138 )
139 {
140 UINTN Index2;
141 UINTN Index3;
142 UINT64 *L1PageTable;
143 UINT64 *L2PageTable;
144 UINT64 *L3PageTable;
145 BOOLEAN IsSplitted;
146 BOOLEAN PageTableSplitted;
147
148 DEBUG ((DEBUG_INFO, "SetPageTableAttributes\n"));
149
150 //
151 // Disable write protection, because we need mark page table to be write protected.
152 // We need *write* page table memory, to mark itself to be *read only*.
153 //
154 AsmWriteCr0 (AsmReadCr0() & ~CR0_WP);
155
156 do {
157 DEBUG ((DEBUG_INFO, "Start...\n"));
158 PageTableSplitted = FALSE;
159
160 L3PageTable = (UINT64 *)GetPageTableBase ();
161
162 SmmSetMemoryAttributesEx ((EFI_PHYSICAL_ADDRESS)(UINTN)L3PageTable, SIZE_4KB, EFI_MEMORY_RO, &IsSplitted);
163 PageTableSplitted = (PageTableSplitted || IsSplitted);
164
165 for (Index3 = 0; Index3 < 4; Index3++) {
166 L2PageTable = (UINT64 *)(UINTN)(L3PageTable[Index3] & PAGING_4K_ADDRESS_MASK_64);
167 if (L2PageTable == NULL) {
168 continue;
169 }
170
171 SmmSetMemoryAttributesEx ((EFI_PHYSICAL_ADDRESS)(UINTN)L2PageTable, SIZE_4KB, EFI_MEMORY_RO, &IsSplitted);
172 PageTableSplitted = (PageTableSplitted || IsSplitted);
173
174 for (Index2 = 0; Index2 < SIZE_4KB/sizeof(UINT64); Index2++) {
175 if ((L2PageTable[Index2] & IA32_PG_PS) != 0) {
176 // 2M
177 continue;
178 }
179 L1PageTable = (UINT64 *)(UINTN)(L2PageTable[Index2] & PAGING_4K_ADDRESS_MASK_64);
180 if (L1PageTable == NULL) {
181 continue;
182 }
183 SmmSetMemoryAttributesEx ((EFI_PHYSICAL_ADDRESS)(UINTN)L1PageTable, SIZE_4KB, EFI_MEMORY_RO, &IsSplitted);
184 PageTableSplitted = (PageTableSplitted || IsSplitted);
185 }
186 }
187 } while (PageTableSplitted);
188
189 //
190 // Enable write protection, after page table updated.
191 //
192 AsmWriteCr0 (AsmReadCr0() | CR0_WP);
193
194 return ;
195 }