1/** @file
2  Perform the platform memory test
3
4Copyright (c) 2004 - 2015, Intel Corporation. All rights reserved.<BR>
5This program and the accompanying materials
6are licensed and made available under the terms and conditions of the BSD License
7which accompanies this distribution.  The full text of the license may be found at
8http://opensource.org/licenses/bsd-license.php
9
10THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
11WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
12
13**/
14
15#include "Bds.h"
16#include "String.h"
17
18//
19// BDS Platform Functions
20//
21/**
22
23  Show progress bar with title above it. It only works in Graphics mode.
24
25
26  @param TitleForeground Foreground color for Title.
27  @param TitleBackground Background color for Title.
28  @param Title           Title above progress bar.
29  @param ProgressColor   Progress bar color.
30  @param Progress        Progress (0-100)
31  @param PreviousValue   The previous value of the progress.
32
33  @retval  EFI_STATUS       Success update the progress bar
34
35**/
36EFI_STATUS
37PlatformBdsShowProgress (
38  IN EFI_GRAPHICS_OUTPUT_BLT_PIXEL TitleForeground,
39  IN EFI_GRAPHICS_OUTPUT_BLT_PIXEL TitleBackground,
40  IN CHAR16                        *Title,
41  IN EFI_GRAPHICS_OUTPUT_BLT_PIXEL ProgressColor,
42  IN UINTN                         Progress,
43  IN UINTN                         PreviousValue
44  )
45{
46  EFI_STATUS                     Status;
47  EFI_GRAPHICS_OUTPUT_PROTOCOL   *GraphicsOutput;
48  EFI_UGA_DRAW_PROTOCOL          *UgaDraw;
49  UINT32                         SizeOfX;
50  UINT32                         SizeOfY;
51  UINT32                         ColorDepth;
52  UINT32                         RefreshRate;
53  EFI_GRAPHICS_OUTPUT_BLT_PIXEL  Color;
54  UINTN                          BlockHeight;
55  UINTN                          BlockWidth;
56  UINTN                          BlockNum;
57  UINTN                          PosX;
58  UINTN                          PosY;
59  UINTN                          Index;
60
61  if (Progress > 100) {
62    return EFI_INVALID_PARAMETER;
63  }
64
65  UgaDraw = NULL;
66  Status = gBS->HandleProtocol (
67                  gST->ConsoleOutHandle,
68                  &gEfiGraphicsOutputProtocolGuid,
69                  (VOID **) &GraphicsOutput
70                  );
71  if (EFI_ERROR (Status) && FeaturePcdGet (PcdUgaConsumeSupport)) {
72    GraphicsOutput = NULL;
73
74    Status = gBS->HandleProtocol (
75                    gST->ConsoleOutHandle,
76                    &gEfiUgaDrawProtocolGuid,
77                    (VOID **) &UgaDraw
78                    );
79  }
80  if (EFI_ERROR (Status)) {
81    return EFI_UNSUPPORTED;
82  }
83
84  SizeOfX = 0;
85  SizeOfY = 0;
86  if (GraphicsOutput != NULL) {
87    SizeOfX = GraphicsOutput->Mode->Info->HorizontalResolution;
88    SizeOfY = GraphicsOutput->Mode->Info->VerticalResolution;
89  } else if (UgaDraw != NULL) {
90    Status = UgaDraw->GetMode (
91                        UgaDraw,
92                        &SizeOfX,
93                        &SizeOfY,
94                        &ColorDepth,
95                        &RefreshRate
96                        );
97    if (EFI_ERROR (Status)) {
98      return EFI_UNSUPPORTED;
99    }
100  } else {
101    return EFI_UNSUPPORTED;
102  }
103
104  BlockWidth  = SizeOfX / 100;
105  BlockHeight = SizeOfY / 50;
106
107  BlockNum    = Progress;
108
109  PosX        = 0;
110  PosY        = SizeOfY * 48 / 50;
111
112  if (BlockNum == 0) {
113    //
114    // Clear progress area
115    //
116    SetMem (&Color, sizeof (EFI_GRAPHICS_OUTPUT_BLT_PIXEL), 0x0);
117
118    if (GraphicsOutput != NULL) {
119      Status = GraphicsOutput->Blt (
120                          GraphicsOutput,
121                          &Color,
122                          EfiBltVideoFill,
123                          0,
124                          0,
125                          0,
126                          PosY - EFI_GLYPH_HEIGHT - 1,
127                          SizeOfX,
128                          SizeOfY - (PosY - EFI_GLYPH_HEIGHT - 1),
129                          SizeOfX * sizeof (EFI_GRAPHICS_OUTPUT_BLT_PIXEL)
130                          );
131    } else if (FeaturePcdGet (PcdUgaConsumeSupport)) {
132      Status = UgaDraw->Blt (
133                          UgaDraw,
134                          (EFI_UGA_PIXEL *) &Color,
135                          EfiUgaVideoFill,
136                          0,
137                          0,
138                          0,
139                          PosY - EFI_GLYPH_HEIGHT - 1,
140                          SizeOfX,
141                          SizeOfY - (PosY - EFI_GLYPH_HEIGHT - 1),
142                          SizeOfX * sizeof (EFI_UGA_PIXEL)
143                          );
144    } else {
145      return EFI_UNSUPPORTED;
146    }
147  }
148  //
149  // Show progress by drawing blocks
150  //
151  for (Index = PreviousValue; Index < BlockNum; Index++) {
152    PosX = Index * BlockWidth;
153    if (GraphicsOutput != NULL) {
154      Status = GraphicsOutput->Blt (
155                          GraphicsOutput,
156                          &ProgressColor,
157                          EfiBltVideoFill,
158                          0,
159                          0,
160                          PosX,
161                          PosY,
162                          BlockWidth - 1,
163                          BlockHeight,
164                          (BlockWidth) * sizeof (EFI_GRAPHICS_OUTPUT_BLT_PIXEL)
165                          );
166    } else if (FeaturePcdGet (PcdUgaConsumeSupport)) {
167      Status = UgaDraw->Blt (
168                          UgaDraw,
169                          (EFI_UGA_PIXEL *) &ProgressColor,
170                          EfiUgaVideoFill,
171                          0,
172                          0,
173                          PosX,
174                          PosY,
175                          BlockWidth - 1,
176                          BlockHeight,
177                          (BlockWidth) * sizeof (EFI_UGA_PIXEL)
178                          );
179    } else {
180      return EFI_UNSUPPORTED;
181    }
182  }
183
184  PrintXY (
185    (SizeOfX - StrLen (Title) * EFI_GLYPH_WIDTH) / 2,
186    PosY - EFI_GLYPH_HEIGHT - 1,
187    &TitleForeground,
188    &TitleBackground,
189    Title
190    );
191
192  return EFI_SUCCESS;
193}
194
195/**
196  Perform the memory test base on the memory test intensive level,
197  and update the memory resource.
198
199  @param  Level         The memory test intensive level.
200
201  @retval EFI_STATUS    Success test all the system memory and update
202                        the memory resource
203
204**/
205EFI_STATUS
206EFIAPI
207BdsMemoryTest (
208  IN EXTENDMEM_COVERAGE_LEVEL Level
209  )
210{
211  EFI_STATUS                        Status;
212  EFI_STATUS                        KeyStatus;
213  EFI_STATUS                        InitStatus;
214  EFI_STATUS                        ReturnStatus;
215  BOOLEAN                           RequireSoftECCInit;
216  EFI_GENERIC_MEMORY_TEST_PROTOCOL  *GenMemoryTest;
217  UINT64                            TestedMemorySize;
218  UINT64                            TotalMemorySize;
219  UINTN                             TestPercent;
220  UINT64                            PreviousValue;
221  BOOLEAN                           ErrorOut;
222  BOOLEAN                           TestAbort;
223  EFI_INPUT_KEY                     Key;
224  CHAR16                            StrPercent[80];
225  CHAR16                            *StrTotalMemory;
226  CHAR16                            *Pos;
227  CHAR16                            *TmpStr;
228  EFI_GRAPHICS_OUTPUT_BLT_PIXEL     Foreground;
229  EFI_GRAPHICS_OUTPUT_BLT_PIXEL     Background;
230  EFI_GRAPHICS_OUTPUT_BLT_PIXEL     Color;
231  BOOLEAN                           IsFirstBoot;
232  UINT32                            TempData;
233  UINTN                             StrTotalMemorySize;
234
235  ReturnStatus = EFI_SUCCESS;
236  ZeroMem (&Key, sizeof (EFI_INPUT_KEY));
237
238  StrTotalMemorySize = 128;
239  Pos = AllocateZeroPool (StrTotalMemorySize);
240
241  if (Pos == NULL) {
242    return ReturnStatus;
243  }
244
245  StrTotalMemory    = Pos;
246
247  TestedMemorySize  = 0;
248  TotalMemorySize   = 0;
249  PreviousValue     = 0;
250  ErrorOut          = FALSE;
251  TestAbort         = FALSE;
252
253  SetMem (&Foreground, sizeof (EFI_GRAPHICS_OUTPUT_BLT_PIXEL), 0xff);
254  SetMem (&Background, sizeof (EFI_GRAPHICS_OUTPUT_BLT_PIXEL), 0x0);
255  SetMem (&Color, sizeof (EFI_GRAPHICS_OUTPUT_BLT_PIXEL), 0xff);
256
257  RequireSoftECCInit = FALSE;
258
259  Status = gBS->LocateProtocol (
260                  &gEfiGenericMemTestProtocolGuid,
261                  NULL,
262                  (VOID **) &GenMemoryTest
263                  );
264  if (EFI_ERROR (Status)) {
265    FreePool (Pos);
266    return EFI_SUCCESS;
267  }
268
269  InitStatus = GenMemoryTest->MemoryTestInit (
270                                GenMemoryTest,
271                                Level,
272                                &RequireSoftECCInit
273                                );
274  if (InitStatus == EFI_NO_MEDIA) {
275    //
276    // The PEI codes also have the relevant memory test code to check the memory,
277    // it can select to test some range of the memory or all of them. If PEI code
278    // checks all the memory, this BDS memory test will has no not-test memory to
279    // do the test, and then the status of EFI_NO_MEDIA will be returned by
280    // "MemoryTestInit". So it does not need to test memory again, just return.
281    //
282    FreePool (Pos);
283    return EFI_SUCCESS;
284  }
285
286  if (!FeaturePcdGet(PcdBootlogoOnlyEnable)) {
287    TmpStr = GetStringById (STRING_TOKEN (STR_ESC_TO_SKIP_MEM_TEST));
288
289    if (TmpStr != NULL) {
290      PrintXY (10, 10, NULL, NULL, TmpStr);
291      FreePool (TmpStr);
292    }
293  } else {
294    DEBUG ((EFI_D_INFO, "Enter memory test.\n"));
295  }
296  do {
297    Status = GenMemoryTest->PerformMemoryTest (
298                              GenMemoryTest,
299                              &TestedMemorySize,
300                              &TotalMemorySize,
301                              &ErrorOut,
302                              TestAbort
303                              );
304    if (ErrorOut && (Status == EFI_DEVICE_ERROR)) {
305      TmpStr = GetStringById (STRING_TOKEN (STR_SYSTEM_MEM_ERROR));
306      if (TmpStr != NULL) {
307        PrintXY (10, 10, NULL, NULL, TmpStr);
308        FreePool (TmpStr);
309      }
310
311      ASSERT (0);
312    }
313
314    if (!FeaturePcdGet(PcdBootlogoOnlyEnable)) {
315      TempData = (UINT32) DivU64x32 (TotalMemorySize, 16);
316      TestPercent = (UINTN) DivU64x32 (
317                              DivU64x32 (MultU64x32 (TestedMemorySize, 100), 16),
318                              TempData
319                              );
320      if (TestPercent != PreviousValue) {
321        UnicodeValueToString (StrPercent, 0, TestPercent, 0);
322        TmpStr = GetStringById (STRING_TOKEN (STR_MEMORY_TEST_PERCENT));
323        if (TmpStr != NULL) {
324          //
325          // TmpStr size is 64, StrPercent is reserved to 16.
326          //
327          StrnCatS (
328            StrPercent,
329            sizeof (StrPercent) / sizeof (CHAR16),
330            TmpStr,
331            sizeof (StrPercent) / sizeof (CHAR16) - StrLen (StrPercent) - 1
332            );
333          PrintXY (10, 10, NULL, NULL, StrPercent);
334          FreePool (TmpStr);
335        }
336
337        TmpStr = GetStringById (STRING_TOKEN (STR_PERFORM_MEM_TEST));
338        if (TmpStr != NULL) {
339          PlatformBdsShowProgress (
340            Foreground,
341            Background,
342            TmpStr,
343            Color,
344            TestPercent,
345            (UINTN) PreviousValue
346            );
347          FreePool (TmpStr);
348        }
349      }
350
351      PreviousValue = TestPercent;
352    } else {
353      DEBUG ((EFI_D_INFO, "Perform memory test (ESC to skip).\n"));
354    }
355
356    if (!PcdGetBool (PcdConInConnectOnDemand)) {
357      KeyStatus     = gST->ConIn->ReadKeyStroke (gST->ConIn, &Key);
358      if (!EFI_ERROR (KeyStatus) && (Key.ScanCode == SCAN_ESC)) {
359        if (!RequireSoftECCInit) {
360          if (!FeaturePcdGet(PcdBootlogoOnlyEnable)) {
361            TmpStr = GetStringById (STRING_TOKEN (STR_PERFORM_MEM_TEST));
362            if (TmpStr != NULL) {
363              PlatformBdsShowProgress (
364                Foreground,
365                Background,
366                TmpStr,
367                Color,
368                100,
369                (UINTN) PreviousValue
370                );
371              FreePool (TmpStr);
372            }
373
374            PrintXY (10, 10, NULL, NULL, L"100");
375          }
376          Status = GenMemoryTest->Finished (GenMemoryTest);
377          goto Done;
378        }
379
380        TestAbort = TRUE;
381      }
382    }
383  } while (Status != EFI_NOT_FOUND);
384
385  Status = GenMemoryTest->Finished (GenMemoryTest);
386
387Done:
388  if (!FeaturePcdGet(PcdBootlogoOnlyEnable)) {
389    UnicodeValueToString (StrTotalMemory, COMMA_TYPE, TotalMemorySize, 0);
390    if (StrTotalMemory[0] == L',') {
391      StrTotalMemory++;
392      StrTotalMemorySize -= sizeof (CHAR16);
393    }
394
395    TmpStr = GetStringById (STRING_TOKEN (STR_MEM_TEST_COMPLETED));
396    if (TmpStr != NULL) {
397      StrnCatS (
398        StrTotalMemory,
399        StrTotalMemorySize / sizeof (CHAR16),
400        TmpStr,
401        StrTotalMemorySize / sizeof (CHAR16) - StrLen (StrTotalMemory) - 1
402        );
403      FreePool (TmpStr);
404    }
405
406    PrintXY (10, 10, NULL, NULL, StrTotalMemory);
407    PlatformBdsShowProgress (
408      Foreground,
409      Background,
410      StrTotalMemory,
411      Color,
412      100,
413      (UINTN) PreviousValue
414      );
415
416  } else {
417    DEBUG ((EFI_D_INFO, "%d bytes of system memory tested OK\r\n", TotalMemorySize));
418  }
419
420  FreePool (Pos);
421
422
423  //
424  // Use a DynamicHii type pcd to save the boot status, which is used to
425  // control configuration mode, such as FULL/MINIMAL/NO_CHANGES configuration.
426  //
427  IsFirstBoot = PcdGetBool(PcdBootState);
428  if (IsFirstBoot) {
429    Status = PcdSetBoolS(PcdBootState, FALSE);
430    if (EFI_ERROR (Status)) {
431      DEBUG ((EFI_D_ERROR, "Set PcdBootState to FALSE failed.\n"));
432    }
433  }
434
435  return ReturnStatus;
436}
437