]> git.proxmox.com Git - mirror_edk2.git/blob - EdkCompatibilityPkg/Compatibility/SmmBaseHelper/SmmBaseHelper.c
Use on-demand paging for CpuSaveStates read/write. It was measured about 200us for...
[mirror_edk2.git] / EdkCompatibilityPkg / Compatibility / SmmBaseHelper / SmmBaseHelper.c
1 /** @file
2 SMM Base Helper SMM driver.
3
4 This driver is the counterpart of the SMM Base On SMM Base2 Thunk driver. It
5 provides helping services in SMM to the SMM Base On SMM Base2 Thunk driver.
6
7 Copyright (c) 2009 - 2010, Intel Corporation
8 All rights reserved. This program and the accompanying materials
9 are licensed and made available under the terms and conditions of the BSD License
10 which accompanies this distribution. The full text of the license may be found at
11 http://opensource.org/licenses/bsd-license.php
12
13 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
14 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
15
16 **/
17
18 #include <PiSmm.h>
19 #include <Library/DebugLib.h>
20 #include <Library/UefiBootServicesTableLib.h>
21 #include <Library/SmmServicesTableLib.h>
22 #include <Library/BaseLib.h>
23 #include <Library/BaseMemoryLib.h>
24 #include <Library/PeCoffLib.h>
25 #include <Library/DevicePathLib.h>
26 #include <Library/CacheMaintenanceLib.h>
27 #include <Library/MemoryAllocationLib.h>
28 #include <Library/SynchronizationLib.h>
29 #include <Library/CpuLib.h>
30 #include <Guid/SmmBaseThunkCommunication.h>
31 #include <Protocol/SmmBaseHelperReady.h>
32 #include <Protocol/SmmCpu.h>
33 #include <Protocol/LoadedImage.h>
34 #include <Protocol/SmmCpuSaveState.h>
35 #include <Protocol/MpService.h>
36 #include <Protocol/LoadPe32Image.h>
37 #include <Protocol/SmmReadyToLock.h>
38
39 ///
40 /// Structure for tracking paired information of registered Framework SMI handler
41 /// and correpsonding dispatch handle for SMI handler thunk.
42 ///
43 typedef struct {
44 LIST_ENTRY Link;
45 EFI_HANDLE DispatchHandle;
46 EFI_HANDLE SmmImageHandle;
47 EFI_SMM_CALLBACK_ENTRY_POINT CallbackAddress;
48 VOID *CommunicationBuffer;
49 UINTN *SourceSize;
50 } CALLBACK_INFO;
51
52 typedef struct {
53 ///
54 /// PI SMM CPU Save State register index
55 ///
56 EFI_SMM_SAVE_STATE_REGISTER Register;
57 ///
58 /// Offset in Framework SMST
59 ///
60 UINTN Offset;
61 } CPU_SAVE_STATE_CONVERSION;
62
63 #define CPU_SAVE_STATE_GET_OFFSET(Field) (UINTN)(&(((EFI_SMM_CPU_SAVE_STATE *) 0)->Ia32SaveState.Field))
64
65
66 EFI_HANDLE mDispatchHandle;
67 EFI_SMM_CPU_PROTOCOL *mSmmCpu;
68 EFI_PE32_IMAGE_PROTOCOL *mLoadPe32Image;
69 EFI_GUID mEfiSmmCpuIoGuid = EFI_SMM_CPU_IO_GUID;
70 EFI_SMM_BASE_HELPER_READY_PROTOCOL *mSmmBaseHelperReady;
71 EFI_SMM_SYSTEM_TABLE *mFrameworkSmst;
72 UINTN mNumberOfProcessors;
73 BOOLEAN mLocked = FALSE;
74 BOOLEAN mPageTableHookEnabled;
75 BOOLEAN mHookInitialized;
76 UINT64 *mCpuStatePageTable;
77 SPIN_LOCK mPFLock;
78 UINT64 mPhyMask;
79 VOID *mOriginalHandler;
80 EFI_SMM_CPU_SAVE_STATE *mShadowSaveState;
81
82 LIST_ENTRY mCallbackInfoListHead = INITIALIZE_LIST_HEAD_VARIABLE (mCallbackInfoListHead);
83
84 CPU_SAVE_STATE_CONVERSION mCpuSaveStateConvTable[] = {
85 {EFI_SMM_SAVE_STATE_REGISTER_LDTBASE , CPU_SAVE_STATE_GET_OFFSET(LDTBase)},
86 {EFI_SMM_SAVE_STATE_REGISTER_ES , CPU_SAVE_STATE_GET_OFFSET(ES)},
87 {EFI_SMM_SAVE_STATE_REGISTER_CS , CPU_SAVE_STATE_GET_OFFSET(CS)},
88 {EFI_SMM_SAVE_STATE_REGISTER_SS , CPU_SAVE_STATE_GET_OFFSET(SS)},
89 {EFI_SMM_SAVE_STATE_REGISTER_DS , CPU_SAVE_STATE_GET_OFFSET(DS)},
90 {EFI_SMM_SAVE_STATE_REGISTER_FS , CPU_SAVE_STATE_GET_OFFSET(FS)},
91 {EFI_SMM_SAVE_STATE_REGISTER_GS , CPU_SAVE_STATE_GET_OFFSET(GS)},
92 {EFI_SMM_SAVE_STATE_REGISTER_TR_SEL , CPU_SAVE_STATE_GET_OFFSET(TR)},
93 {EFI_SMM_SAVE_STATE_REGISTER_DR7 , CPU_SAVE_STATE_GET_OFFSET(DR7)},
94 {EFI_SMM_SAVE_STATE_REGISTER_DR6 , CPU_SAVE_STATE_GET_OFFSET(DR6)},
95 {EFI_SMM_SAVE_STATE_REGISTER_RAX , CPU_SAVE_STATE_GET_OFFSET(EAX)},
96 {EFI_SMM_SAVE_STATE_REGISTER_RBX , CPU_SAVE_STATE_GET_OFFSET(EBX)},
97 {EFI_SMM_SAVE_STATE_REGISTER_RCX , CPU_SAVE_STATE_GET_OFFSET(ECX)},
98 {EFI_SMM_SAVE_STATE_REGISTER_RDX , CPU_SAVE_STATE_GET_OFFSET(EDX)},
99 {EFI_SMM_SAVE_STATE_REGISTER_RSP , CPU_SAVE_STATE_GET_OFFSET(ESP)},
100 {EFI_SMM_SAVE_STATE_REGISTER_RBP , CPU_SAVE_STATE_GET_OFFSET(EBP)},
101 {EFI_SMM_SAVE_STATE_REGISTER_RSI , CPU_SAVE_STATE_GET_OFFSET(ESI)},
102 {EFI_SMM_SAVE_STATE_REGISTER_RDI , CPU_SAVE_STATE_GET_OFFSET(EDI)},
103 {EFI_SMM_SAVE_STATE_REGISTER_RIP , CPU_SAVE_STATE_GET_OFFSET(EIP)},
104 {EFI_SMM_SAVE_STATE_REGISTER_RFLAGS , CPU_SAVE_STATE_GET_OFFSET(EFLAGS)},
105 {EFI_SMM_SAVE_STATE_REGISTER_CR0 , CPU_SAVE_STATE_GET_OFFSET(CR0)},
106 {EFI_SMM_SAVE_STATE_REGISTER_CR3 , CPU_SAVE_STATE_GET_OFFSET(CR3)}
107 };
108
109 VOID
110 PageFaultHandlerHook (
111 VOID
112 );
113
114 VOID
115 ReadCpuSaveState (
116 UINTN CpuIndex,
117 EFI_SMM_CPU_SAVE_STATE *ToRead
118 )
119 {
120 EFI_STATUS Status;
121 UINTN Index;
122 EFI_SMM_CPU_STATE *State;
123 EFI_SMI_CPU_SAVE_STATE *SaveState;
124
125 State = (EFI_SMM_CPU_STATE *)gSmst->CpuSaveState[CpuIndex];
126 if (ToRead != NULL) {
127 SaveState = &ToRead->Ia32SaveState;
128 } else {
129 SaveState = &mFrameworkSmst->CpuSaveState[CpuIndex].Ia32SaveState;
130 }
131
132 if (State->x86.SMMRevId < EFI_SMM_MIN_REV_ID_x64) {
133 SaveState->SMBASE = State->x86.SMBASE;
134 SaveState->SMMRevId = State->x86.SMMRevId;
135 SaveState->IORestart = State->x86.IORestart;
136 SaveState->AutoHALTRestart = State->x86.AutoHALTRestart;
137 } else {
138 SaveState->SMBASE = State->x64.SMBASE;
139 SaveState->SMMRevId = State->x64.SMMRevId;
140 SaveState->IORestart = State->x64.IORestart;
141 SaveState->AutoHALTRestart = State->x64.AutoHALTRestart;
142 }
143
144 for (Index = 0; Index < sizeof (mCpuSaveStateConvTable) / sizeof (CPU_SAVE_STATE_CONVERSION); Index++) {
145 ///
146 /// Try to use SMM CPU Protocol to access CPU save states if possible
147 ///
148 Status = mSmmCpu->ReadSaveState (
149 mSmmCpu,
150 (UINTN)sizeof (UINT32),
151 mCpuSaveStateConvTable[Index].Register,
152 CpuIndex,
153 ((UINT8 *)SaveState) + mCpuSaveStateConvTable[Index].Offset
154 );
155 ASSERT_EFI_ERROR (Status);
156 }
157 }
158
159 VOID
160 WriteCpuSaveState (
161 UINTN CpuIndex,
162 EFI_SMM_CPU_SAVE_STATE *ToWrite
163 )
164 {
165 EFI_STATUS Status;
166 UINTN Index;
167 EFI_SMI_CPU_SAVE_STATE *SaveState;
168
169 if (ToWrite != NULL) {
170 SaveState = &ToWrite->Ia32SaveState;
171 } else {
172 SaveState = &mFrameworkSmst->CpuSaveState[CpuIndex].Ia32SaveState;
173 }
174
175 for (Index = 0; Index < sizeof (mCpuSaveStateConvTable) / sizeof (CPU_SAVE_STATE_CONVERSION); Index++) {
176 Status = mSmmCpu->WriteSaveState (
177 mSmmCpu,
178 (UINTN)sizeof (UINT32),
179 mCpuSaveStateConvTable[Index].Register,
180 CpuIndex,
181 ((UINT8 *)SaveState) +
182 mCpuSaveStateConvTable[Index].Offset
183 );
184 }
185 }
186
187 VOID
188 ReadWriteCpuStatePage (
189 UINT64 PageAddress,
190 BOOLEAN IsRead
191 )
192 {
193 UINTN FirstSSIndex; // Index of first CpuSaveState in the page
194 UINTN LastSSIndex; // Index of last CpuSaveState in the page
195 BOOLEAN FirstSSAligned; // Whether first CpuSaveState is page-aligned
196 BOOLEAN LastSSAligned; // Whether the end of last CpuSaveState is page-aligned
197 UINTN ClippedSize;
198 UINTN CpuIndex;
199
200 FirstSSIndex = ((UINTN)PageAddress - (UINTN)mFrameworkSmst->CpuSaveState) / sizeof (EFI_SMM_CPU_SAVE_STATE);
201 FirstSSAligned = TRUE;
202 if (((UINTN)PageAddress - (UINTN)mFrameworkSmst->CpuSaveState) % sizeof (EFI_SMM_CPU_SAVE_STATE) != 0) {
203 FirstSSIndex++;
204 FirstSSAligned = FALSE;
205 }
206 LastSSIndex = ((UINTN)PageAddress + SIZE_4KB - (UINTN)mFrameworkSmst->CpuSaveState - 1) / sizeof (EFI_SMM_CPU_SAVE_STATE);
207 LastSSAligned = TRUE;
208 if (((UINTN)PageAddress + SIZE_4KB - (UINTN)mFrameworkSmst->CpuSaveState) % sizeof (EFI_SMM_CPU_SAVE_STATE) != 0) {
209 LastSSIndex--;
210 LastSSAligned = FALSE;
211 }
212 for (CpuIndex = FirstSSIndex; CpuIndex <= LastSSIndex && CpuIndex < mNumberOfProcessors; CpuIndex++) {
213 if (IsRead) {
214 ReadCpuSaveState (CpuIndex, NULL);
215 } else {
216 WriteCpuSaveState (CpuIndex, NULL);
217 }
218 }
219 if (!FirstSSAligned) {
220 ReadCpuSaveState (FirstSSIndex - 1, mShadowSaveState);
221 ClippedSize = (UINTN)&mFrameworkSmst->CpuSaveState[FirstSSIndex] & (SIZE_4KB - 1);
222 if (IsRead) {
223 CopyMem ((VOID*)(UINTN)PageAddress, (VOID*)((UINTN)(mShadowSaveState + 1) - ClippedSize), ClippedSize);
224 } else {
225 CopyMem ((VOID*)((UINTN)(mShadowSaveState + 1) - ClippedSize), (VOID*)(UINTN)PageAddress, ClippedSize);
226 WriteCpuSaveState (FirstSSIndex - 1, mShadowSaveState);
227 }
228 }
229 if (!LastSSAligned && LastSSIndex + 1 < mNumberOfProcessors) {
230 ReadCpuSaveState (LastSSIndex + 1, mShadowSaveState);
231 ClippedSize = SIZE_4KB - ((UINTN)&mFrameworkSmst->CpuSaveState[LastSSIndex + 1] & (SIZE_4KB - 1));
232 if (IsRead) {
233 CopyMem (&mFrameworkSmst->CpuSaveState[LastSSIndex + 1], mShadowSaveState, ClippedSize);
234 } else {
235 CopyMem (mShadowSaveState, &mFrameworkSmst->CpuSaveState[LastSSIndex + 1], ClippedSize);
236 WriteCpuSaveState (LastSSIndex + 1, mShadowSaveState);
237 }
238 }
239 }
240
241 BOOLEAN
242 PageFaultHandler (
243 VOID
244 )
245 {
246 BOOLEAN IsHandled;
247 UINT64 *PageTable;
248 UINT64 PFAddress;
249 UINTN NumCpuStatePages;
250
251 ASSERT (mPageTableHookEnabled);
252 AcquireSpinLock (&mPFLock);
253
254 PageTable = (UINT64*)(UINTN)(AsmReadCr3 () & mPhyMask);
255 PFAddress = AsmReadCr2 ();
256 NumCpuStatePages = EFI_SIZE_TO_PAGES (mNumberOfProcessors * sizeof (EFI_SMM_CPU_SAVE_STATE));
257 IsHandled = FALSE;
258 if (((UINTN)mFrameworkSmst->CpuSaveState & ~(SIZE_2MB-1)) == (PFAddress & ~(SIZE_2MB-1))) {
259 if ((UINTN)mFrameworkSmst->CpuSaveState <= PFAddress &&
260 PFAddress < (UINTN)mFrameworkSmst->CpuSaveState + EFI_PAGES_TO_SIZE (NumCpuStatePages)
261 ) {
262 mCpuStatePageTable[BitFieldRead64 (PFAddress, 12, 20)] |= BIT0 | BIT1; // present and rw
263 CpuFlushTlb ();
264 ReadWriteCpuStatePage (PFAddress & ~(SIZE_4KB-1), TRUE);
265 IsHandled = TRUE;
266 } else {
267 ASSERT (FALSE);
268 }
269 }
270
271 ReleaseSpinLock (&mPFLock);
272 return IsHandled;
273 }
274
275 VOID
276 WriteBackDirtyPages (
277 VOID
278 )
279 {
280 UINTN NumCpuStatePages;
281 UINTN PTIndex;
282 UINTN PTStartIndex;
283 UINTN PTEndIndex;
284
285 NumCpuStatePages = EFI_SIZE_TO_PAGES (mNumberOfProcessors * sizeof (EFI_SMM_CPU_SAVE_STATE));
286 PTStartIndex = (UINTN)BitFieldRead64 ((UINT64)mFrameworkSmst->CpuSaveState, 12, 20);
287 PTEndIndex = (UINTN)BitFieldRead64 ((UINT64)mFrameworkSmst->CpuSaveState + EFI_PAGES_TO_SIZE(NumCpuStatePages) - 1, 12, 20);
288 for (PTIndex = PTStartIndex; PTIndex <= PTEndIndex; PTIndex++) {
289 if ((mCpuStatePageTable[PTIndex] & (BIT0|BIT6)) == (BIT0|BIT6)) { // present and dirty?
290 ReadWriteCpuStatePage (mCpuStatePageTable[PTIndex] & mPhyMask, FALSE);
291 }
292 }
293 }
294
295 VOID
296 HookPageFaultHandler (
297 VOID
298 )
299 {
300 IA32_DESCRIPTOR Idtr;
301 IA32_IDT_GATE_DESCRIPTOR *IdtGateDesc;
302 UINT32 OffsetUpper;
303
304 InitializeSpinLock (&mPFLock);
305
306 AsmReadIdtr (&Idtr);
307 IdtGateDesc = (IA32_IDT_GATE_DESCRIPTOR *) Idtr.Base;
308 OffsetUpper = *(UINT32*)((UINT64*)IdtGateDesc + 1);
309 mOriginalHandler = (VOID *)(UINTN)(LShiftU64 (OffsetUpper, 32) + IdtGateDesc[14].Bits.OffsetLow + (IdtGateDesc[14].Bits.OffsetHigh << 16));
310 IdtGateDesc[14].Bits.OffsetLow = (UINT32)((UINTN)PageFaultHandlerHook & ((1 << 16) - 1));
311 IdtGateDesc[14].Bits.OffsetHigh = (UINT32)(((UINTN)PageFaultHandlerHook >> 16) & ((1 << 16) - 1));
312 }
313
314 UINT64 *
315 InitCpuStatePageTable (
316 VOID *HookData
317 )
318 {
319 UINTN Index;
320 UINT64 *PageTable;
321 UINT64 *PDPTE;
322 UINT64 HookAddress;
323 UINT64 PDE;
324 UINT64 Address;
325
326 //
327 // Initialize physical address mask
328 // NOTE: Physical memory above virtual address limit is not supported !!!
329 //
330 AsmCpuid (0x80000008, (UINT32*)&Index, NULL, NULL, NULL);
331 mPhyMask = LShiftU64 (1, (UINT8)Index) - 1;
332 mPhyMask &= (1ull << 48) - EFI_PAGE_SIZE;
333
334 HookAddress = (UINT64)(UINTN)HookData;
335 PageTable = (UINT64 *)(UINTN)(AsmReadCr3 () & mPhyMask);
336 PageTable = (UINT64 *)(UINTN)(PageTable[BitFieldRead64 (HookAddress, 39, 47)] & mPhyMask);
337 PageTable = (UINT64 *)(UINTN)(PageTable[BitFieldRead64 (HookAddress, 30, 38)] & mPhyMask);
338
339 PDPTE = (UINT64 *)(UINTN)PageTable;
340 PDE = PDPTE[BitFieldRead64 (HookAddress, 21, 29)];
341 ASSERT ((PDE & BIT0) != 0); // Present and 2M Page
342
343 if ((PDE & BIT7) == 0) { // 4KB Page Directory
344 PageTable = (UINT64 *)(UINTN)(PDE & mPhyMask);
345 } else {
346 ASSERT ((PDE & mPhyMask) == (HookAddress & ~(SIZE_2MB-1))); // 2MB Page Point to HookAddress
347 PageTable = AllocatePages (1);
348 Address = HookAddress & ~(SIZE_2MB-1);
349 for (Index = 0; Index < 512; Index++) {
350 PageTable[Index] = Address | BIT0 | BIT1; // Present and RW
351 Address += SIZE_4KB;
352 }
353 PDPTE[BitFieldRead64 (HookAddress, 21, 29)] = (UINT64)(UINTN)PageTable | BIT0 | BIT1; // Present and RW
354 }
355 return PageTable;
356 }
357
358 VOID
359 HookCpuStateMemory (
360 EFI_SMM_CPU_SAVE_STATE *CpuSaveState
361 )
362 {
363 UINT64 Index;
364 UINT64 PTStartIndex;
365 UINT64 PTEndIndex;
366
367 PTStartIndex = BitFieldRead64 ((UINTN)CpuSaveState, 12, 20);
368 PTEndIndex = BitFieldRead64 ((UINTN)CpuSaveState + mNumberOfProcessors * sizeof (EFI_SMM_CPU_SAVE_STATE) - 1, 12, 20);
369 for (Index = PTStartIndex; Index <= PTEndIndex; Index++) {
370 mCpuStatePageTable[Index] &= ~(BIT0|BIT5|BIT6); // not present nor accessed nor dirty
371 }
372 }
373
374 /**
375 Framework SMST SmmInstallConfigurationTable() Thunk.
376
377 This thunk calls the PI SMM SmmInstallConfigurationTable() and then update the configuration
378 table related fields in the Framework SMST because the PI SMM SmmInstallConfigurationTable()
379 function may modify these fields.
380
381 @param[in] SystemTable A pointer to the SMM System Table.
382 @param[in] Guid A pointer to the GUID for the entry to add, update, or remove.
383 @param[in] Table A pointer to the buffer of the table to add.
384 @param[in] TableSize The size of the table to install.
385
386 @retval EFI_SUCCESS The (Guid, Table) pair was added, updated, or removed.
387 @retval EFI_INVALID_PARAMETER Guid is not valid.
388 @retval EFI_NOT_FOUND An attempt was made to delete a non-existent entry.
389 @retval EFI_OUT_OF_RESOURCES There is not enough memory available to complete the operation.
390 **/
391 EFI_STATUS
392 EFIAPI
393 SmmInstallConfigurationTable (
394 IN EFI_SMM_SYSTEM_TABLE *SystemTable,
395 IN EFI_GUID *Guid,
396 IN VOID *Table,
397 IN UINTN TableSize
398 )
399 {
400 EFI_STATUS Status;
401
402 Status = gSmst->SmmInstallConfigurationTable (gSmst, Guid, Table, TableSize);
403 if (!EFI_ERROR (Status)) {
404 mFrameworkSmst->NumberOfTableEntries = gSmst->NumberOfTableEntries;
405 mFrameworkSmst->SmmConfigurationTable = gSmst->SmmConfigurationTable;
406 }
407 return Status;
408 }
409
410 VOID
411 InitHook (
412 EFI_SMM_SYSTEM_TABLE *FrameworkSmst
413 )
414 {
415 UINTN NumCpuStatePages;
416 UINTN CpuStatePage;
417 UINTN Bottom2MPage;
418 UINTN Top2MPage;
419
420 mPageTableHookEnabled = FALSE;
421 NumCpuStatePages = EFI_SIZE_TO_PAGES (mNumberOfProcessors * sizeof (EFI_SMM_CPU_SAVE_STATE));
422 //
423 // Only hook page table for X64 image and less than 2MB needed to hold all CPU Save States
424 //
425 if (EFI_IMAGE_MACHINE_TYPE_SUPPORTED(EFI_IMAGE_MACHINE_X64) && NumCpuStatePages <= EFI_SIZE_TO_PAGES (SIZE_2MB)) {
426 //
427 // Allocate double page size to make sure all CPU Save States are in one 2MB page.
428 //
429 CpuStatePage = (UINTN)AllocatePages (NumCpuStatePages * 2);
430 ASSERT (CpuStatePage != 0);
431 Bottom2MPage = CpuStatePage & ~(SIZE_2MB-1);
432 Top2MPage = (CpuStatePage + EFI_PAGES_TO_SIZE (NumCpuStatePages * 2) - 1) & ~(SIZE_2MB-1);
433 if (Bottom2MPage == Top2MPage ||
434 CpuStatePage + EFI_PAGES_TO_SIZE (NumCpuStatePages * 2) - Top2MPage >= EFI_PAGES_TO_SIZE (NumCpuStatePages)
435 ) {
436 //
437 // If the allocated 4KB pages are within the same 2MB page or higher portion is larger, use higher portion pages.
438 //
439 FrameworkSmst->CpuSaveState = (EFI_SMM_CPU_SAVE_STATE *)(CpuStatePage + EFI_PAGES_TO_SIZE (NumCpuStatePages));
440 FreePages ((VOID*)CpuStatePage, NumCpuStatePages);
441 } else {
442 FrameworkSmst->CpuSaveState = (EFI_SMM_CPU_SAVE_STATE *)CpuStatePage;
443 FreePages ((VOID*)(CpuStatePage + EFI_PAGES_TO_SIZE (NumCpuStatePages)), NumCpuStatePages);
444 }
445 //
446 // Add temporary working buffer for hooking
447 //
448 mShadowSaveState = (EFI_SMM_CPU_SAVE_STATE*) AllocatePool (sizeof (EFI_SMM_CPU_SAVE_STATE));
449 ASSERT (mShadowSaveState != NULL);
450 //
451 // Allocate and initialize 4KB Page Table for hooking CpuSaveState.
452 // Replace the original 2MB PDE with new 4KB page table.
453 //
454 mCpuStatePageTable = InitCpuStatePageTable (FrameworkSmst->CpuSaveState);
455 //
456 // Mark PTE for CpuSaveState as non-exist.
457 //
458 HookCpuStateMemory (FrameworkSmst->CpuSaveState);
459 HookPageFaultHandler ();
460 CpuFlushTlb ();
461 mPageTableHookEnabled = TRUE;
462 }
463 mHookInitialized = TRUE;
464 }
465
466 /**
467 Construct a Framework SMST based on the PI SMM SMST.
468
469 @return Pointer to the constructed Framework SMST.
470 **/
471 EFI_SMM_SYSTEM_TABLE *
472 ConstructFrameworkSmst (
473 VOID
474 )
475 {
476 EFI_SMM_SYSTEM_TABLE *FrameworkSmst;
477
478 FrameworkSmst = (EFI_SMM_SYSTEM_TABLE *)AllocatePool (sizeof (EFI_SMM_SYSTEM_TABLE));
479 ASSERT (FrameworkSmst != NULL);
480
481 ///
482 /// Copy same things from PI SMST to Framework SMST
483 ///
484 CopyMem (FrameworkSmst, gSmst, (UINTN)(&((EFI_SMM_SYSTEM_TABLE *)0)->SmmIo));
485 CopyMem (
486 &FrameworkSmst->SmmIo,
487 &gSmst->SmmIo,
488 sizeof (EFI_SMM_SYSTEM_TABLE) - (UINTN)(&((EFI_SMM_SYSTEM_TABLE *)0)->SmmIo)
489 );
490
491 ///
492 /// Update Framework SMST
493 ///
494 FrameworkSmst->Hdr.Revision = EFI_SMM_SYSTEM_TABLE_REVISION;
495 CopyGuid (&FrameworkSmst->EfiSmmCpuIoGuid, &mEfiSmmCpuIoGuid);
496
497 mHookInitialized = FALSE;
498 FrameworkSmst->CpuSaveState = (EFI_SMM_CPU_SAVE_STATE *)AllocateZeroPool (mNumberOfProcessors * sizeof (EFI_SMM_CPU_SAVE_STATE));
499 ASSERT (FrameworkSmst->CpuSaveState != NULL);
500
501 ///
502 /// Do not support floating point state now
503 ///
504 FrameworkSmst->CpuOptionalFloatingPointState = NULL;
505
506 FrameworkSmst->SmmInstallConfigurationTable = SmmInstallConfigurationTable;
507
508 return FrameworkSmst;
509 }
510
511 /**
512 Load a given Framework SMM driver into SMRAM and invoke its entry point.
513
514 @param[in] ParentImageHandle Parent Image Handle.
515 @param[in] FilePath Location of the image to be installed as the handler.
516 @param[in] SourceBuffer Optional source buffer in case the image file
517 is in memory.
518 @param[in] SourceSize Size of the source image file, if in memory.
519 @param[out] ImageHandle The handle that the base driver uses to decode
520 the handler. Unique among SMM handlers only,
521 not unique across DXE/EFI.
522
523 @retval EFI_SUCCESS The operation was successful.
524 @retval EFI_OUT_OF_RESOURCES There were no additional SMRAM resources to load the handler
525 @retval EFI_UNSUPPORTED Can not find its copy in normal memory.
526 @retval EFI_INVALID_PARAMETER The handlers was not the correct image type
527 **/
528 EFI_STATUS
529 LoadImage (
530 IN EFI_HANDLE ParentImageHandle,
531 IN EFI_DEVICE_PATH_PROTOCOL *FilePath,
532 IN VOID *SourceBuffer,
533 IN UINTN SourceSize,
534 OUT EFI_HANDLE *ImageHandle
535 )
536 {
537 EFI_STATUS Status;
538 UINTN PageCount;
539 UINTN OrgPageCount;
540 EFI_PHYSICAL_ADDRESS DstBuffer;
541
542 if (FilePath == NULL || ImageHandle == NULL) {
543 return EFI_INVALID_PARAMETER;
544 }
545
546 PageCount = 1;
547 do {
548 OrgPageCount = PageCount;
549 DstBuffer = (UINTN)-1;
550 Status = gSmst->SmmAllocatePages (
551 AllocateMaxAddress,
552 EfiRuntimeServicesCode,
553 PageCount,
554 &DstBuffer
555 );
556 if (EFI_ERROR (Status)) {
557 return Status;
558 }
559
560 Status = mLoadPe32Image->LoadPeImage (
561 mLoadPe32Image,
562 ParentImageHandle,
563 FilePath,
564 SourceBuffer,
565 SourceSize,
566 DstBuffer,
567 &PageCount,
568 ImageHandle,
569 NULL,
570 EFI_LOAD_PE_IMAGE_ATTRIBUTE_NONE
571 );
572 if (EFI_ERROR (Status)) {
573 FreePages ((VOID *)(UINTN)DstBuffer, OrgPageCount);
574 }
575 } while (Status == EFI_BUFFER_TOO_SMALL);
576
577 if (!EFI_ERROR (Status)) {
578 ///
579 /// Update MP state in Framework SMST before transferring control to Framework SMM driver entry point
580 /// in case it may invoke AP
581 ///
582 mFrameworkSmst->CurrentlyExecutingCpu = gSmst->CurrentlyExecutingCpu;
583
584 Status = gBS->StartImage (*ImageHandle, NULL, NULL);
585 if (EFI_ERROR (Status)) {
586 mLoadPe32Image->UnLoadPeImage (mLoadPe32Image, *ImageHandle);
587 *ImageHandle = NULL;
588 FreePages ((VOID *)(UINTN)DstBuffer, PageCount);
589 }
590 }
591
592 return Status;
593 }
594
595
596 /**
597 Thunk service of EFI_SMM_BASE_PROTOCOL.Register().
598
599 @param[in, out] FunctionData Pointer to SMMBASE_FUNCTION_DATA.
600 **/
601 VOID
602 Register (
603 IN OUT SMMBASE_FUNCTION_DATA *FunctionData
604 )
605 {
606 EFI_STATUS Status;
607
608 if (mLocked || FunctionData->Args.Register.LegacyIA32Binary) {
609 Status = EFI_UNSUPPORTED;
610 } else {
611 Status = LoadImage (
612 FunctionData->SmmBaseImageHandle,
613 FunctionData->Args.Register.FilePath,
614 FunctionData->Args.Register.SourceBuffer,
615 FunctionData->Args.Register.SourceSize,
616 FunctionData->Args.Register.ImageHandle
617 );
618 }
619 FunctionData->Status = Status;
620 }
621
622 /**
623 Thunk service of EFI_SMM_BASE_PROTOCOL.UnRegister().
624
625 @param[in, out] FunctionData Pointer to SMMBASE_FUNCTION_DATA.
626 **/
627 VOID
628 UnRegister (
629 IN OUT SMMBASE_FUNCTION_DATA *FunctionData
630 )
631 {
632 ///
633 /// Unregister not supported now
634 ///
635 FunctionData->Status = EFI_UNSUPPORTED;
636 }
637
638 /**
639 Search for Framework SMI handler information according to specific PI SMM dispatch handle.
640
641 @param[in] DispatchHandle The unique handle assigned by SmiHandlerRegister().
642
643 @return Pointer to CALLBACK_INFO. If NULL, no callback info record is found.
644 **/
645 CALLBACK_INFO *
646 GetCallbackInfo (
647 IN EFI_HANDLE DispatchHandle
648 )
649 {
650 LIST_ENTRY *Node;
651
652 Node = GetFirstNode (&mCallbackInfoListHead);
653 while (!IsNull (&mCallbackInfoListHead, Node)) {
654 if (((CALLBACK_INFO *)Node)->DispatchHandle == DispatchHandle) {
655 return (CALLBACK_INFO *)Node;
656 }
657 Node = GetNextNode (&mCallbackInfoListHead, Node);
658 }
659 return NULL;
660 }
661
662 /**
663 Callback thunk for Framework SMI handler.
664
665 This thunk functions calls the Framework SMI handler and converts the return value
666 defined from Framework SMI handlers to a correpsonding return value defined by PI SMM.
667
668 @param[in] DispatchHandle The unique handle assigned to this handler by SmiHandlerRegister().
669 @param[in] Context Points to an optional handler context which was specified when the
670 handler was registered.
671 @param[in, out] CommBuffer A pointer to a collection of data in memory that will
672 be conveyed from a non-SMM environment into an SMM environment.
673 @param[in, out] CommBufferSize The size of the CommBuffer.
674
675 @retval EFI_SUCCESS The interrupt was handled and quiesced. No other handlers
676 should still be called.
677 @retval EFI_WARN_INTERRUPT_SOURCE_QUIESCED The interrupt has been quiesced but other handlers should
678 still be called.
679 @retval EFI_WARN_INTERRUPT_SOURCE_PENDING The interrupt is still pending and other handlers should still
680 be called.
681 @retval EFI_INTERRUPT_PENDING The interrupt could not be quiesced.
682 **/
683 EFI_STATUS
684 EFIAPI
685 CallbackThunk (
686 IN EFI_HANDLE DispatchHandle,
687 IN CONST VOID *Context OPTIONAL,
688 IN OUT VOID *CommBuffer OPTIONAL,
689 IN OUT UINTN *CommBufferSize OPTIONAL
690 )
691 {
692 EFI_STATUS Status;
693 CALLBACK_INFO *CallbackInfo;
694 UINTN CpuIndex;
695
696 ///
697 /// Before transferring the control into the Framework SMI handler, update CPU Save States
698 /// and MP states in the Framework SMST.
699 ///
700
701 if (!mHookInitialized) {
702 InitHook (mFrameworkSmst);
703 }
704 if (mPageTableHookEnabled) {
705 HookCpuStateMemory (mFrameworkSmst->CpuSaveState);
706 CpuFlushTlb ();
707 } else {
708 for (CpuIndex = 0; CpuIndex < mNumberOfProcessors; CpuIndex++) {
709 ReadCpuSaveState (CpuIndex, NULL);
710 }
711 }
712
713 mFrameworkSmst->SmmStartupThisAp = gSmst->SmmStartupThisAp;
714 mFrameworkSmst->NumberOfCpus = mNumberOfProcessors;
715 mFrameworkSmst->CurrentlyExecutingCpu = gSmst->CurrentlyExecutingCpu;
716
717 ///
718 /// Search for Framework SMI handler information
719 ///
720 CallbackInfo = GetCallbackInfo (DispatchHandle);
721 ASSERT (CallbackInfo != NULL);
722
723 ///
724 /// Thunk into original Framwork SMI handler
725 ///
726 Status = (CallbackInfo->CallbackAddress) (
727 CallbackInfo->SmmImageHandle,
728 CallbackInfo->CommunicationBuffer,
729 CallbackInfo->SourceSize
730 );
731 ///
732 /// Save CPU Save States in case any of them was modified
733 ///
734 if (mPageTableHookEnabled) {
735 WriteBackDirtyPages ();
736 } else {
737 for (CpuIndex = 0; CpuIndex < mNumberOfProcessors; CpuIndex++) {
738 WriteCpuSaveState (CpuIndex, NULL);
739 }
740 }
741
742 ///
743 /// Conversion of returned status code
744 ///
745 switch (Status) {
746 case EFI_HANDLER_SUCCESS:
747 Status = EFI_WARN_INTERRUPT_SOURCE_QUIESCED;
748 break;
749 case EFI_HANDLER_CRITICAL_EXIT:
750 case EFI_HANDLER_SOURCE_QUIESCED:
751 Status = EFI_SUCCESS;
752 break;
753 case EFI_HANDLER_SOURCE_PENDING:
754 Status = EFI_WARN_INTERRUPT_SOURCE_PENDING;
755 break;
756 }
757 return Status;
758 }
759
760 /**
761 Thunk service of EFI_SMM_BASE_PROTOCOL.RegisterCallback().
762
763 @param[in, out] FunctionData Pointer to SMMBASE_FUNCTION_DATA.
764 **/
765 VOID
766 RegisterCallback (
767 IN OUT SMMBASE_FUNCTION_DATA *FunctionData
768 )
769 {
770 CALLBACK_INFO *Buffer;
771
772 if (mLocked) {
773 FunctionData->Status = EFI_UNSUPPORTED;
774 return;
775 }
776
777 ///
778 /// Note that MakeLast and FloatingPointSave options are not supported in PI SMM
779 ///
780
781 ///
782 /// Allocate buffer for callback thunk information
783 ///
784 Buffer = (CALLBACK_INFO *)AllocateZeroPool (sizeof (CALLBACK_INFO));
785 if (Buffer == NULL) {
786 FunctionData->Status = EFI_OUT_OF_RESOURCES;
787 return;
788 }
789
790 ///
791 /// Fill SmmImageHandle and CallbackAddress into the thunk
792 ///
793 Buffer->SmmImageHandle = FunctionData->Args.RegisterCallback.SmmImageHandle;
794 Buffer->CallbackAddress = FunctionData->Args.RegisterCallback.CallbackAddress;
795
796 ///
797 /// Register the thunk code as a root SMI handler
798 ///
799 FunctionData->Status = gSmst->SmiHandlerRegister (
800 CallbackThunk,
801 NULL,
802 &Buffer->DispatchHandle
803 );
804 if (EFI_ERROR (FunctionData->Status)) {
805 FreePool (Buffer);
806 return;
807 }
808
809 ///
810 /// Save this callback info
811 ///
812 InsertTailList (&mCallbackInfoListHead, &Buffer->Link);
813 }
814
815
816 /**
817 Thunk service of EFI_SMM_BASE_PROTOCOL.SmmAllocatePool().
818
819 @param[in, out] FunctionData Pointer to SMMBASE_FUNCTION_DATA.
820 **/
821 VOID
822 HelperAllocatePool (
823 IN OUT SMMBASE_FUNCTION_DATA *FunctionData
824 )
825 {
826 if (mLocked) {
827 FunctionData->Status = EFI_UNSUPPORTED;
828 } else {
829 FunctionData->Status = gSmst->SmmAllocatePool (
830 FunctionData->Args.AllocatePool.PoolType,
831 FunctionData->Args.AllocatePool.Size,
832 FunctionData->Args.AllocatePool.Buffer
833 );
834 }
835 }
836
837 /**
838 Thunk service of EFI_SMM_BASE_PROTOCOL.SmmFreePool().
839
840 @param[in, out] FunctionData Pointer to SMMBASE_FUNCTION_DATA.
841 **/
842 VOID
843 HelperFreePool (
844 IN OUT SMMBASE_FUNCTION_DATA *FunctionData
845 )
846 {
847 if (mLocked) {
848 FunctionData->Status = EFI_UNSUPPORTED;
849 } else {
850 FreePool (FunctionData->Args.FreePool.Buffer);
851 FunctionData->Status = EFI_SUCCESS;
852 }
853 }
854
855 /**
856 Thunk service of EFI_SMM_BASE_PROTOCOL.Communicate().
857
858 @param[in, out] FunctionData Pointer to SMMBASE_FUNCTION_DATA.
859 **/
860 VOID
861 HelperCommunicate (
862 IN OUT SMMBASE_FUNCTION_DATA *FunctionData
863 )
864 {
865 LIST_ENTRY *Node;
866 CALLBACK_INFO *CallbackInfo;
867
868 if (FunctionData->Args.Communicate.CommunicationBuffer == NULL) {
869 FunctionData->Status = EFI_INVALID_PARAMETER;
870 return;
871 }
872
873 Node = GetFirstNode (&mCallbackInfoListHead);
874 while (!IsNull (&mCallbackInfoListHead, Node)) {
875 CallbackInfo = (CALLBACK_INFO *)Node;
876
877 if (FunctionData->Args.Communicate.ImageHandle == CallbackInfo->SmmImageHandle) {
878 CallbackInfo->CommunicationBuffer = FunctionData->Args.Communicate.CommunicationBuffer;
879 CallbackInfo->SourceSize = FunctionData->Args.Communicate.SourceSize;
880
881 ///
882 /// The message was successfully posted.
883 ///
884 FunctionData->Status = EFI_SUCCESS;
885 return;
886 }
887 Node = GetNextNode (&mCallbackInfoListHead, Node);
888 }
889
890 FunctionData->Status = EFI_INVALID_PARAMETER;
891 }
892
893 /**
894 Communication service SMI Handler entry.
895
896 This SMI handler provides services for the SMM Base Thunk driver.
897
898 @param[in] DispatchHandle The unique handle assigned to this handler by SmiHandlerRegister().
899 @param[in] RegisterContext Points to an optional handler context which was specified when the
900 handler was registered.
901 @param[in, out] CommBuffer A pointer to a collection of data in memory that will
902 be conveyed from a non-SMM environment into an SMM environment.
903 @param[in, out] CommBufferSize The size of the CommBuffer.
904
905 @retval EFI_SUCCESS The interrupt was handled and quiesced. No other handlers
906 should still be called.
907 @retval EFI_WARN_INTERRUPT_SOURCE_QUIESCED The interrupt has been quiesced but other handlers should
908 still be called.
909 @retval EFI_WARN_INTERRUPT_SOURCE_PENDING The interrupt is still pending and other handlers should still
910 be called.
911 @retval EFI_INTERRUPT_PENDING The interrupt could not be quiesced.
912 **/
913 EFI_STATUS
914 EFIAPI
915 SmmHandlerEntry (
916 IN EFI_HANDLE DispatchHandle,
917 IN CONST VOID *RegisterContext,
918 IN OUT VOID *CommBuffer,
919 IN OUT UINTN *CommBufferSize
920 )
921 {
922 SMMBASE_FUNCTION_DATA *FunctionData;
923
924 ASSERT (CommBuffer != NULL);
925 ASSERT (*CommBufferSize == sizeof (SMMBASE_FUNCTION_DATA));
926
927 FunctionData = (SMMBASE_FUNCTION_DATA *)CommBuffer;
928
929 switch (FunctionData->Function) {
930 case SmmBaseFunctionRegister:
931 Register (FunctionData);
932 break;
933 case SmmBaseFunctionUnregister:
934 UnRegister (FunctionData);
935 break;
936 case SmmBaseFunctionRegisterCallback:
937 RegisterCallback (FunctionData);
938 break;
939 case SmmBaseFunctionAllocatePool:
940 HelperAllocatePool (FunctionData);
941 break;
942 case SmmBaseFunctionFreePool:
943 HelperFreePool (FunctionData);
944 break;
945 case SmmBaseFunctionCommunicate:
946 HelperCommunicate (FunctionData);
947 break;
948 default:
949 ASSERT (FALSE);
950 FunctionData->Status = EFI_UNSUPPORTED;
951 }
952 return EFI_SUCCESS;
953 }
954
955 /**
956 Smm Ready To Lock event notification handler.
957
958 It sets a flag indicating that SMRAM has been locked.
959
960 @param[in] Protocol Points to the protocol's unique identifier.
961 @param[in] Interface Points to the interface instance.
962 @param[in] Handle The handle on which the interface was installed.
963
964 @retval EFI_SUCCESS Notification handler runs successfully.
965 **/
966 EFI_STATUS
967 EFIAPI
968 SmmReadyToLockEventNotify (
969 IN CONST EFI_GUID *Protocol,
970 IN VOID *Interface,
971 IN EFI_HANDLE Handle
972 )
973 {
974 mLocked = TRUE;
975 return EFI_SUCCESS;
976 }
977
978 /**
979 Entry point function of the SMM Base Helper SMM driver.
980
981 @param[in] ImageHandle The firmware allocated handle for the EFI image.
982 @param[in] SystemTable A pointer to the EFI System Table.
983
984 @retval EFI_SUCCESS The entry point is executed successfully.
985 @retval other Some error occurs when executing this entry point.
986 **/
987 EFI_STATUS
988 EFIAPI
989 SmmBaseHelperMain (
990 IN EFI_HANDLE ImageHandle,
991 IN EFI_SYSTEM_TABLE *SystemTable
992 )
993 {
994 EFI_STATUS Status;
995 EFI_MP_SERVICES_PROTOCOL *MpServices;
996 EFI_HANDLE Handle;
997 UINTN NumberOfEnabledProcessors;
998 VOID *Registration;
999
1000 Handle = NULL;
1001 ///
1002 /// Locate SMM CPU Protocol which is used later to retrieve/update CPU Save States
1003 ///
1004 Status = gSmst->SmmLocateProtocol (&gEfiSmmCpuProtocolGuid, NULL, (VOID **) &mSmmCpu);
1005 ASSERT_EFI_ERROR (Status);
1006
1007 ///
1008 /// Locate PE32 Image Protocol which is used later to load Framework SMM driver
1009 ///
1010 Status = SystemTable->BootServices->LocateProtocol (&gEfiLoadPeImageProtocolGuid, NULL, (VOID **) &mLoadPe32Image);
1011 ASSERT_EFI_ERROR (Status);
1012
1013 //
1014 // Get MP Services Protocol
1015 //
1016 Status = SystemTable->BootServices->LocateProtocol (&gEfiMpServiceProtocolGuid, NULL, (VOID **)&MpServices);
1017 ASSERT_EFI_ERROR (Status);
1018
1019 //
1020 // Use MP Services Protocol to retrieve the number of processors and number of enabled processors
1021 //
1022 Status = MpServices->GetNumberOfProcessors (MpServices, &mNumberOfProcessors, &NumberOfEnabledProcessors);
1023 ASSERT_EFI_ERROR (Status);
1024
1025 ///
1026 /// Interface structure of SMM BASE Helper Ready Protocol is allocated from UEFI pool
1027 /// instead of SMM pool so that SMM Base Thunk driver can access it in Non-SMM mode.
1028 ///
1029 Status = gBS->AllocatePool (
1030 EfiBootServicesData,
1031 sizeof (EFI_SMM_BASE_HELPER_READY_PROTOCOL),
1032 (VOID **)&mSmmBaseHelperReady
1033 );
1034 ASSERT_EFI_ERROR (Status);
1035
1036 ///
1037 /// Construct Framework SMST from PI SMST
1038 ///
1039 mFrameworkSmst = ConstructFrameworkSmst ();
1040 mSmmBaseHelperReady->FrameworkSmst = mFrameworkSmst;
1041 mSmmBaseHelperReady->ServiceEntry = SmmHandlerEntry;
1042
1043 //
1044 // Register SMM Ready To Lock Protocol notification
1045 //
1046 Status = gSmst->SmmRegisterProtocolNotify (
1047 &gEfiSmmReadyToLockProtocolGuid,
1048 SmmReadyToLockEventNotify,
1049 &Registration
1050 );
1051 ASSERT_EFI_ERROR (Status);
1052
1053 ///
1054 /// Register SMM Base Helper services for SMM Base Thunk driver
1055 ///
1056 Status = gSmst->SmiHandlerRegister (SmmHandlerEntry, &gEfiSmmBaseThunkCommunicationGuid, &mDispatchHandle);
1057 ASSERT_EFI_ERROR (Status);
1058
1059 ///
1060 /// Install EFI SMM Base Helper Protocol in the UEFI handle database
1061 ///
1062 Status = gBS->InstallProtocolInterface (
1063 &Handle,
1064 &gEfiSmmBaseHelperReadyProtocolGuid,
1065 EFI_NATIVE_INTERFACE,
1066 mSmmBaseHelperReady
1067 );
1068 ASSERT_EFI_ERROR (Status);
1069
1070 return Status;
1071 }
1072