1 /** @file
2   General purpose supporting routines for FAT recovery PEIM
3 
4 Copyright (c) 2006 - 2015, Intel Corporation. All rights reserved.<BR>
5 
6 This program and the accompanying materials are licensed and made available
7 under the terms and conditions of the BSD License which accompanies this
8 distribution. The 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 "FatLitePeim.h"
17 
18 
19 #define CHAR_FAT_VALID  0x01
20 
21 
22 /**
23   Converts a union code character to upper case.
24   This functions converts a unicode character to upper case.
25   If the input Letter is not a lower-cased letter,
26   the original value is returned.
27 
28   @param  Letter            The input unicode character.
29 
30   @return The upper cased letter.
31 
32 **/
33 CHAR16
ToUpper(IN CHAR16 Letter)34 ToUpper (
35   IN CHAR16                    Letter
36   )
37 {
38   if ('a' <= Letter && Letter <= 'z') {
39     Letter = (CHAR16) (Letter - 0x20);
40   }
41 
42   return Letter;
43 }
44 
45 
46 /**
47   Reads a block of data from the block device by calling
48   underlying Block I/O service.
49 
50   @param  PrivateData       Global memory map for accessing global variables
51   @param  BlockDeviceNo     The index for the block device number.
52   @param  Lba               The logic block address to read data from.
53   @param  BufferSize        The size of data in byte to read.
54   @param  Buffer            The buffer of the
55 
56   @retval EFI_DEVICE_ERROR  The specified block device number exceeds the maximum
57                             device number.
58   @retval EFI_DEVICE_ERROR  The maximum address has exceeded the maximum address
59                             of the block device.
60 
61 **/
62 EFI_STATUS
FatReadBlock(IN PEI_FAT_PRIVATE_DATA * PrivateData,IN UINTN BlockDeviceNo,IN EFI_PEI_LBA Lba,IN UINTN BufferSize,OUT VOID * Buffer)63 FatReadBlock (
64   IN  PEI_FAT_PRIVATE_DATA   *PrivateData,
65   IN  UINTN                  BlockDeviceNo,
66   IN  EFI_PEI_LBA            Lba,
67   IN  UINTN                  BufferSize,
68   OUT VOID                   *Buffer
69   )
70 {
71   EFI_STATUS            Status;
72   PEI_FAT_BLOCK_DEVICE  *BlockDev;
73 
74   if (BlockDeviceNo > PEI_FAT_MAX_BLOCK_DEVICE - 1) {
75     return EFI_DEVICE_ERROR;
76   }
77 
78   Status    = EFI_SUCCESS;
79   BlockDev  = &(PrivateData->BlockDevice[BlockDeviceNo]);
80 
81   if (BufferSize > MultU64x32 (BlockDev->LastBlock - Lba + 1, BlockDev->BlockSize)) {
82     return EFI_DEVICE_ERROR;
83   }
84 
85   if (!BlockDev->Logical) {
86     //
87     // Status = BlockDev->ReadFunc
88     //  (PrivateData->PeiServices, BlockDev->PhysicalDevNo, Lba, BufferSize, Buffer);
89     //
90     if (BlockDev->BlockIo2 != NULL) {
91       Status = BlockDev->BlockIo2->ReadBlocks (
92                                     (EFI_PEI_SERVICES **) GetPeiServicesTablePointer (),
93                                     BlockDev->BlockIo2,
94                                     BlockDev->PhysicalDevNo,
95                                     Lba,
96                                     BufferSize,
97                                     Buffer
98                                     );
99     } else {
100       Status = BlockDev->BlockIo->ReadBlocks (
101                                   (EFI_PEI_SERVICES **) GetPeiServicesTablePointer (),
102                                   BlockDev->BlockIo,
103                                   BlockDev->PhysicalDevNo,
104                                   Lba,
105                                   BufferSize,
106                                   Buffer
107                                   );
108     }
109 
110   } else {
111     Status = FatReadDisk (
112               PrivateData,
113               BlockDev->ParentDevNo,
114               BlockDev->StartingPos + MultU64x32 (Lba, BlockDev->BlockSize),
115               BufferSize,
116               Buffer
117               );
118   }
119 
120   return Status;
121 }
122 
123 
124 /**
125   Find a cache block designated to specific Block device and Lba.
126   If not found, invalidate an oldest one and use it. (LRU cache)
127 
128   @param  PrivateData       the global memory map.
129   @param  BlockDeviceNo     the Block device.
130   @param  Lba               the Logical Block Address
131   @param  CachePtr          Ptr to the starting address of the memory holding the
132                             data;
133 
134   @retval EFI_SUCCESS       The function completed successfully.
135   @retval EFI_DEVICE_ERROR  Something error while accessing media.
136 
137 **/
138 EFI_STATUS
FatGetCacheBlock(IN PEI_FAT_PRIVATE_DATA * PrivateData,IN UINTN BlockDeviceNo,IN UINT64 Lba,OUT CHAR8 ** CachePtr)139 FatGetCacheBlock (
140   IN  PEI_FAT_PRIVATE_DATA  *PrivateData,
141   IN  UINTN                 BlockDeviceNo,
142   IN  UINT64                Lba,
143   OUT CHAR8                 **CachePtr
144   )
145 {
146   EFI_STATUS            Status;
147   PEI_FAT_CACHE_BUFFER  *CacheBuffer;
148   INTN                  Index;
149   STATIC UINT8          Seed;
150 
151   Status      = EFI_SUCCESS;
152   CacheBuffer = NULL;
153 
154   //
155   // go through existing cache buffers
156   //
157   for (Index = 0; Index < PEI_FAT_CACHE_SIZE; Index++) {
158     CacheBuffer = &(PrivateData->CacheBuffer[Index]);
159     if (CacheBuffer->Valid && CacheBuffer->BlockDeviceNo == BlockDeviceNo && CacheBuffer->Lba == Lba) {
160       break;
161     }
162   }
163 
164   if (Index < PEI_FAT_CACHE_SIZE) {
165     *CachePtr = (CHAR8 *) CacheBuffer->Buffer;
166     return EFI_SUCCESS;
167   }
168   //
169   // We have to find an invalid cache buffer
170   //
171   for (Index = 0; Index < PEI_FAT_CACHE_SIZE; Index++) {
172     if (!PrivateData->CacheBuffer[Index].Valid) {
173       break;
174     }
175   }
176   //
177   // Use the cache buffer
178   //
179   if (Index == PEI_FAT_CACHE_SIZE) {
180     Index = (Seed++) % PEI_FAT_CACHE_SIZE;
181   }
182 
183   //
184   // Current device ID should be less than maximum device ID.
185   //
186   if (BlockDeviceNo >= PEI_FAT_MAX_BLOCK_DEVICE) {
187     return EFI_DEVICE_ERROR;
188   }
189 
190   CacheBuffer                 = &(PrivateData->CacheBuffer[Index]);
191 
192   CacheBuffer->BlockDeviceNo  = BlockDeviceNo;
193   CacheBuffer->Lba            = Lba;
194   CacheBuffer->Size           = PrivateData->BlockDevice[BlockDeviceNo].BlockSize;
195 
196   //
197   // Read in the data
198   //
199   Status = FatReadBlock (
200             PrivateData,
201             BlockDeviceNo,
202             Lba,
203             CacheBuffer->Size,
204             CacheBuffer->Buffer
205             );
206   if (EFI_ERROR (Status)) {
207     return EFI_DEVICE_ERROR;
208   }
209 
210   CacheBuffer->Valid  = TRUE;
211   *CachePtr           = (CHAR8 *) CacheBuffer->Buffer;
212 
213   return Status;
214 }
215 
216 
217 /**
218   Disk reading.
219 
220   @param  PrivateData       the global memory map;
221   @param  BlockDeviceNo     the block device to read;
222   @param  StartingAddress   the starting address.
223   @param  Size              the amount of data to read.
224   @param  Buffer            the buffer holding the data
225 
226   @retval EFI_SUCCESS       The function completed successfully.
227   @retval EFI_DEVICE_ERROR  Something error.
228 
229 **/
230 EFI_STATUS
FatReadDisk(IN PEI_FAT_PRIVATE_DATA * PrivateData,IN UINTN BlockDeviceNo,IN UINT64 StartingAddress,IN UINTN Size,OUT VOID * Buffer)231 FatReadDisk (
232   IN  PEI_FAT_PRIVATE_DATA  *PrivateData,
233   IN  UINTN                 BlockDeviceNo,
234   IN  UINT64                StartingAddress,
235   IN  UINTN                 Size,
236   OUT VOID                  *Buffer
237   )
238 {
239   EFI_STATUS  Status;
240   UINT32      BlockSize;
241   CHAR8       *BufferPtr;
242   CHAR8       *CachePtr;
243   UINT32      Offset;
244   UINT64      Lba;
245   UINT64      OverRunLba;
246   UINTN       Amount;
247 
248   Status    = EFI_SUCCESS;
249   BufferPtr = Buffer;
250   BlockSize = PrivateData->BlockDevice[BlockDeviceNo].BlockSize;
251 
252   //
253   // Read underrun
254   //
255   Lba     = DivU64x32Remainder (StartingAddress, BlockSize, &Offset);
256   Status  = FatGetCacheBlock (PrivateData, BlockDeviceNo, Lba, &CachePtr);
257   if (EFI_ERROR (Status)) {
258     return EFI_DEVICE_ERROR;
259   }
260 
261   Amount = Size < (BlockSize - Offset) ? Size : (BlockSize - Offset);
262   CopyMem (BufferPtr, CachePtr + Offset, Amount);
263 
264   if (Size == Amount) {
265     return EFI_SUCCESS;
266   }
267 
268   Size -= Amount;
269   BufferPtr += Amount;
270   StartingAddress += Amount;
271   Lba += 1;
272 
273   //
274   // Read aligned parts
275   //
276   OverRunLba = Lba + DivU64x32Remainder (Size, BlockSize, &Offset);
277 
278   Size -= Offset;
279   Status = FatReadBlock (PrivateData, BlockDeviceNo, Lba, Size, BufferPtr);
280   if (EFI_ERROR (Status)) {
281     return EFI_DEVICE_ERROR;
282   }
283 
284   BufferPtr += Size;
285 
286   //
287   // Read overrun
288   //
289   if (Offset != 0) {
290     Status = FatGetCacheBlock (PrivateData, BlockDeviceNo, OverRunLba, &CachePtr);
291     if (EFI_ERROR (Status)) {
292       return EFI_DEVICE_ERROR;
293     }
294 
295     CopyMem (BufferPtr, CachePtr, Offset);
296   }
297 
298   return Status;
299 }
300 
301 
302 /**
303   This version is different from the version in Unicode collation
304   protocol in that this version strips off trailing blanks.
305   Converts an 8.3 FAT file name using an OEM character set
306   to a Null-terminated Unicode string.
307   Here does not expand DBCS FAT chars.
308 
309   @param  FatSize           The size of the string Fat in bytes.
310   @param  Fat               A pointer to a Null-terminated string that contains
311                             an 8.3 file name using an OEM character set.
312   @param  Str               A pointer to a Null-terminated Unicode string. The
313                             string must be allocated in advance to hold FatSize
314                             Unicode characters
315 
316 **/
317 VOID
EngFatToStr(IN UINTN FatSize,IN CHAR8 * Fat,OUT CHAR16 * Str)318 EngFatToStr (
319   IN UINTN                            FatSize,
320   IN CHAR8                            *Fat,
321   OUT CHAR16                          *Str
322   )
323 {
324   CHAR16  *String;
325 
326   String = Str;
327   //
328   // No DBCS issues, just expand and add null terminate to end of string
329   //
330   while (*Fat != 0 && FatSize != 0) {
331     if (*Fat == ' ') {
332       break;
333     }
334     *String = *Fat;
335     String += 1;
336     Fat += 1;
337     FatSize -= 1;
338   }
339 
340   *String = 0;
341 }
342 
343 
344 /**
345   Performs a case-insensitive comparison of two Null-terminated Unicode strings.
346 
347   @param  PrivateData       Global memory map for accessing global variables
348   @param  Str1              First string to perform case insensitive comparison.
349   @param  Str2              Second string to perform case insensitive comparison.
350 
351 **/
352 BOOLEAN
EngStriColl(IN PEI_FAT_PRIVATE_DATA * PrivateData,IN CHAR16 * Str1,IN CHAR16 * Str2)353 EngStriColl (
354   IN  PEI_FAT_PRIVATE_DATA  *PrivateData,
355   IN CHAR16                 *Str1,
356   IN CHAR16                 *Str2
357   )
358 {
359   CHAR16  UpperS1;
360   CHAR16  UpperS2;
361 
362   UpperS1 = ToUpper (*Str1);
363   UpperS2 = ToUpper (*Str2);
364   while (*Str1 != 0) {
365     if (UpperS1 != UpperS2) {
366       return FALSE;
367     }
368 
369     Str1++;
370     Str2++;
371     UpperS1 = ToUpper (*Str1);
372     UpperS2 = ToUpper (*Str2);
373   }
374 
375   return (BOOLEAN) ((*Str2 != 0) ? FALSE : TRUE);
376 }
377