]> git.proxmox.com Git - mirror_edk2.git/blame - FatPkg/FatPei/FatLiteAccess.c
FatPkg: Replace BSD License with BSD+Patent License
[mirror_edk2.git] / FatPkg / FatPei / FatLiteAccess.c
CommitLineData
2f4dfa84
JJ
1/** @file\r
2 FAT file system access routines for FAT recovery PEIM\r
3\r
e38f26a2 4Copyright (c) 2006 - 2018, Intel Corporation. All rights reserved.<BR>\r
2f4dfa84 5\r
eb6cb4ce 6SPDX-License-Identifier: BSD-2-Clause-Patent\r
2f4dfa84
JJ
7\r
8**/\r
9\r
10#include "FatLitePeim.h"\r
11\r
12\r
13/**\r
14 Check if there is a valid FAT in the corresponding Block device\r
15 of the volume and if yes, fill in the relevant fields for the\r
16 volume structure. Note there should be a valid Block device number\r
17 already set.\r
18\r
e38f26a2
LG
19 @param PrivateData Global memory map for accessing global\r
20 variables.\r
21 @param Volume On input, the BlockDeviceNumber field of the\r
22 Volume should be a valid value. On successful\r
23 output, all fields except the VolumeNumber\r
24 field is initialized.\r
2f4dfa84 25\r
e38f26a2
LG
26 @retval EFI_SUCCESS A FAT is found and the volume structure is\r
27 initialized.\r
28 @retval EFI_NOT_FOUND There is no FAT on the corresponding device.\r
2f4dfa84
JJ
29 @retval EFI_DEVICE_ERROR There is something error while accessing device.\r
30\r
31**/\r
32EFI_STATUS\r
33FatGetBpbInfo (\r
34 IN PEI_FAT_PRIVATE_DATA *PrivateData,\r
35 IN OUT PEI_FAT_VOLUME *Volume\r
36 )\r
37{\r
38 EFI_STATUS Status;\r
39 PEI_FAT_BOOT_SECTOR Bpb;\r
40 PEI_FAT_BOOT_SECTOR_EX BpbEx;\r
41 UINT32 Sectors;\r
42 UINT32 SectorsPerFat;\r
43 UINT32 RootDirSectors;\r
44 UINT64 FatLba;\r
45 UINT64 RootLba;\r
46 UINT64 FirstClusterLba;\r
47\r
48 //\r
49 // Read in the BPB\r
50 //\r
51 Status = FatReadDisk (\r
52 PrivateData,\r
53 Volume->BlockDeviceNo,\r
54 0,\r
55 sizeof (PEI_FAT_BOOT_SECTOR_EX),\r
56 &BpbEx\r
57 );\r
58 if (EFI_ERROR (Status)) {\r
59 return Status;\r
60 }\r
61\r
62 CopyMem (\r
63 (UINT8 *) (&Bpb),\r
64 (UINT8 *) (&BpbEx),\r
65 sizeof (PEI_FAT_BOOT_SECTOR)\r
66 );\r
67\r
68 Volume->FatType = FatUnknown;\r
69\r
70 Sectors = Bpb.Sectors;\r
71 if (Sectors == 0) {\r
72 Sectors = Bpb.LargeSectors;\r
73 }\r
74\r
75 SectorsPerFat = Bpb.SectorsPerFat;\r
76 if (SectorsPerFat == 0) {\r
77 SectorsPerFat = BpbEx.LargeSectorsPerFat;\r
78 Volume->FatType = Fat32;\r
79 }\r
80 //\r
81 // Filter out those not a FAT\r
82 //\r
83 if (Bpb.Ia32Jump[0] != 0xe9 && Bpb.Ia32Jump[0] != 0xeb && Bpb.Ia32Jump[0] != 0x49) {\r
84 return EFI_NOT_FOUND;\r
85 }\r
86\r
87 if (Bpb.ReservedSectors == 0 || Bpb.NoFats == 0 || Sectors == 0) {\r
88 return EFI_NOT_FOUND;\r
89 }\r
90\r
91 if (Bpb.SectorsPerCluster != 1 &&\r
92 Bpb.SectorsPerCluster != 2 &&\r
93 Bpb.SectorsPerCluster != 4 &&\r
94 Bpb.SectorsPerCluster != 8 &&\r
95 Bpb.SectorsPerCluster != 16 &&\r
96 Bpb.SectorsPerCluster != 32 &&\r
97 Bpb.SectorsPerCluster != 64 &&\r
98 Bpb.SectorsPerCluster != 128\r
99 ) {\r
100 return EFI_NOT_FOUND;\r
101 }\r
102\r
103 if (Volume->FatType == Fat32 && (SectorsPerFat == 0 || BpbEx.FsVersion != 0)) {\r
104 return EFI_NOT_FOUND;\r
105 }\r
106\r
107 if (Bpb.Media != 0xf0 &&\r
108 Bpb.Media != 0xf8 &&\r
109 Bpb.Media != 0xf9 &&\r
110 Bpb.Media != 0xfb &&\r
111 Bpb.Media != 0xfc &&\r
112 Bpb.Media != 0xfd &&\r
113 Bpb.Media != 0xfe &&\r
114 Bpb.Media != 0xff &&\r
115 //\r
116 // FujitsuFMR\r
117 //\r
118 Bpb.Media != 0x00 &&\r
119 Bpb.Media != 0x01 &&\r
120 Bpb.Media != 0xfa\r
121 ) {\r
122 return EFI_NOT_FOUND;\r
123 }\r
124\r
125 if (Volume->FatType != Fat32 && Bpb.RootEntries == 0) {\r
126 return EFI_NOT_FOUND;\r
127 }\r
128 //\r
129 // If this is fat32, refuse to mount mirror-disabled volumes\r
130 //\r
131 if (Volume->FatType == Fat32 && ((BpbEx.ExtendedFlags & 0x80) != 0)) {\r
132 return EFI_NOT_FOUND;\r
133 }\r
134 //\r
135 // Fill in the volume structure fields\r
136 // (Sectors & SectorsPerFat is computed earlier already)\r
137 //\r
138 Volume->ClusterSize = Bpb.SectorSize * Bpb.SectorsPerCluster;\r
139 Volume->RootEntries = Bpb.RootEntries;\r
140 Volume->SectorSize = Bpb.SectorSize;\r
141\r
142 RootDirSectors = ((Volume->RootEntries * sizeof (FAT_DIRECTORY_ENTRY)) + (Volume->SectorSize - 1)) / Volume->SectorSize;\r
143\r
144 FatLba = Bpb.ReservedSectors;\r
145 RootLba = Bpb.NoFats * SectorsPerFat + FatLba;\r
146 FirstClusterLba = RootLba + RootDirSectors;\r
147\r
148 Volume->VolumeSize = MultU64x32 (Sectors, Volume->SectorSize);\r
149 Volume->FatPos = MultU64x32 (FatLba, Volume->SectorSize);\r
150 Volume->RootDirPos = MultU64x32 (RootLba, Volume->SectorSize);\r
151 Volume->FirstClusterPos = MultU64x32 (FirstClusterLba, Volume->SectorSize);\r
152 Volume->MaxCluster = (UINT32) (Sectors - FirstClusterLba) / Bpb.SectorsPerCluster;\r
153 Volume->RootDirCluster = BpbEx.RootDirFirstCluster;\r
154\r
155 //\r
156 // If this is not a fat32, determine if it's a fat16 or fat12\r
157 //\r
158 if (Volume->FatType != Fat32) {\r
159\r
160 if (Volume->MaxCluster >= 65525) {\r
161 return EFI_NOT_FOUND;\r
162 }\r
163\r
164 Volume->FatType = Volume->MaxCluster < 4085 ? Fat12 : Fat16;\r
165 }\r
166\r
167 return EFI_SUCCESS;\r
168}\r
169\r
170\r
171/**\r
172 Gets the next cluster in the cluster chain\r
173\r
e38f26a2
LG
174 @param PrivateData Global memory map for accessing global variables\r
175 @param Volume The volume\r
176 @param Cluster The cluster\r
177 @param NextCluster The cluster number of the next cluster\r
2f4dfa84 178\r
e38f26a2
LG
179 @retval EFI_SUCCESS The address is got\r
180 @retval EFI_INVALID_PARAMETER ClusterNo exceeds the MaxCluster of the volume.\r
2f4dfa84
JJ
181 @retval EFI_DEVICE_ERROR Read disk error\r
182\r
183**/\r
184EFI_STATUS\r
185FatGetNextCluster (\r
186 IN PEI_FAT_PRIVATE_DATA *PrivateData,\r
187 IN PEI_FAT_VOLUME *Volume,\r
188 IN UINT32 Cluster,\r
189 OUT UINT32 *NextCluster\r
190 )\r
191{\r
192 EFI_STATUS Status;\r
193 UINT64 FatEntryPos;\r
194 UINT32 Dummy;\r
195\r
196 *NextCluster = 0;\r
197\r
198 if (Volume->FatType == Fat32) {\r
199 FatEntryPos = Volume->FatPos + MultU64x32 (4, Cluster);\r
200\r
201 Status = FatReadDisk (PrivateData, Volume->BlockDeviceNo, FatEntryPos, 4, NextCluster);\r
202 *NextCluster &= 0x0fffffff;\r
203\r
204 //\r
205 // Pad high bits for our FAT_CLUSTER_... macro definitions to work\r
206 //\r
207 if ((*NextCluster) >= 0x0ffffff7) {\r
208 *NextCluster |= (-1 &~0xf);\r
209 }\r
210\r
211 } else if (Volume->FatType == Fat16) {\r
212 FatEntryPos = Volume->FatPos + MultU64x32 (2, Cluster);\r
213\r
214 Status = FatReadDisk (PrivateData, Volume->BlockDeviceNo, FatEntryPos, 2, NextCluster);\r
215\r
216 //\r
217 // Pad high bits for our FAT_CLUSTER_... macro definitions to work\r
218 //\r
219 if ((*NextCluster) >= 0xfff7) {\r
220 *NextCluster |= (-1 &~0xf);\r
221 }\r
222\r
223 } else {\r
224 FatEntryPos = Volume->FatPos + DivU64x32Remainder (MultU64x32 (3, Cluster), 2, &Dummy);\r
225\r
226 Status = FatReadDisk (PrivateData, Volume->BlockDeviceNo, FatEntryPos, 2, NextCluster);\r
227\r
228 if ((Cluster & 0x01) != 0) {\r
229 *NextCluster = (*NextCluster) >> 4;\r
230 } else {\r
231 *NextCluster = (*NextCluster) & 0x0fff;\r
232 }\r
233 //\r
234 // Pad high bits for our FAT_CLUSTER_... macro definitions to work\r
235 //\r
236 if ((*NextCluster) >= 0x0ff7) {\r
237 *NextCluster |= (-1 &~0xf);\r
238 }\r
239 }\r
240\r
241 if (EFI_ERROR (Status)) {\r
242 return EFI_DEVICE_ERROR;\r
243 }\r
244\r
245 return EFI_SUCCESS;\r
246\r
247}\r
248\r
249\r
250/**\r
251 Set a file's CurrentPos and CurrentCluster, then compute StraightReadAmount.\r
252\r
e38f26a2
LG
253 @param PrivateData the global memory map\r
254 @param File the file\r
255 @param Pos the Position which is offset from the file's\r
256 CurrentPos\r
2f4dfa84 257\r
e38f26a2
LG
258 @retval EFI_SUCCESS Success.\r
259 @retval EFI_INVALID_PARAMETER Pos is beyond file's size.\r
2f4dfa84
JJ
260 @retval EFI_DEVICE_ERROR Something error while accessing media.\r
261\r
262**/\r
263EFI_STATUS\r
264FatSetFilePos (\r
265 IN PEI_FAT_PRIVATE_DATA *PrivateData,\r
266 IN PEI_FAT_FILE *File,\r
267 IN UINT32 Pos\r
268 )\r
269{\r
270 EFI_STATUS Status;\r
271 UINT32 AlignedPos;\r
272 UINT32 Offset;\r
273 UINT32 Cluster;\r
274 UINT32 PrevCluster;\r
275\r
276 if (File->IsFixedRootDir) {\r
277\r
278 if (Pos >= MultU64x32 (File->Volume->RootEntries, 32) - File->CurrentPos) {\r
279 return EFI_INVALID_PARAMETER;\r
280 }\r
281\r
282 File->CurrentPos += Pos;\r
283 File->StraightReadAmount = (UINT32) (MultU64x32 (File->Volume->RootEntries, 32) - File->CurrentPos);\r
284\r
285 } else {\r
286\r
287 DivU64x32Remainder (File->CurrentPos, File->Volume->ClusterSize, &Offset);\r
288 AlignedPos = (UINT32) File->CurrentPos - (UINT32) Offset;\r
289\r
290 while\r
291 (\r
292 !FAT_CLUSTER_FUNCTIONAL (File->CurrentCluster) &&\r
293 AlignedPos + File->Volume->ClusterSize <= File->CurrentPos + Pos\r
294 ) {\r
295 AlignedPos += File->Volume->ClusterSize;\r
296 Status = FatGetNextCluster (\r
297 PrivateData,\r
298 File->Volume,\r
299 File->CurrentCluster,\r
300 &File->CurrentCluster\r
301 );\r
302 if (EFI_ERROR (Status)) {\r
303 return EFI_DEVICE_ERROR;\r
304 }\r
305 }\r
306\r
307 if (FAT_CLUSTER_FUNCTIONAL (File->CurrentCluster)) {\r
308 return EFI_INVALID_PARAMETER;\r
309 }\r
310\r
311 File->CurrentPos += Pos;\r
1d951a30
FT
312 //\r
313 // Calculate the amount of consecutive cluster occupied by the file.\r
314 // FatReadFile() will use it to read these blocks once.\r
315 //\r
2f4dfa84
JJ
316 File->StraightReadAmount = 0;\r
317 Cluster = File->CurrentCluster;\r
318 while (!FAT_CLUSTER_FUNCTIONAL (Cluster)) {\r
319 File->StraightReadAmount += File->Volume->ClusterSize;\r
320 PrevCluster = Cluster;\r
321 Status = FatGetNextCluster (PrivateData, File->Volume, Cluster, &Cluster);\r
322 if (EFI_ERROR (Status)) {\r
323 return EFI_DEVICE_ERROR;\r
324 }\r
325\r
326 if (Cluster != PrevCluster + 1) {\r
327 break;\r
328 }\r
329 }\r
330\r
331 DivU64x32Remainder (File->CurrentPos, File->Volume->ClusterSize, &Offset);\r
332 File->StraightReadAmount -= (UINT32) Offset;\r
333\r
334 }\r
335\r
336 return EFI_SUCCESS;\r
337}\r
338\r
339\r
340/**\r
341 Reads file data. Updates the file's CurrentPos.\r
342\r
e38f26a2
LG
343 @param PrivateData Global memory map for accessing global variables\r
344 @param File The file.\r
345 @param Size The amount of data to read.\r
346 @param Buffer The buffer storing the data.\r
2f4dfa84 347\r
e38f26a2
LG
348 @retval EFI_SUCCESS The data is read.\r
349 @retval EFI_INVALID_PARAMETER File is invalid.\r
2f4dfa84
JJ
350 @retval EFI_DEVICE_ERROR Something error while accessing media.\r
351\r
352**/\r
353EFI_STATUS\r
354FatReadFile (\r
355 IN PEI_FAT_PRIVATE_DATA *PrivateData,\r
356 IN PEI_FAT_FILE *File,\r
357 IN UINTN Size,\r
358 OUT VOID *Buffer\r
359 )\r
360{\r
361 EFI_STATUS Status;\r
362 CHAR8 *BufferPtr;\r
363 UINT32 Offset;\r
364 UINT64 PhysicalAddr;\r
365 UINTN Amount;\r
366\r
367 BufferPtr = Buffer;\r
368\r
369 if (File->IsFixedRootDir) {\r
370 //\r
371 // This is the fixed root dir in FAT12 and FAT16\r
372 //\r
373 if (File->CurrentPos + Size > File->Volume->RootEntries * sizeof (FAT_DIRECTORY_ENTRY)) {\r
374 return EFI_INVALID_PARAMETER;\r
375 }\r
376\r
377 Status = FatReadDisk (\r
378 PrivateData,\r
379 File->Volume->BlockDeviceNo,\r
380 File->Volume->RootDirPos + File->CurrentPos,\r
381 Size,\r
382 Buffer\r
383 );\r
384 File->CurrentPos += (UINT32) Size;\r
385 return Status;\r
386\r
387 } else {\r
388\r
389 if ((File->Attributes & FAT_ATTR_DIRECTORY) == 0) {\r
64b25f5d 390 Size = Size < (File->FileSize - File->CurrentPos) ? Size : (File->FileSize - File->CurrentPos);\r
2f4dfa84
JJ
391 }\r
392 //\r
393 // This is a normal cluster based file\r
394 //\r
395 while (Size != 0) {\r
396 DivU64x32Remainder (File->CurrentPos, File->Volume->ClusterSize, &Offset);\r
397 PhysicalAddr = File->Volume->FirstClusterPos + MultU64x32 (File->Volume->ClusterSize, File->CurrentCluster - 2);\r
398\r
399 Amount = File->StraightReadAmount;\r
400 Amount = Size > Amount ? Amount : Size;\r
401 Status = FatReadDisk (\r
402 PrivateData,\r
403 File->Volume->BlockDeviceNo,\r
404 PhysicalAddr + Offset,\r
405 Amount,\r
406 BufferPtr\r
407 );\r
408 if (EFI_ERROR (Status)) {\r
409 return EFI_DEVICE_ERROR;\r
410 }\r
411 //\r
412 // Advance the file's current pos and current cluster\r
413 //\r
414 FatSetFilePos (PrivateData, File, (UINT32) Amount);\r
415\r
416 BufferPtr += Amount;\r
417 Size -= Amount;\r
418 }\r
419\r
420 return EFI_SUCCESS;\r
421 }\r
422}\r
423\r
424\r
425/**\r
426 This function reads the next item in the parent directory and\r
427 initializes the output parameter SubFile (CurrentPos is initialized to 0).\r
428 The function updates the CurrentPos of the parent dir to after the item read.\r
429 If no more items were found, the function returns EFI_NOT_FOUND.\r
430\r
e38f26a2
LG
431 @param PrivateData Global memory map for accessing global variables\r
432 @param ParentDir The parent directory.\r
433 @param SubFile The File structure containing the sub file that\r
434 is caught.\r
2f4dfa84 435\r
e38f26a2
LG
436 @retval EFI_SUCCESS The next sub file is obtained.\r
437 @retval EFI_INVALID_PARAMETER The ParentDir is not a directory.\r
438 @retval EFI_NOT_FOUND No more sub file exists.\r
2f4dfa84
JJ
439 @retval EFI_DEVICE_ERROR Something error while accessing media.\r
440\r
441**/\r
442EFI_STATUS\r
443FatReadNextDirectoryEntry (\r
444 IN PEI_FAT_PRIVATE_DATA *PrivateData,\r
445 IN PEI_FAT_FILE *ParentDir,\r
446 OUT PEI_FAT_FILE *SubFile\r
447 )\r
448{\r
449 EFI_STATUS Status;\r
450 FAT_DIRECTORY_ENTRY DirEntry;\r
451 CHAR16 *Pos;\r
452 CHAR16 BaseName[9];\r
453 CHAR16 Ext[4];\r
454\r
455 ZeroMem ((UINT8 *) SubFile, sizeof (PEI_FAT_FILE));\r
456\r
457 //\r
458 // Pick a valid directory entry\r
459 //\r
460 while (1) {\r
461 //\r
462 // Read one entry\r
463 //\r
464 Status = FatReadFile (PrivateData, ParentDir, 32, &DirEntry);\r
465 if (EFI_ERROR (Status)) {\r
466 return EFI_DEVICE_ERROR;\r
467 }\r
468 //\r
469 // We only search for *FILE* in root directory\r
470 // Long file name entry is *NOT* supported\r
471 //\r
3ba5368d 472 if (((DirEntry.Attributes & FAT_ATTR_DIRECTORY) == FAT_ATTR_DIRECTORY) || (DirEntry.Attributes == FAT_ATTR_LFN)) {\r
2f4dfa84
JJ
473 continue;\r
474 }\r
475 //\r
476 // if this is a terminator dir entry, just return EFI_NOT_FOUND\r
477 //\r
478 if (DirEntry.FileName[0] == EMPTY_ENTRY_MARK) {\r
479 return EFI_NOT_FOUND;\r
480 }\r
481 //\r
482 // If this not an invalid entry neither an empty entry, this is what we want.\r
483 // otherwise we will start a new loop to continue to find something meaningful\r
484 //\r
485 if ((UINT8) DirEntry.FileName[0] != DELETE_ENTRY_MARK) {\r
486 break;\r
487 }\r
488 }\r
489 //\r
490 // fill in the output parameter\r
491 //\r
492 EngFatToStr (8, DirEntry.FileName, BaseName);\r
493 EngFatToStr (3, DirEntry.FileName + 8, Ext);\r
494\r
495 Pos = (UINT16 *) SubFile->FileName;\r
496 SetMem ((UINT8 *) Pos, FAT_MAX_FILE_NAME_LENGTH, 0);\r
497 CopyMem ((UINT8 *) Pos, (UINT8 *) BaseName, 2 * (StrLen (BaseName) + 1));\r
498\r
499 if (Ext[0] != 0) {\r
500 Pos += StrLen (BaseName);\r
501 *Pos = '.';\r
502 Pos++;\r
503 CopyMem ((UINT8 *) Pos, (UINT8 *) Ext, 2 * (StrLen (Ext) + 1));\r
504 }\r
505\r
506 SubFile->Attributes = DirEntry.Attributes;\r
507 SubFile->CurrentCluster = DirEntry.FileCluster;\r
508 if (ParentDir->Volume->FatType == Fat32) {\r
509 SubFile->CurrentCluster |= DirEntry.FileClusterHigh << 16;\r
510 }\r
511\r
512 SubFile->CurrentPos = 0;\r
513 SubFile->FileSize = DirEntry.FileSize;\r
514 SubFile->StartingCluster = SubFile->CurrentCluster;\r
515 SubFile->Volume = ParentDir->Volume;\r
516\r
2f4dfa84
JJ
517 //\r
518 // in Pei phase, time parameters do not need to be filled for minimum use.\r
519 //\r
520 return Status;\r
521}\r