]> git.proxmox.com Git - mirror_edk2.git/blob - MdePkg/Library/SmmMemLib/SmmMemLib.c
8c78a0b426f2fd5a0effa7cb6d26002945a583d1
[mirror_edk2.git] / MdePkg / Library / SmmMemLib / SmmMemLib.c
1 /** @file
2 Instance of SMM memory check library.
3
4 SMM memory check library library implementation. This library consumes SMM_ACCESS2_PROTOCOL
5 to get SMRAM information. In order to use this library instance, the platform should produce
6 all SMRAM range via SMM_ACCESS2_PROTOCOL, including the range for firmware (like SMM Core
7 and SMM driver) and/or specific dedicated hardware.
8
9 Copyright (c) 2015 - 2018, Intel Corporation. All rights reserved.<BR>
10 This program and the accompanying materials
11 are licensed and made available under the terms and conditions of the BSD License
12 which accompanies this distribution. The full text of the license may be found at
13 http://opensource.org/licenses/bsd-license.php
14
15 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
16 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
17
18 **/
19
20
21 #include <PiSmm.h>
22
23 #include <Library/BaseLib.h>
24 #include <Library/BaseMemoryLib.h>
25 #include <Library/DebugLib.h>
26 #include <Library/MemoryAllocationLib.h>
27 #include <Library/UefiBootServicesTableLib.h>
28 #include <Library/SmmServicesTableLib.h>
29 #include <Library/HobLib.h>
30 #include <Protocol/SmmAccess2.h>
31 #include <Protocol/SmmReadyToLock.h>
32 #include <Protocol/SmmEndOfDxe.h>
33
34 #define NEXT_MEMORY_DESCRIPTOR(MemoryDescriptor, Size) \
35 ((EFI_MEMORY_DESCRIPTOR *)((UINT8 *)(MemoryDescriptor) + (Size)))
36
37 EFI_SMRAM_DESCRIPTOR *mSmmMemLibInternalSmramRanges;
38 UINTN mSmmMemLibInternalSmramCount;
39
40 //
41 // Maximum support address used to check input buffer
42 //
43 EFI_PHYSICAL_ADDRESS mSmmMemLibInternalMaximumSupportAddress = 0;
44
45 UINTN mMemoryMapEntryCount;
46 EFI_MEMORY_DESCRIPTOR *mMemoryMap;
47 UINTN mDescriptorSize;
48
49 VOID *mRegistrationEndOfDxe;
50 VOID *mRegistrationReadyToLock;
51
52 BOOLEAN mSmmReadyToLock = FALSE;
53
54 /**
55 Calculate and save the maximum support address.
56
57 **/
58 VOID
59 SmmMemLibInternalCalculateMaximumSupportAddress (
60 VOID
61 )
62 {
63 VOID *Hob;
64 UINT32 RegEax;
65 UINT8 PhysicalAddressBits;
66
67 //
68 // Get physical address bits supported.
69 //
70 Hob = GetFirstHob (EFI_HOB_TYPE_CPU);
71 if (Hob != NULL) {
72 PhysicalAddressBits = ((EFI_HOB_CPU *) Hob)->SizeOfMemorySpace;
73 } else {
74 AsmCpuid (0x80000000, &RegEax, NULL, NULL, NULL);
75 if (RegEax >= 0x80000008) {
76 AsmCpuid (0x80000008, &RegEax, NULL, NULL, NULL);
77 PhysicalAddressBits = (UINT8) RegEax;
78 } else {
79 PhysicalAddressBits = 36;
80 }
81 }
82 //
83 // IA-32e paging translates 48-bit linear addresses to 52-bit physical addresses.
84 //
85 ASSERT (PhysicalAddressBits <= 52);
86 if (PhysicalAddressBits > 48) {
87 PhysicalAddressBits = 48;
88 }
89
90 //
91 // Save the maximum support address in one global variable
92 //
93 mSmmMemLibInternalMaximumSupportAddress = (EFI_PHYSICAL_ADDRESS)(UINTN)(LShiftU64 (1, PhysicalAddressBits) - 1);
94 DEBUG ((EFI_D_INFO, "mSmmMemLibInternalMaximumSupportAddress = 0x%lx\n", mSmmMemLibInternalMaximumSupportAddress));
95 }
96
97 /**
98 This function check if the buffer is valid per processor architecture and not overlap with SMRAM.
99
100 @param Buffer The buffer start address to be checked.
101 @param Length The buffer length to be checked.
102
103 @retval TRUE This buffer is valid per processor architecture and not overlap with SMRAM.
104 @retval FALSE This buffer is not valid per processor architecture or overlap with SMRAM.
105 **/
106 BOOLEAN
107 EFIAPI
108 SmmIsBufferOutsideSmmValid (
109 IN EFI_PHYSICAL_ADDRESS Buffer,
110 IN UINT64 Length
111 )
112 {
113 UINTN Index;
114
115 //
116 // Check override.
117 // NOTE: (B:0->L:4G) is invalid for IA32, but (B:1->L:4G-1)/(B:4G-1->L:1) is valid.
118 //
119 if ((Length > mSmmMemLibInternalMaximumSupportAddress) ||
120 (Buffer > mSmmMemLibInternalMaximumSupportAddress) ||
121 ((Length != 0) && (Buffer > (mSmmMemLibInternalMaximumSupportAddress - (Length - 1)))) ) {
122 //
123 // Overflow happen
124 //
125 DEBUG ((
126 EFI_D_ERROR,
127 "SmmIsBufferOutsideSmmValid: Overflow: Buffer (0x%lx) - Length (0x%lx), MaximumSupportAddress (0x%lx)\n",
128 Buffer,
129 Length,
130 mSmmMemLibInternalMaximumSupportAddress
131 ));
132 return FALSE;
133 }
134
135 for (Index = 0; Index < mSmmMemLibInternalSmramCount; Index ++) {
136 if (((Buffer >= mSmmMemLibInternalSmramRanges[Index].CpuStart) && (Buffer < mSmmMemLibInternalSmramRanges[Index].CpuStart + mSmmMemLibInternalSmramRanges[Index].PhysicalSize)) ||
137 ((mSmmMemLibInternalSmramRanges[Index].CpuStart >= Buffer) && (mSmmMemLibInternalSmramRanges[Index].CpuStart < Buffer + Length))) {
138 DEBUG ((
139 EFI_D_ERROR,
140 "SmmIsBufferOutsideSmmValid: Overlap: Buffer (0x%lx) - Length (0x%lx), ",
141 Buffer,
142 Length
143 ));
144 DEBUG ((
145 EFI_D_ERROR,
146 "CpuStart (0x%lx) - PhysicalSize (0x%lx)\n",
147 mSmmMemLibInternalSmramRanges[Index].CpuStart,
148 mSmmMemLibInternalSmramRanges[Index].PhysicalSize
149 ));
150 return FALSE;
151 }
152 }
153
154 //
155 // Check override for Valid Communication Region
156 //
157 if (mSmmReadyToLock) {
158 EFI_MEMORY_DESCRIPTOR *MemoryMap;
159 BOOLEAN InValidCommunicationRegion;
160
161 InValidCommunicationRegion = FALSE;
162 MemoryMap = mMemoryMap;
163 for (Index = 0; Index < mMemoryMapEntryCount; Index++) {
164 if ((Buffer >= MemoryMap->PhysicalStart) &&
165 (Buffer + Length <= MemoryMap->PhysicalStart + LShiftU64 (MemoryMap->NumberOfPages, EFI_PAGE_SHIFT))) {
166 InValidCommunicationRegion = TRUE;
167 }
168 MemoryMap = NEXT_MEMORY_DESCRIPTOR(MemoryMap, mDescriptorSize);
169 }
170
171 if (!InValidCommunicationRegion) {
172 DEBUG ((
173 EFI_D_ERROR,
174 "SmmIsBufferOutsideSmmValid: Not in ValidCommunicationRegion: Buffer (0x%lx) - Length (0x%lx), ",
175 Buffer,
176 Length
177 ));
178 return FALSE;
179 }
180 }
181 return TRUE;
182 }
183
184 /**
185 Copies a source buffer (non-SMRAM) to a destination buffer (SMRAM).
186
187 This function copies a source buffer (non-SMRAM) to a destination buffer (SMRAM).
188 It checks if source buffer is valid per processor architecture and not overlap with SMRAM.
189 If the check passes, it copies memory and returns EFI_SUCCESS.
190 If the check fails, it return EFI_SECURITY_VIOLATION.
191 The implementation must be reentrant.
192
193 @param DestinationBuffer The pointer to the destination buffer of the memory copy.
194 @param SourceBuffer The pointer to the source buffer of the memory copy.
195 @param Length The number of bytes to copy from SourceBuffer to DestinationBuffer.
196
197 @retval EFI_SECURITY_VIOLATION The SourceBuffer is invalid per processor architecture or overlap with SMRAM.
198 @retval EFI_SUCCESS Memory is copied.
199
200 **/
201 EFI_STATUS
202 EFIAPI
203 SmmCopyMemToSmram (
204 OUT VOID *DestinationBuffer,
205 IN CONST VOID *SourceBuffer,
206 IN UINTN Length
207 )
208 {
209 if (!SmmIsBufferOutsideSmmValid ((EFI_PHYSICAL_ADDRESS)(UINTN)SourceBuffer, Length)) {
210 DEBUG ((EFI_D_ERROR, "SmmCopyMemToSmram: Security Violation: Source (0x%x), Length (0x%x)\n", SourceBuffer, Length));
211 return EFI_SECURITY_VIOLATION;
212 }
213 CopyMem (DestinationBuffer, SourceBuffer, Length);
214 return EFI_SUCCESS;
215 }
216
217 /**
218 Copies a source buffer (SMRAM) to a destination buffer (NON-SMRAM).
219
220 This function copies a source buffer (non-SMRAM) to a destination buffer (SMRAM).
221 It checks if destination buffer is valid per processor architecture and not overlap with SMRAM.
222 If the check passes, it copies memory and returns EFI_SUCCESS.
223 If the check fails, it returns EFI_SECURITY_VIOLATION.
224 The implementation must be reentrant.
225
226 @param DestinationBuffer The pointer to the destination buffer of the memory copy.
227 @param SourceBuffer The pointer to the source buffer of the memory copy.
228 @param Length The number of bytes to copy from SourceBuffer to DestinationBuffer.
229
230 @retval EFI_SECURITY_VIOLATION The DesinationBuffer is invalid per processor architecture or overlap with SMRAM.
231 @retval EFI_SUCCESS Memory is copied.
232
233 **/
234 EFI_STATUS
235 EFIAPI
236 SmmCopyMemFromSmram (
237 OUT VOID *DestinationBuffer,
238 IN CONST VOID *SourceBuffer,
239 IN UINTN Length
240 )
241 {
242 if (!SmmIsBufferOutsideSmmValid ((EFI_PHYSICAL_ADDRESS)(UINTN)DestinationBuffer, Length)) {
243 DEBUG ((EFI_D_ERROR, "SmmCopyMemFromSmram: Security Violation: Destination (0x%x), Length (0x%x)\n", DestinationBuffer, Length));
244 return EFI_SECURITY_VIOLATION;
245 }
246 CopyMem (DestinationBuffer, SourceBuffer, Length);
247 return EFI_SUCCESS;
248 }
249
250 /**
251 Copies a source buffer (NON-SMRAM) to a destination buffer (NON-SMRAM).
252
253 This function copies a source buffer (non-SMRAM) to a destination buffer (SMRAM).
254 It checks if source buffer and destination buffer are valid per processor architecture and not overlap with SMRAM.
255 If the check passes, it copies memory and returns EFI_SUCCESS.
256 If the check fails, it returns EFI_SECURITY_VIOLATION.
257 The implementation must be reentrant, and it must handle the case where source buffer overlaps destination buffer.
258
259 @param DestinationBuffer The pointer to the destination buffer of the memory copy.
260 @param SourceBuffer The pointer to the source buffer of the memory copy.
261 @param Length The number of bytes to copy from SourceBuffer to DestinationBuffer.
262
263 @retval EFI_SECURITY_VIOLATION The DesinationBuffer is invalid per processor architecture or overlap with SMRAM.
264 @retval EFI_SECURITY_VIOLATION The SourceBuffer is invalid per processor architecture or overlap with SMRAM.
265 @retval EFI_SUCCESS Memory is copied.
266
267 **/
268 EFI_STATUS
269 EFIAPI
270 SmmCopyMem (
271 OUT VOID *DestinationBuffer,
272 IN CONST VOID *SourceBuffer,
273 IN UINTN Length
274 )
275 {
276 if (!SmmIsBufferOutsideSmmValid ((EFI_PHYSICAL_ADDRESS)(UINTN)DestinationBuffer, Length)) {
277 DEBUG ((EFI_D_ERROR, "SmmCopyMem: Security Violation: Destination (0x%x), Length (0x%x)\n", DestinationBuffer, Length));
278 return EFI_SECURITY_VIOLATION;
279 }
280 if (!SmmIsBufferOutsideSmmValid ((EFI_PHYSICAL_ADDRESS)(UINTN)SourceBuffer, Length)) {
281 DEBUG ((EFI_D_ERROR, "SmmCopyMem: Security Violation: Source (0x%x), Length (0x%x)\n", SourceBuffer, Length));
282 return EFI_SECURITY_VIOLATION;
283 }
284 CopyMem (DestinationBuffer, SourceBuffer, Length);
285 return EFI_SUCCESS;
286 }
287
288 /**
289 Fills a target buffer (NON-SMRAM) with a byte value.
290
291 This function fills a target buffer (non-SMRAM) with a byte value.
292 It checks if target buffer is valid per processor architecture and not overlap with SMRAM.
293 If the check passes, it fills memory and returns EFI_SUCCESS.
294 If the check fails, it returns EFI_SECURITY_VIOLATION.
295
296 @param Buffer The memory to set.
297 @param Length The number of bytes to set.
298 @param Value The value with which to fill Length bytes of Buffer.
299
300 @retval EFI_SECURITY_VIOLATION The Buffer is invalid per processor architecture or overlap with SMRAM.
301 @retval EFI_SUCCESS Memory is set.
302
303 **/
304 EFI_STATUS
305 EFIAPI
306 SmmSetMem (
307 OUT VOID *Buffer,
308 IN UINTN Length,
309 IN UINT8 Value
310 )
311 {
312 if (!SmmIsBufferOutsideSmmValid ((EFI_PHYSICAL_ADDRESS)(UINTN)Buffer, Length)) {
313 DEBUG ((EFI_D_ERROR, "SmmSetMem: Security Violation: Source (0x%x), Length (0x%x)\n", Buffer, Length));
314 return EFI_SECURITY_VIOLATION;
315 }
316 SetMem (Buffer, Length, Value);
317 return EFI_SUCCESS;
318 }
319
320 /**
321 Notification for SMM EndOfDxe protocol.
322
323 @param[in] Protocol Points to the protocol's unique identifier.
324 @param[in] Interface Points to the interface instance.
325 @param[in] Handle The handle on which the interface was installed.
326
327 @retval EFI_SUCCESS Notification runs successfully.
328 **/
329 EFI_STATUS
330 EFIAPI
331 SmmLibInternalEndOfDxeNotify (
332 IN CONST EFI_GUID *Protocol,
333 IN VOID *Interface,
334 IN EFI_HANDLE Handle
335 )
336 {
337 EFI_STATUS Status;
338 UINTN MapKey;
339 UINTN MemoryMapSize;
340 EFI_MEMORY_DESCRIPTOR *MemoryMap;
341 EFI_MEMORY_DESCRIPTOR *MemoryMapStart;
342 EFI_MEMORY_DESCRIPTOR *SmmMemoryMapStart;
343 UINTN MemoryMapEntryCount;
344 UINTN DescriptorSize;
345 UINT32 DescriptorVersion;
346 UINTN Index;
347
348 MemoryMapSize = 0;
349 MemoryMap = NULL;
350 Status = gBS->GetMemoryMap (
351 &MemoryMapSize,
352 MemoryMap,
353 &MapKey,
354 &DescriptorSize,
355 &DescriptorVersion
356 );
357 ASSERT (Status == EFI_BUFFER_TOO_SMALL);
358
359 do {
360 Status = gBS->AllocatePool (EfiBootServicesData, MemoryMapSize, (VOID **)&MemoryMap);
361 ASSERT (MemoryMap != NULL);
362
363 Status = gBS->GetMemoryMap (
364 &MemoryMapSize,
365 MemoryMap,
366 &MapKey,
367 &DescriptorSize,
368 &DescriptorVersion
369 );
370 if (EFI_ERROR (Status)) {
371 gBS->FreePool (MemoryMap);
372 }
373 } while (Status == EFI_BUFFER_TOO_SMALL);
374
375 //
376 // Get Count
377 //
378 mDescriptorSize = DescriptorSize;
379 MemoryMapEntryCount = MemoryMapSize/DescriptorSize;
380 MemoryMapStart = MemoryMap;
381 mMemoryMapEntryCount = 0;
382 for (Index = 0; Index < MemoryMapEntryCount; Index++) {
383 switch (MemoryMap->Type) {
384 case EfiReservedMemoryType:
385 case EfiRuntimeServicesCode:
386 case EfiRuntimeServicesData:
387 case EfiACPIMemoryNVS:
388 mMemoryMapEntryCount++;
389 break;
390 }
391 MemoryMap = NEXT_MEMORY_DESCRIPTOR(MemoryMap, DescriptorSize);
392 }
393 MemoryMap = MemoryMapStart;
394
395 //
396 // Get Data
397 //
398 mMemoryMap = AllocatePool (mMemoryMapEntryCount*DescriptorSize);
399 ASSERT (mMemoryMap != NULL);
400 SmmMemoryMapStart = mMemoryMap;
401 for (Index = 0; Index < MemoryMapEntryCount; Index++) {
402 switch (MemoryMap->Type) {
403 case EfiReservedMemoryType:
404 case EfiRuntimeServicesCode:
405 case EfiRuntimeServicesData:
406 case EfiACPIMemoryNVS:
407 CopyMem (mMemoryMap, MemoryMap, DescriptorSize);
408 mMemoryMap = NEXT_MEMORY_DESCRIPTOR(mMemoryMap, DescriptorSize);
409 break;
410 }
411 MemoryMap = NEXT_MEMORY_DESCRIPTOR(MemoryMap, DescriptorSize);
412 }
413 mMemoryMap = SmmMemoryMapStart;
414 MemoryMap = MemoryMapStart;
415
416 gBS->FreePool (MemoryMap);
417
418 return EFI_SUCCESS;
419 }
420
421
422 /**
423 Notification for SMM ReadyToLock protocol.
424
425 @param[in] Protocol Points to the protocol's unique identifier.
426 @param[in] Interface Points to the interface instance.
427 @param[in] Handle The handle on which the interface was installed.
428
429 @retval EFI_SUCCESS Notification runs successfully.
430 **/
431 EFI_STATUS
432 EFIAPI
433 SmmLibInternalReadyToLockNotify (
434 IN CONST EFI_GUID *Protocol,
435 IN VOID *Interface,
436 IN EFI_HANDLE Handle
437 )
438 {
439 mSmmReadyToLock = TRUE;
440 return EFI_SUCCESS;
441 }
442 /**
443 The constructor function initializes the Smm Mem library
444
445 @param ImageHandle The firmware allocated handle for the EFI image.
446 @param SystemTable A pointer to the EFI System Table.
447
448 @retval EFI_SUCCESS The constructor always returns EFI_SUCCESS.
449
450 **/
451 EFI_STATUS
452 EFIAPI
453 SmmMemLibConstructor (
454 IN EFI_HANDLE ImageHandle,
455 IN EFI_SYSTEM_TABLE *SystemTable
456 )
457 {
458 EFI_STATUS Status;
459 EFI_SMM_ACCESS2_PROTOCOL *SmmAccess;
460 UINTN Size;
461
462 //
463 // Get SMRAM information
464 //
465 Status = gBS->LocateProtocol (&gEfiSmmAccess2ProtocolGuid, NULL, (VOID **)&SmmAccess);
466 ASSERT_EFI_ERROR (Status);
467
468 Size = 0;
469 Status = SmmAccess->GetCapabilities (SmmAccess, &Size, NULL);
470 ASSERT (Status == EFI_BUFFER_TOO_SMALL);
471
472 mSmmMemLibInternalSmramRanges = AllocatePool (Size);
473 ASSERT (mSmmMemLibInternalSmramRanges != NULL);
474
475 Status = SmmAccess->GetCapabilities (SmmAccess, &Size, mSmmMemLibInternalSmramRanges);
476 ASSERT_EFI_ERROR (Status);
477
478 mSmmMemLibInternalSmramCount = Size / sizeof (EFI_SMRAM_DESCRIPTOR);
479
480 //
481 // Calculate and save maximum support address
482 //
483 SmmMemLibInternalCalculateMaximumSupportAddress ();
484
485 //
486 // Register EndOfDxe to get UEFI memory map
487 //
488 Status = gSmst->SmmRegisterProtocolNotify (&gEfiSmmEndOfDxeProtocolGuid, SmmLibInternalEndOfDxeNotify, &mRegistrationEndOfDxe);
489 ASSERT_EFI_ERROR (Status);
490
491 //
492 // Register ready to lock so that we can know when to check valid SMRAM region
493 //
494 Status = gSmst->SmmRegisterProtocolNotify (&gEfiSmmReadyToLockProtocolGuid, SmmLibInternalReadyToLockNotify, &mRegistrationReadyToLock);
495 ASSERT_EFI_ERROR (Status);
496
497 return EFI_SUCCESS;
498 }
499
500 /**
501 The destructor function frees resource used in the Smm Mem library
502
503 @param[in] ImageHandle The firmware allocated handle for the EFI image.
504 @param[in] SystemTable A pointer to the EFI System Table.
505
506 @retval EFI_SUCCESS The deconstructor always returns EFI_SUCCESS.
507 **/
508 EFI_STATUS
509 EFIAPI
510 SmmMemLibDestructor (
511 IN EFI_HANDLE ImageHandle,
512 IN EFI_SYSTEM_TABLE *SystemTable
513 )
514 {
515 FreePool (mSmmMemLibInternalSmramRanges);
516
517 gSmst->SmmRegisterProtocolNotify (&gEfiSmmEndOfDxeProtocolGuid, NULL, &mRegistrationEndOfDxe);
518 gSmst->SmmRegisterProtocolNotify (&gEfiSmmReadyToLockProtocolGuid, NULL, &mRegistrationReadyToLock);
519 return EFI_SUCCESS;
520 }