1 /** @file
2 
3   Copyright (c) 2006 - 2018, Intel Corporation. All rights reserved.<BR>
4 
5   SPDX-License-Identifier: BSD-2-Clause-Patent
6 
7 **/
8 
9 #include "LightMemoryTest.h"
10 
11 //
12 // Global:
13 // Since this driver will only ever produce one instance of the memory test
14 // protocol, so we do not need to dynamically allocate the PrivateData.
15 //
16 EFI_PHYSICAL_ADDRESS    mCurrentAddress;
17 LIST_ENTRY          *mCurrentLink;
18 NONTESTED_MEMORY_RANGE  *mCurrentRange;
19 UINT64                  mTestedSystemMemory;
20 UINT64                  mNonTestedSystemMemory;
21 
22 UINT32                  GenericMemoryTestMonoPattern[GENERIC_CACHELINE_SIZE / 4] = {
23   0x5a5a5a5a,
24   0xa5a5a5a5,
25   0x5a5a5a5a,
26   0xa5a5a5a5,
27   0x5a5a5a5a,
28   0xa5a5a5a5,
29   0x5a5a5a5a,
30   0xa5a5a5a5,
31   0x5a5a5a5a,
32   0xa5a5a5a5,
33   0x5a5a5a5a,
34   0xa5a5a5a5,
35   0x5a5a5a5a,
36   0xa5a5a5a5,
37   0x5a5a5a5a,
38   0xa5a5a5a5
39 };
40 
41 /**
42   Compares the contents of two buffers.
43 
44   This function compares Length bytes of SourceBuffer to Length bytes of DestinationBuffer.
45   If all Length bytes of the two buffers are identical, then 0 is returned.  Otherwise, the
46   value returned is the first mismatched byte in SourceBuffer subtracted from the first
47   mismatched byte in DestinationBuffer.
48 
49   If Length = 0, then ASSERT().
50 
51   @param[in] DestinationBuffer The pointer to the destination buffer to compare.
52   @param[in] SourceBuffer      The pointer to the source buffer to compare.
53   @param[in] Length            The number of bytes to compare.
54 
55   @return 0                 All Length bytes of the two buffers are identical.
56   @retval Non-zero          The first mismatched byte in SourceBuffer subtracted from the first
57                             mismatched byte in DestinationBuffer.
58 
59 **/
60 INTN
61 EFIAPI
CompareMemWithoutCheckArgument(IN CONST VOID * DestinationBuffer,IN CONST VOID * SourceBuffer,IN UINTN Length)62 CompareMemWithoutCheckArgument (
63   IN      CONST VOID                *DestinationBuffer,
64   IN      CONST VOID                *SourceBuffer,
65   IN      UINTN                     Length
66   )
67 {
68   ASSERT (Length > 0);
69   while ((--Length != 0) &&
70          (*(INT8*)DestinationBuffer == *(INT8*)SourceBuffer)) {
71     DestinationBuffer = (INT8*)DestinationBuffer + 1;
72     SourceBuffer = (INT8*)SourceBuffer + 1;
73   }
74   return (INTN)*(UINT8*)DestinationBuffer - (INTN)*(UINT8*)SourceBuffer;
75 }
76 
77 /**
78   Construct the system base memory range through GCD service.
79 
80   @param[in] Private  Point to generic memory test driver's private data.
81 
82   @retval EFI_SUCCESS          Successful construct the base memory range through GCD service.
83   @retval EFI_OUT_OF_RESOURCE  Could not allocate needed resource from base memory.
84   @retval Others               Failed to construct base memory range through GCD service.
85 
86 **/
87 EFI_STATUS
ConstructBaseMemoryRange(IN GENERIC_MEMORY_TEST_PRIVATE * Private)88 ConstructBaseMemoryRange (
89   IN  GENERIC_MEMORY_TEST_PRIVATE  *Private
90   )
91 {
92   UINTN                           NumberOfDescriptors;
93   EFI_GCD_MEMORY_SPACE_DESCRIPTOR *MemorySpaceMap;
94   UINTN                           Index;
95 
96   //
97   // Base memory will always below 4G
98   //
99   gDS->GetMemorySpaceMap (&NumberOfDescriptors, &MemorySpaceMap);
100 
101   for (Index = 0; Index < NumberOfDescriptors; Index++) {
102     if ((MemorySpaceMap[Index].GcdMemoryType == EfiGcdMemoryTypeSystemMemory) ||
103         (MemorySpaceMap[Index].GcdMemoryType == EfiGcdMemoryTypeMoreReliable)) {
104       Private->BaseMemorySize += MemorySpaceMap[Index].Length;
105     }
106   }
107 
108   return EFI_SUCCESS;
109 }
110 
111 /**
112   Destroy the link list base on the correspond link list type.
113 
114   @param[in] Private  Point to generic memory test driver's private data.
115 
116 **/
117 VOID
DestroyLinkList(IN GENERIC_MEMORY_TEST_PRIVATE * Private)118 DestroyLinkList (
119   IN  GENERIC_MEMORY_TEST_PRIVATE  *Private
120   )
121 {
122   LIST_ENTRY          *Link;
123   NONTESTED_MEMORY_RANGE  *NontestedRange;
124 
125   Link = Private->NonTestedMemRanList.BackLink;
126 
127   while (Link != &Private->NonTestedMemRanList) {
128     RemoveEntryList (Link);
129     NontestedRange = NONTESTED_MEMORY_RANGE_FROM_LINK (Link);
130     gBS->FreePool (NontestedRange);
131     Link = Private->NonTestedMemRanList.BackLink;;
132   }
133 }
134 
135 /**
136   Convert the memory range to tested.
137 
138   @param BaseAddress  Base address of the memory range.
139   @param Length       Length of the memory range.
140   @param Capabilities Capabilities of the memory range.
141 
142   @retval EFI_SUCCESS The memory range is converted to tested.
143   @retval others      Error happens.
144 **/
145 EFI_STATUS
ConvertToTestedMemory(IN UINT64 BaseAddress,IN UINT64 Length,IN UINT64 Capabilities)146 ConvertToTestedMemory (
147   IN UINT64           BaseAddress,
148   IN UINT64           Length,
149   IN UINT64           Capabilities
150   )
151 {
152   EFI_STATUS Status;
153   Status = gDS->RemoveMemorySpace (
154                   BaseAddress,
155                   Length
156                   );
157   if (!EFI_ERROR (Status)) {
158     Status = gDS->AddMemorySpace (
159                     ((Capabilities & EFI_MEMORY_MORE_RELIABLE) == EFI_MEMORY_MORE_RELIABLE) ?
160                     EfiGcdMemoryTypeMoreReliable : EfiGcdMemoryTypeSystemMemory,
161                     BaseAddress,
162                     Length,
163                     Capabilities &~
164                     (EFI_MEMORY_PRESENT | EFI_MEMORY_INITIALIZED | EFI_MEMORY_TESTED | EFI_MEMORY_RUNTIME)
165                     );
166   }
167   return Status;
168 }
169 
170 /**
171   Add the extened memory to whole system memory map.
172 
173   @param[in] Private  Point to generic memory test driver's private data.
174 
175   @retval EFI_SUCCESS Successful add all the extended memory to system memory map.
176   @retval Others      Failed to add the tested extended memory.
177 
178 **/
179 EFI_STATUS
UpdateMemoryMap(IN GENERIC_MEMORY_TEST_PRIVATE * Private)180 UpdateMemoryMap (
181   IN  GENERIC_MEMORY_TEST_PRIVATE  *Private
182   )
183 {
184   LIST_ENTRY          *Link;
185   NONTESTED_MEMORY_RANGE  *Range;
186 
187   Link = Private->NonTestedMemRanList.ForwardLink;
188 
189   while (Link != &Private->NonTestedMemRanList) {
190     Range = NONTESTED_MEMORY_RANGE_FROM_LINK (Link);
191 
192     ConvertToTestedMemory (
193       Range->StartAddress,
194       Range->Length,
195       Range->Capabilities &~
196       (EFI_MEMORY_PRESENT | EFI_MEMORY_INITIALIZED | EFI_MEMORY_TESTED | EFI_MEMORY_RUNTIME)
197       );
198     Link = Link->ForwardLink;
199   }
200 
201   return EFI_SUCCESS;
202 }
203 
204 /**
205   Test a range of the memory directly .
206 
207   @param[in] Private       Point to generic memory test driver's private data.
208   @param[in] StartAddress  Starting address of the memory range to be tested.
209   @param[in] Length        Length in bytes of the memory range to be tested.
210   @param[in] Capabilities  The bit mask of attributes that the memory range supports.
211 
212   @retval EFI_SUCCESS      Successful test the range of memory.
213   @retval Others           Failed to test the range of memory.
214 
215 **/
216 EFI_STATUS
DirectRangeTest(IN GENERIC_MEMORY_TEST_PRIVATE * Private,IN EFI_PHYSICAL_ADDRESS StartAddress,IN UINT64 Length,IN UINT64 Capabilities)217 DirectRangeTest (
218   IN  GENERIC_MEMORY_TEST_PRIVATE  *Private,
219   IN  EFI_PHYSICAL_ADDRESS         StartAddress,
220   IN  UINT64                       Length,
221   IN  UINT64                       Capabilities
222   )
223 {
224   EFI_STATUS  Status;
225 
226   //
227   // Perform a dummy memory test, so directly write the pattern to all range
228   //
229   WriteMemory (Private, StartAddress, Length);
230 
231   //
232   // Verify the memory range
233   //
234   Status = VerifyMemory (Private, StartAddress, Length);
235   if (EFI_ERROR (Status)) {
236     return Status;
237   }
238   //
239   // Add the tested compatible memory to system memory using GCD service
240   //
241   ConvertToTestedMemory (
242       StartAddress,
243       Length,
244       Capabilities &~
245       (EFI_MEMORY_PRESENT | EFI_MEMORY_INITIALIZED | EFI_MEMORY_TESTED | EFI_MEMORY_RUNTIME)
246       );
247 
248   return EFI_SUCCESS;
249 }
250 
251 /**
252   Construct the system non-tested memory range through GCD service.
253 
254   @param[in] Private  Point to generic memory test driver's private data.
255 
256   @retval EFI_SUCCESS          Successful construct the non-tested memory range through GCD service.
257   @retval EFI_OUT_OF_RESOURCE  Could not allocate needed resource from base memory.
258   @retval Others               Failed to construct non-tested memory range through GCD service.
259 
260 **/
261 EFI_STATUS
ConstructNonTestedMemoryRange(IN GENERIC_MEMORY_TEST_PRIVATE * Private)262 ConstructNonTestedMemoryRange (
263   IN  GENERIC_MEMORY_TEST_PRIVATE  *Private
264   )
265 {
266   NONTESTED_MEMORY_RANGE          *Range;
267   BOOLEAN                         NoFound;
268   UINTN                           NumberOfDescriptors;
269   EFI_GCD_MEMORY_SPACE_DESCRIPTOR *MemorySpaceMap;
270   UINTN                           Index;
271 
272   //
273   // Non tested memory range may be span 4G here
274   //
275   NoFound = TRUE;
276 
277   gDS->GetMemorySpaceMap (&NumberOfDescriptors, &MemorySpaceMap);
278 
279   for (Index = 0; Index < NumberOfDescriptors; Index++) {
280     if (MemorySpaceMap[Index].GcdMemoryType == EfiGcdMemoryTypeReserved &&
281         (MemorySpaceMap[Index].Capabilities & (EFI_MEMORY_PRESENT | EFI_MEMORY_INITIALIZED | EFI_MEMORY_TESTED)) ==
282           (EFI_MEMORY_PRESENT | EFI_MEMORY_INITIALIZED)
283           ) {
284       NoFound = FALSE;
285       //
286       // Light version do not need to process >4G memory range
287       //
288       gBS->AllocatePool (
289             EfiBootServicesData,
290             sizeof (NONTESTED_MEMORY_RANGE),
291             (VOID **) &Range
292             );
293 
294       Range->Signature    = EFI_NONTESTED_MEMORY_RANGE_SIGNATURE;
295       Range->StartAddress = MemorySpaceMap[Index].BaseAddress;
296       Range->Length       = MemorySpaceMap[Index].Length;
297       Range->Capabilities = MemorySpaceMap[Index].Capabilities;
298 
299       mNonTestedSystemMemory += MemorySpaceMap[Index].Length;
300       InsertTailList (&Private->NonTestedMemRanList, &Range->Link);
301     }
302   }
303 
304   if (NoFound) {
305     return EFI_NOT_FOUND;
306   }
307 
308   return EFI_SUCCESS;
309 }
310 
311 /**
312   Write the memory test pattern into a range of physical memory.
313 
314   @param[in] Private  Point to generic memory test driver's private data.
315   @param[in] Start    The memory range's start address.
316   @param[in] Size     The memory range's size.
317 
318   @retval EFI_SUCCESS Successful write the test pattern into the non-tested memory.
319   @retval Others      The test pattern may not really write into the physical memory.
320 
321 **/
322 EFI_STATUS
WriteMemory(IN GENERIC_MEMORY_TEST_PRIVATE * Private,IN EFI_PHYSICAL_ADDRESS Start,IN UINT64 Size)323 WriteMemory (
324   IN  GENERIC_MEMORY_TEST_PRIVATE  *Private,
325   IN  EFI_PHYSICAL_ADDRESS         Start,
326   IN  UINT64                       Size
327   )
328 {
329   EFI_PHYSICAL_ADDRESS  Address;
330 
331   Address = Start;
332 
333   //
334   // Add 4G memory address check for IA32 platform
335   // NOTE: Without page table, there is no way to use memory above 4G.
336   //
337   if (Start + Size > MAX_ADDRESS) {
338     return EFI_SUCCESS;
339   }
340 
341   while (Address < (Start + Size)) {
342     CopyMem ((VOID *) (UINTN) Address, Private->MonoPattern, Private->MonoTestSize);
343     Address += Private->CoverageSpan;
344   }
345   //
346   // bug bug: we may need GCD service to make the code cache and data uncache,
347   // if GCD do not support it or return fail, then just flush the whole cache.
348   //
349   if (Private->Cpu != NULL) {
350     Private->Cpu->FlushDataCache (Private->Cpu, Start, Size, EfiCpuFlushTypeWriteBackInvalidate);
351   }
352 
353   return EFI_SUCCESS;
354 }
355 
356 /**
357   Verify the range of physical memory which covered by memory test pattern.
358 
359   This function will also do not return any informatin just cause system reset,
360   because the handle error encount fatal error and disable the bad DIMMs.
361 
362   @param[in] Private  Point to generic memory test driver's private data.
363   @param[in] Start    The memory range's start address.
364   @param[in] Size     The memory range's size.
365 
366   @retval EFI_SUCCESS Successful verify the range of memory, no errors' location found.
367   @retval Others      The range of memory have errors contained.
368 
369 **/
370 EFI_STATUS
VerifyMemory(IN GENERIC_MEMORY_TEST_PRIVATE * Private,IN EFI_PHYSICAL_ADDRESS Start,IN UINT64 Size)371 VerifyMemory (
372   IN  GENERIC_MEMORY_TEST_PRIVATE  *Private,
373   IN  EFI_PHYSICAL_ADDRESS         Start,
374   IN  UINT64                       Size
375   )
376 {
377   EFI_PHYSICAL_ADDRESS            Address;
378   INTN                            ErrorFound;
379   EFI_MEMORY_EXTENDED_ERROR_DATA  *ExtendedErrorData;
380 
381   Address           = Start;
382   ExtendedErrorData = NULL;
383 
384   //
385   // Add 4G memory address check for IA32 platform
386   // NOTE: Without page table, there is no way to use memory above 4G.
387   //
388   if (Start + Size > MAX_ADDRESS) {
389     return EFI_SUCCESS;
390   }
391 
392   //
393   // Use the software memory test to check whether have detected miscompare
394   // error here. If there is miscompare error here then check if generic
395   // memory test driver can disable the bad DIMM.
396   //
397   while (Address < (Start + Size)) {
398     ErrorFound = CompareMemWithoutCheckArgument (
399                   (VOID *) (UINTN) (Address),
400                   Private->MonoPattern,
401                   Private->MonoTestSize
402                   );
403     if (ErrorFound != 0) {
404       //
405       // Report uncorrectable errors
406       //
407       ExtendedErrorData = AllocateZeroPool (sizeof (EFI_MEMORY_EXTENDED_ERROR_DATA));
408       if (ExtendedErrorData == NULL) {
409         return EFI_OUT_OF_RESOURCES;
410       }
411 
412       ExtendedErrorData->DataHeader.HeaderSize  = (UINT16) sizeof (EFI_STATUS_CODE_DATA);
413       ExtendedErrorData->DataHeader.Size        = (UINT16) (sizeof (EFI_MEMORY_EXTENDED_ERROR_DATA) - sizeof (EFI_STATUS_CODE_DATA));
414       ExtendedErrorData->Granularity            = EFI_MEMORY_ERROR_DEVICE;
415       ExtendedErrorData->Operation              = EFI_MEMORY_OPERATION_READ;
416       ExtendedErrorData->Syndrome               = 0x0;
417       ExtendedErrorData->Address                = Address;
418       ExtendedErrorData->Resolution             = 0x40;
419 
420       REPORT_STATUS_CODE_EX (
421           EFI_ERROR_CODE,
422           EFI_COMPUTING_UNIT_MEMORY | EFI_CU_MEMORY_EC_UNCORRECTABLE,
423           0,
424           &gEfiGenericMemTestProtocolGuid,
425           NULL,
426           (UINT8 *) ExtendedErrorData + sizeof (EFI_STATUS_CODE_DATA),
427           ExtendedErrorData->DataHeader.Size
428           );
429 
430       return EFI_DEVICE_ERROR;
431     }
432 
433     Address += Private->CoverageSpan;
434   }
435 
436   return EFI_SUCCESS;
437 }
438 
439 /**
440   Initialize the generic memory test.
441 
442   @param[in]  This                The protocol instance pointer.
443   @param[in]  Level               The coverage level of the memory test.
444   @param[out] RequireSoftECCInit  Indicate if the memory need software ECC init.
445 
446   @retval EFI_SUCCESS         The generic memory test is initialized correctly.
447   @retval EFI_NO_MEDIA        The system had no memory to be tested.
448 
449 **/
450 EFI_STATUS
451 EFIAPI
InitializeMemoryTest(IN EFI_GENERIC_MEMORY_TEST_PROTOCOL * This,IN EXTENDMEM_COVERAGE_LEVEL Level,OUT BOOLEAN * RequireSoftECCInit)452 InitializeMemoryTest (
453   IN EFI_GENERIC_MEMORY_TEST_PROTOCOL          *This,
454   IN  EXTENDMEM_COVERAGE_LEVEL                 Level,
455   OUT BOOLEAN                                  *RequireSoftECCInit
456   )
457 {
458   EFI_STATUS                  Status;
459   GENERIC_MEMORY_TEST_PRIVATE *Private;
460   EFI_CPU_ARCH_PROTOCOL       *Cpu;
461 
462   Private             = GENERIC_MEMORY_TEST_PRIVATE_FROM_THIS (This);
463   *RequireSoftECCInit = FALSE;
464 
465   //
466   // This is initialize for default value, but some value may be reset base on
467   // platform memory test driver.
468   //
469   Private->CoverLevel   = Level;
470   Private->BdsBlockSize = TEST_BLOCK_SIZE;
471   Private->MonoPattern  = GenericMemoryTestMonoPattern;
472   Private->MonoTestSize = GENERIC_CACHELINE_SIZE;
473 
474   //
475   // Initialize several internal link list
476   //
477   InitializeListHead (&Private->NonTestedMemRanList);
478 
479   //
480   // Construct base memory range
481   //
482   ConstructBaseMemoryRange (Private);
483 
484   //
485   // get the cpu arch protocol to support flash cache
486   //
487   Status = gBS->LocateProtocol (
488                   &gEfiCpuArchProtocolGuid,
489                   NULL,
490                   (VOID **) &Cpu
491                   );
492   if (!EFI_ERROR (Status)) {
493     Private->Cpu = Cpu;
494   }
495   //
496   // Create the CoverageSpan of the memory test base on the coverage level
497   //
498   switch (Private->CoverLevel) {
499   case EXTENSIVE:
500     Private->CoverageSpan = GENERIC_CACHELINE_SIZE;
501     break;
502 
503   case SPARSE:
504     Private->CoverageSpan = SPARSE_SPAN_SIZE;
505     break;
506 
507   //
508   // Even the BDS do not need to test any memory, but in some case it
509   // still need to init ECC memory.
510   //
511   default:
512     Private->CoverageSpan = QUICK_SPAN_SIZE;
513     break;
514   }
515   //
516   // This is the first time we construct the non-tested memory range, if no
517   // extended memory found, we know the system have not any extended memory
518   // need to be test
519   //
520   Status = ConstructNonTestedMemoryRange (Private);
521   if (Status == EFI_NOT_FOUND) {
522     return EFI_NO_MEDIA;
523   }
524   //
525   // ready to perform the R/W/V memory test
526   //
527   mTestedSystemMemory = Private->BaseMemorySize;
528   mCurrentLink        = Private->NonTestedMemRanList.ForwardLink;
529   mCurrentRange       = NONTESTED_MEMORY_RANGE_FROM_LINK (mCurrentLink);
530   mCurrentAddress     = mCurrentRange->StartAddress;
531 
532   return EFI_SUCCESS;
533 }
534 
535 /**
536   Perform the memory test.
537 
538   @param[in]  This              The protocol instance pointer.
539   @param[out] TestedMemorySize  Return the tested extended memory size.
540   @param[out] TotalMemorySize   Return the whole system physical memory size.
541                                 The total memory size does not include memory in a slot with a disabled DIMM.
542   @param[out] ErrorOut          TRUE if the memory error occured.
543   @param[in]  IfTestAbort       Indicates that the user pressed "ESC" to skip the memory test.
544 
545   @retval EFI_SUCCESS         One block of memory passed the test.
546   @retval EFI_NOT_FOUND       All memory blocks have already been tested.
547   @retval EFI_DEVICE_ERROR    Memory device error occured, and no agent can handle it.
548 
549 **/
550 EFI_STATUS
551 EFIAPI
GenPerformMemoryTest(IN EFI_GENERIC_MEMORY_TEST_PROTOCOL * This,OUT UINT64 * TestedMemorySize,OUT UINT64 * TotalMemorySize,OUT BOOLEAN * ErrorOut,IN BOOLEAN TestAbort)552 GenPerformMemoryTest (
553   IN EFI_GENERIC_MEMORY_TEST_PROTOCOL          *This,
554   OUT UINT64                                   *TestedMemorySize,
555   OUT UINT64                                   *TotalMemorySize,
556   OUT BOOLEAN                                  *ErrorOut,
557   IN BOOLEAN                                   TestAbort
558   )
559 {
560   EFI_STATUS                      Status;
561   GENERIC_MEMORY_TEST_PRIVATE     *Private;
562   EFI_MEMORY_RANGE_EXTENDED_DATA  *RangeData;
563   UINT64                          BlockBoundary;
564 
565   Private       = GENERIC_MEMORY_TEST_PRIVATE_FROM_THIS (This);
566   *ErrorOut     = FALSE;
567   RangeData     = NULL;
568   BlockBoundary = 0;
569 
570   //
571   // In extensive mode the boundary of "mCurrentRange->Length" may will lost
572   // some range that is not Private->BdsBlockSize size boundary, so need
573   // the software mechanism to confirm all memory location be covered.
574   //
575   if (mCurrentAddress < (mCurrentRange->StartAddress + mCurrentRange->Length)) {
576     if ((mCurrentAddress + Private->BdsBlockSize) <= (mCurrentRange->StartAddress + mCurrentRange->Length)) {
577       BlockBoundary = Private->BdsBlockSize;
578     } else {
579       BlockBoundary = mCurrentRange->StartAddress + mCurrentRange->Length - mCurrentAddress;
580     }
581     //
582     // If TestAbort is true, means user cancel the memory test
583     //
584     if (!TestAbort && Private->CoverLevel != IGNORE) {
585       //
586       // Report status code of every memory range
587       //
588       RangeData                         = AllocateZeroPool (sizeof (EFI_MEMORY_RANGE_EXTENDED_DATA));
589       if (RangeData == NULL) {
590         return EFI_OUT_OF_RESOURCES;
591       }
592       RangeData->DataHeader.HeaderSize  = (UINT16) sizeof (EFI_STATUS_CODE_DATA);
593       RangeData->DataHeader.Size        = (UINT16) (sizeof (EFI_MEMORY_RANGE_EXTENDED_DATA) - sizeof (EFI_STATUS_CODE_DATA));
594       RangeData->Start                  = mCurrentAddress;
595       RangeData->Length                 = BlockBoundary;
596 
597       REPORT_STATUS_CODE_EX (
598           EFI_PROGRESS_CODE,
599           EFI_COMPUTING_UNIT_MEMORY | EFI_CU_MEMORY_PC_TEST,
600           0,
601           &gEfiGenericMemTestProtocolGuid,
602           NULL,
603           (UINT8 *) RangeData + sizeof (EFI_STATUS_CODE_DATA),
604           RangeData->DataHeader.Size
605           );
606 
607       //
608       // The software memory test (R/W/V) perform here. It will detect the
609       // memory mis-compare error.
610       //
611       WriteMemory (Private, mCurrentAddress, BlockBoundary);
612 
613       Status = VerifyMemory (Private, mCurrentAddress, BlockBoundary);
614       if (EFI_ERROR (Status)) {
615         //
616         // If perform here, means there is mis-compare error, and no agent can
617         // handle it, so we return to BDS EFI_DEVICE_ERROR.
618         //
619         *ErrorOut = TRUE;
620         return EFI_DEVICE_ERROR;
621       }
622     }
623 
624     mTestedSystemMemory += BlockBoundary;
625     *TestedMemorySize = mTestedSystemMemory;
626 
627     //
628     // If the memory test restart after the platform driver disable dimms,
629     // the NonTestSystemMemory may be changed, but the base memory size will
630     // not changed, so we can get the current total memory size.
631     //
632     *TotalMemorySize = Private->BaseMemorySize + mNonTestedSystemMemory;
633 
634     //
635     // Update the current test address pointing to next BDS BLOCK
636     //
637     mCurrentAddress += Private->BdsBlockSize;
638 
639     return EFI_SUCCESS;
640   }
641   //
642   // Change to next non tested memory range
643   //
644   mCurrentLink = mCurrentLink->ForwardLink;
645   if (mCurrentLink != &Private->NonTestedMemRanList) {
646     mCurrentRange   = NONTESTED_MEMORY_RANGE_FROM_LINK (mCurrentLink);
647     mCurrentAddress = mCurrentRange->StartAddress;
648     return EFI_SUCCESS;
649   } else {
650     //
651     // Here means all the memory test have finished
652     //
653     *TestedMemorySize = mTestedSystemMemory;
654     *TotalMemorySize  = Private->BaseMemorySize + mNonTestedSystemMemory;
655     return EFI_NOT_FOUND;
656   }
657 
658 }
659 
660 /**
661   Finish the memory test.
662 
663   @param[in] This             The protocol instance pointer.
664 
665   @retval EFI_SUCCESS         Success. All resources used in the memory test are freed.
666 
667 **/
668 EFI_STATUS
669 EFIAPI
GenMemoryTestFinished(IN EFI_GENERIC_MEMORY_TEST_PROTOCOL * This)670 GenMemoryTestFinished (
671   IN EFI_GENERIC_MEMORY_TEST_PROTOCOL *This
672   )
673 {
674   EFI_STATUS                  Status;
675   GENERIC_MEMORY_TEST_PRIVATE *Private;
676 
677   Private = GENERIC_MEMORY_TEST_PRIVATE_FROM_THIS (This);
678 
679   //
680   // Perform Data and Address line test
681   //
682   Status = PerformAddressDataLineTest (Private);
683   ASSERT_EFI_ERROR (Status);
684 
685   //
686   // Add the non tested memory range to system memory map through GCD service
687   //
688   UpdateMemoryMap (Private);
689 
690   //
691   // we need to free all the memory allocate
692   //
693   DestroyLinkList (Private);
694 
695   return EFI_SUCCESS;
696 }
697 
698 /**
699   Provides the capability to test the compatible range used by some special drivers.
700 
701   @param[in]  This              The protocol instance pointer.
702   @param[in]  StartAddress      The start address of the compatible memory range that
703                                 must be below 16M.
704   @param[in]  Length            The compatible memory range's length.
705 
706   @retval EFI_SUCCESS           The compatible memory range pass the memory test.
707   @retval EFI_INVALID_PARAMETER The compatible memory range are not below Low 16M.
708 
709 **/
710 EFI_STATUS
711 EFIAPI
GenCompatibleRangeTest(IN EFI_GENERIC_MEMORY_TEST_PROTOCOL * This,IN EFI_PHYSICAL_ADDRESS StartAddress,IN UINT64 Length)712 GenCompatibleRangeTest (
713   IN EFI_GENERIC_MEMORY_TEST_PROTOCOL         *This,
714   IN EFI_PHYSICAL_ADDRESS                     StartAddress,
715   IN UINT64                                   Length
716   )
717 {
718   EFI_STATUS                      Status;
719   GENERIC_MEMORY_TEST_PRIVATE     *Private;
720   EFI_GCD_MEMORY_SPACE_DESCRIPTOR Descriptor;
721   EFI_PHYSICAL_ADDRESS            CurrentBase;
722   UINT64                          CurrentLength;
723 
724   Private = GENERIC_MEMORY_TEST_PRIVATE_FROM_THIS (This);
725 
726   //
727   // Check if the parameter is below 16MB
728   //
729   if (StartAddress + Length > 0x1000000) {
730     return EFI_INVALID_PARAMETER;
731   }
732   CurrentBase = StartAddress;
733   do {
734     //
735     // Check the required memory range status; if the required memory range span
736     // the different GCD memory descriptor, it may be cause different action.
737     //
738     Status = gDS->GetMemorySpaceDescriptor (
739                     CurrentBase,
740                     &Descriptor
741                     );
742     if (EFI_ERROR (Status)) {
743       return Status;
744     }
745 
746     if (Descriptor.GcdMemoryType == EfiGcdMemoryTypeReserved &&
747         (Descriptor.Capabilities & (EFI_MEMORY_PRESENT | EFI_MEMORY_INITIALIZED | EFI_MEMORY_TESTED)) ==
748           (EFI_MEMORY_PRESENT | EFI_MEMORY_INITIALIZED)
749           ) {
750       CurrentLength = Descriptor.BaseAddress + Descriptor.Length - CurrentBase;
751       if (CurrentBase + CurrentLength > StartAddress + Length) {
752         CurrentLength = StartAddress + Length - CurrentBase;
753       }
754       Status = DirectRangeTest (
755                  Private,
756                  CurrentBase,
757                  CurrentLength,
758                  Descriptor.Capabilities
759                  );
760       if (EFI_ERROR (Status)) {
761         return Status;
762       }
763     }
764     CurrentBase = Descriptor.BaseAddress + Descriptor.Length;
765   } while (CurrentBase < StartAddress + Length);
766   //
767   // Here means the required range already be tested, so just return success.
768   //
769   return EFI_SUCCESS;
770 }
771 
772 /**
773   Perform the address line walking ones test.
774 
775   @param[in] Private  Point to generic memory test driver's private data.
776 
777   @retval EFI_SUCCESS          Successful finished walking ones test.
778   @retval EFI_OUT_OF_RESOURCE  Could not get resource in base memory.
779   @retval EFI_ACCESS_DENIED    Code may can not run here because if walking one test
780                                failed, system may be already halt.
781 
782 **/
783 EFI_STATUS
PerformAddressDataLineTest(IN GENERIC_MEMORY_TEST_PRIVATE * Private)784 PerformAddressDataLineTest (
785   IN  GENERIC_MEMORY_TEST_PRIVATE      *Private
786   )
787 {
788   LIST_ENTRY              *ExtendedLink;
789   NONTESTED_MEMORY_RANGE  *ExtendedRange;
790   BOOLEAN                 InExtendedRange;
791   EFI_PHYSICAL_ADDRESS    TestAddress;
792 
793   //
794   // Light version no data line test, only perform the address line test
795   //
796   TestAddress = (EFI_PHYSICAL_ADDRESS) 0x1;
797   while (TestAddress < MAX_ADDRESS && TestAddress > 0) {
798     //
799     // only test if the address falls in the enabled range
800     //
801     InExtendedRange = FALSE;
802     ExtendedLink    = Private->NonTestedMemRanList.BackLink;
803     while (ExtendedLink != &Private->NonTestedMemRanList) {
804       ExtendedRange = NONTESTED_MEMORY_RANGE_FROM_LINK (ExtendedLink);
805       if ((TestAddress >= ExtendedRange->StartAddress) &&
806           (TestAddress < (ExtendedRange->StartAddress + ExtendedRange->Length))
807           ) {
808         InExtendedRange = TRUE;
809       }
810 
811       ExtendedLink = ExtendedLink->BackLink;
812     }
813 
814     if (InExtendedRange) {
815       *(EFI_PHYSICAL_ADDRESS *) (UINTN) TestAddress = TestAddress;
816       Private->Cpu->FlushDataCache (Private->Cpu, TestAddress, 1, EfiCpuFlushTypeWriteBackInvalidate);
817       if (*(EFI_PHYSICAL_ADDRESS *) (UINTN) TestAddress != TestAddress) {
818         return EFI_ACCESS_DENIED;
819       }
820     }
821 
822     TestAddress = LShiftU64 (TestAddress, 1);
823   }
824 
825   return EFI_SUCCESS;
826 }
827 //
828 // Driver entry here
829 //
830 GENERIC_MEMORY_TEST_PRIVATE mGenericMemoryTestPrivate = {
831   EFI_GENERIC_MEMORY_TEST_PRIVATE_SIGNATURE,
832   NULL,
833   NULL,
834   {
835     InitializeMemoryTest,
836     GenPerformMemoryTest,
837     GenMemoryTestFinished,
838     GenCompatibleRangeTest
839   },
840   (EXTENDMEM_COVERAGE_LEVEL) 0,
841   0,
842   0,
843   NULL,
844   0,
845   0,
846   {
847     NULL,
848     NULL
849   }
850 };
851 
852 /**
853   The generic memory test driver's entry point.
854 
855   It initializes private data to default value.
856 
857   @param[in] ImageHandle  The firmware allocated handle for the EFI image.
858   @param[in] SystemTable  A pointer to the EFI System Table.
859 
860   @retval EFI_SUCCESS     The entry point is executed successfully.
861   @retval EFI_NOT_FOUND   Can't find HandOff Hob in HobList.
862   @retval other           Some error occurs when executing this entry point.
863 
864 **/
865 EFI_STATUS
866 EFIAPI
GenericMemoryTestEntryPoint(IN EFI_HANDLE ImageHandle,IN EFI_SYSTEM_TABLE * SystemTable)867 GenericMemoryTestEntryPoint (
868   IN  EFI_HANDLE           ImageHandle,
869   IN  EFI_SYSTEM_TABLE     *SystemTable
870   )
871 {
872   EFI_STATUS            Status;
873   VOID                  *HobList;
874   EFI_BOOT_MODE         BootMode;
875   EFI_PEI_HOB_POINTERS  Hob;
876 
877   //
878   // Use the generic pattern to test compatible memory range
879   //
880   mGenericMemoryTestPrivate.MonoPattern   = GenericMemoryTestMonoPattern;
881   mGenericMemoryTestPrivate.MonoTestSize  = GENERIC_CACHELINE_SIZE;
882 
883   //
884   // Get the platform boot mode
885   //
886   HobList = GetHobList ();
887 
888   Hob.Raw = HobList;
889   if (Hob.Header->HobType != EFI_HOB_TYPE_HANDOFF) {
890     return EFI_NOT_FOUND;
891   }
892 
893   BootMode = Hob.HandoffInformationTable->BootMode;
894 
895   //
896   // Get the platform boot mode and create the default memory test coverage
897   // level and span size for compatible memory test using
898   //
899   switch (BootMode) {
900   case BOOT_WITH_FULL_CONFIGURATION:
901   case BOOT_WITH_DEFAULT_SETTINGS:
902     mGenericMemoryTestPrivate.CoverageSpan = SPARSE_SPAN_SIZE;
903     break;
904 
905   case BOOT_WITH_FULL_CONFIGURATION_PLUS_DIAGNOSTICS:
906     mGenericMemoryTestPrivate.CoverageSpan = GENERIC_CACHELINE_SIZE;
907     break;
908 
909   default:
910     mGenericMemoryTestPrivate.CoverageSpan = QUICK_SPAN_SIZE;
911     break;
912   }
913   //
914   // Install the protocol
915   //
916   Status = gBS->InstallProtocolInterface (
917                   &mGenericMemoryTestPrivate.Handle,
918                   &gEfiGenericMemTestProtocolGuid,
919                   EFI_NATIVE_INTERFACE,
920                   &mGenericMemoryTestPrivate.GenericMemoryTest
921                   );
922 
923   return Status;
924 }
925