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