1 /** @file
2   Diagnostics Protocol implementation for the MMC DXE driver
3 
4   Copyright (c) 2011-2020, ARM Limited. All rights reserved.
5 
6   SPDX-License-Identifier: BSD-2-Clause-Patent
7 
8 **/
9 
10 #include <Uefi.h>
11 #include <Library/DebugLib.h>
12 #include <Library/BaseMemoryLib.h>
13 #include <Library/MemoryAllocationLib.h>
14 #include <Library/BaseLib.h>
15 
16 #include "Mmc.h"
17 
18 #define DIAGNOSTIC_LOGBUFFER_MAXCHAR  1024
19 
20 CHAR16* mLogBuffer = NULL;
21 UINTN   mLogRemainChar = 0;
22 
23 CHAR16*
DiagnosticInitLog(UINTN MaxBufferChar)24 DiagnosticInitLog (
25   UINTN MaxBufferChar
26   )
27 {
28   mLogRemainChar = MaxBufferChar;
29   mLogBuffer = AllocatePool ((UINTN)MaxBufferChar * sizeof (CHAR16));
30   return mLogBuffer;
31 }
32 
33 UINTN
DiagnosticLog(CONST CHAR16 * Str)34 DiagnosticLog (
35   CONST CHAR16* Str
36   )
37 {
38   UINTN len = StrLen (Str);
39   if (len < mLogRemainChar) {
40     StrCpyS (mLogBuffer, mLogRemainChar, Str);
41     mLogRemainChar -= len;
42     mLogBuffer += len;
43     return len;
44   } else {
45     return 0;
46   }
47 }
48 
49 VOID
GenerateRandomBuffer(VOID * Buffer,UINTN BufferSize)50 GenerateRandomBuffer (
51   VOID* Buffer,
52   UINTN BufferSize
53   )
54 {
55   UINT64  i;
56   UINT64* Buffer64 = (UINT64*)Buffer;
57 
58   for (i = 0; i < (BufferSize >> 3); i++) {
59     *Buffer64 = i | LShiftU64 (~i, 32);
60     Buffer64++;
61   }
62 }
63 
64 BOOLEAN
CompareBuffer(VOID * BufferA,VOID * BufferB,UINTN BufferSize)65 CompareBuffer (
66   VOID  *BufferA,
67   VOID  *BufferB,
68   UINTN BufferSize
69   )
70 {
71   UINTN i;
72   UINT64* BufferA64 = (UINT64*)BufferA;
73   UINT64* BufferB64 = (UINT64*)BufferB;
74 
75   for (i = 0; i < (BufferSize >> 3); i++) {
76     if (*BufferA64 != *BufferB64) {
77       DEBUG ((EFI_D_ERROR, "CompareBuffer: Error at %i", i));
78       DEBUG ((EFI_D_ERROR, "(0x%lX) != (0x%lX)\n", *BufferA64, *BufferB64));
79       return FALSE;
80     }
81     BufferA64++;
82     BufferB64++;
83   }
84   return TRUE;
85 }
86 
87 EFI_STATUS
MmcReadWriteDataTest(MMC_HOST_INSTANCE * MmcHostInstance,EFI_LBA Lba,UINTN BufferSize)88 MmcReadWriteDataTest (
89   MMC_HOST_INSTANCE *MmcHostInstance,
90   EFI_LBA           Lba,
91   UINTN             BufferSize
92   )
93 {
94   VOID                        *BackBuffer;
95   VOID                        *WriteBuffer;
96   VOID                        *ReadBuffer;
97   EFI_STATUS                  Status;
98 
99   // Check if a Media is Present
100   if (!MmcHostInstance->BlockIo.Media->MediaPresent) {
101     DiagnosticLog (L"ERROR: No Media Present\n");
102     return EFI_NO_MEDIA;
103   }
104 
105   if (MmcHostInstance->State != MmcTransferState) {
106     DiagnosticLog (L"ERROR: Not ready for Transfer state\n");
107     return EFI_NOT_READY;
108   }
109 
110   BackBuffer = AllocatePool (BufferSize);
111   WriteBuffer = AllocatePool (BufferSize);
112   ReadBuffer = AllocatePool (BufferSize);
113 
114   // Read (and save) buffer at a specific location
115   Status = MmcReadBlocks (&(MmcHostInstance->BlockIo), MmcHostInstance->BlockIo.Media->MediaId,Lba,BufferSize,BackBuffer);
116   if (Status != EFI_SUCCESS) {
117     DiagnosticLog (L"ERROR: Fail to Read Block (1)\n");
118     return Status;
119   }
120 
121   // Write buffer at the same location
122   GenerateRandomBuffer (WriteBuffer,BufferSize);
123   Status = MmcWriteBlocks (&(MmcHostInstance->BlockIo), MmcHostInstance->BlockIo.Media->MediaId,Lba,BufferSize,WriteBuffer);
124   if (Status != EFI_SUCCESS) {
125     DiagnosticLog (L"ERROR: Fail to Write Block (1)\n");
126     return Status;
127   }
128 
129   // Read the buffer at the same location
130   Status = MmcReadBlocks (&(MmcHostInstance->BlockIo), MmcHostInstance->BlockIo.Media->MediaId,Lba,BufferSize,ReadBuffer);
131   if (Status != EFI_SUCCESS) {
132     DiagnosticLog (L"ERROR: Fail to Read Block (2)\n");
133     return Status;
134   }
135 
136   // Check that is conform
137   if (!CompareBuffer (ReadBuffer,WriteBuffer,BufferSize)) {
138     DiagnosticLog (L"ERROR: Fail to Read/Write Block (1)\n");
139     return EFI_INVALID_PARAMETER;
140   }
141 
142   // Restore content at the original location
143   Status = MmcWriteBlocks (&(MmcHostInstance->BlockIo), MmcHostInstance->BlockIo.Media->MediaId,Lba,BufferSize,BackBuffer);
144   if (Status != EFI_SUCCESS) {
145     DiagnosticLog (L"ERROR: Fail to Write Block (2)\n");
146     return Status;
147   }
148 
149   // Read the restored content
150   Status = MmcReadBlocks (&(MmcHostInstance->BlockIo), MmcHostInstance->BlockIo.Media->MediaId,Lba,BufferSize,ReadBuffer);
151   if (Status != EFI_SUCCESS) {
152     DiagnosticLog (L"ERROR: Fail to Read Block (3)\n");
153     return Status;
154   }
155 
156   // Check the content is correct
157   if (!CompareBuffer (ReadBuffer,BackBuffer,BufferSize)) {
158     DiagnosticLog (L"ERROR: Fail to Read/Write Block (2)\n");
159     return EFI_INVALID_PARAMETER;
160   }
161 
162   return EFI_SUCCESS;
163 }
164 
165 EFI_STATUS
166 EFIAPI
MmcDriverDiagnosticsRunDiagnostics(IN EFI_DRIVER_DIAGNOSTICS_PROTOCOL * This,IN EFI_HANDLE ControllerHandle,IN EFI_HANDLE ChildHandle OPTIONAL,IN EFI_DRIVER_DIAGNOSTIC_TYPE DiagnosticType,IN CHAR8 * Language,OUT EFI_GUID ** ErrorType,OUT UINTN * BufferSize,OUT CHAR16 ** Buffer)167 MmcDriverDiagnosticsRunDiagnostics (
168   IN  EFI_DRIVER_DIAGNOSTICS_PROTOCOL               *This,
169   IN  EFI_HANDLE                                    ControllerHandle,
170   IN  EFI_HANDLE                                    ChildHandle  OPTIONAL,
171   IN  EFI_DRIVER_DIAGNOSTIC_TYPE                    DiagnosticType,
172   IN  CHAR8                                         *Language,
173   OUT EFI_GUID                                      **ErrorType,
174   OUT UINTN                                         *BufferSize,
175   OUT CHAR16                                        **Buffer
176   )
177 {
178   LIST_ENTRY              *CurrentLink;
179   MMC_HOST_INSTANCE       *MmcHostInstance;
180   EFI_STATUS              Status;
181 
182   if ((Language         == NULL) ||
183       (ErrorType        == NULL) ||
184       (Buffer           == NULL) ||
185       (ControllerHandle == NULL) ||
186       (BufferSize       == NULL)) {
187     return EFI_INVALID_PARAMETER;
188   }
189 
190   // Check Language is supported (i.e. is "en-*" - only English is supported)
191   if (AsciiStrnCmp (Language, "en", 2) != 0) {
192     return EFI_UNSUPPORTED;
193   }
194 
195   Status = EFI_SUCCESS;
196   *ErrorType  = NULL;
197   *BufferSize = DIAGNOSTIC_LOGBUFFER_MAXCHAR;
198   *Buffer = DiagnosticInitLog (DIAGNOSTIC_LOGBUFFER_MAXCHAR);
199 
200   DiagnosticLog (L"MMC Driver Diagnostics\n");
201 
202   // Find the MMC Host instance on which we have been asked to run diagnostics
203   MmcHostInstance = NULL;
204   CurrentLink = mMmcHostPool.ForwardLink;
205   while (CurrentLink != NULL && CurrentLink != &mMmcHostPool && (Status == EFI_SUCCESS)) {
206     MmcHostInstance = MMC_HOST_INSTANCE_FROM_LINK(CurrentLink);
207     ASSERT(MmcHostInstance != NULL);
208     if (MmcHostInstance->MmcHandle == ControllerHandle) {
209       break;
210     }
211     CurrentLink = CurrentLink->ForwardLink;
212   }
213 
214   // If we didn't find the controller, return EFI_UNSUPPORTED
215   if ((MmcHostInstance == NULL)
216       || (MmcHostInstance->MmcHandle != ControllerHandle)) {
217     return EFI_UNSUPPORTED;
218   }
219 
220   // LBA=1 Size=BlockSize
221   DiagnosticLog (L"MMC Driver Diagnostics - Test: First Block\n");
222   Status = MmcReadWriteDataTest (MmcHostInstance, 1, MmcHostInstance->BlockIo.Media->BlockSize);
223 
224   // LBA=2 Size=BlockSize
225   DiagnosticLog (L"MMC Driver Diagnostics - Test: Second Block\n");
226   Status = MmcReadWriteDataTest (MmcHostInstance, 2, MmcHostInstance->BlockIo.Media->BlockSize);
227 
228   // LBA=10 Size=BlockSize
229   DiagnosticLog (L"MMC Driver Diagnostics - Test: Any Block\n");
230   Status = MmcReadWriteDataTest (
231              MmcHostInstance,
232              RShiftU64 (MmcHostInstance->BlockIo.Media->LastBlock, 1),
233              MmcHostInstance->BlockIo.Media->BlockSize
234              );
235 
236   // LBA=LastBlock Size=BlockSize
237   DiagnosticLog (L"MMC Driver Diagnostics - Test: Last Block\n");
238   Status = MmcReadWriteDataTest (MmcHostInstance, MmcHostInstance->BlockIo.Media->LastBlock, MmcHostInstance->BlockIo.Media->BlockSize);
239 
240   // LBA=1 Size=2*BlockSize
241   DiagnosticLog (L"MMC Driver Diagnostics - Test: First Block / 2 BlockSSize\n");
242   Status = MmcReadWriteDataTest (MmcHostInstance, 1, 2 * MmcHostInstance->BlockIo.Media->BlockSize);
243 
244   return Status;
245 }
246 
247 //
248 // EFI Driver Diagnostics 2 Protocol
249 //
250 GLOBAL_REMOVE_IF_UNREFERENCED EFI_DRIVER_DIAGNOSTICS2_PROTOCOL gMmcDriverDiagnostics2 = {
251   (EFI_DRIVER_DIAGNOSTICS2_RUN_DIAGNOSTICS) MmcDriverDiagnosticsRunDiagnostics,
252   "en"
253 };
254