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