7ad4005630619db693ce64b58447284f16f525f4
[efi/fat/.git] / FatPkg / EnhancedFatDxe / Init.c
1 /*++\r
2 \r
3 Copyright (c) 2005 - 2007, Intel Corporation\r
4 All rights reserved. This program and the accompanying materials\r
5 are licensed and made available under the terms and conditions of the Software\r
6 License Agreement which accompanies this distribution.\r
7 \r
8 \r
9 Module Name:\r
10 \r
11   Init.c\r
12 \r
13 Abstract:\r
14 \r
15   Initialization routines\r
16 \r
17 --*/\r
18 \r
19 #include "Fat.h"\r
20 \r
21 EFI_STATUS\r
22 FatAllocateVolume (\r
23   IN  EFI_HANDLE                Handle,\r
24   IN  EFI_DISK_IO_PROTOCOL      *DiskIo,\r
25   IN  EFI_BLOCK_IO_PROTOCOL     *BlockIo\r
26   )\r
27 /*++\r
28 \r
29 Routine Description:\r
30 \r
31   Allocates volume structure, detects FAT file system, installs protocol,\r
32   and initialize cache.\r
33 \r
34 Arguments:\r
35 \r
36   Handle                - The handle of parent device.\r
37   DiskIo                - The DiskIo of parent device.\r
38   BlockIo               - The BlockIo of parent devicel\r
39 \r
40 Returns:\r
41 \r
42   EFI_SUCCESS           - Allocate a new volume successfully.\r
43   EFI_OUT_OF_RESOURCES  - Can not allocate the memory.\r
44   Others                - Allocating a new volume failed.\r
45 \r
46 --*/\r
47 {\r
48   EFI_STATUS  Status;\r
49   FAT_VOLUME  *Volume;\r
50   BOOLEAN     LockedByMe;\r
51   LockedByMe = FALSE;\r
52   //\r
53   // Allocate a volume structure\r
54   //\r
55   Volume = AllocateZeroPool (sizeof (FAT_VOLUME));\r
56   if (Volume == NULL) {\r
57     return EFI_OUT_OF_RESOURCES;\r
58   }\r
59   //\r
60   // Acquire the lock.\r
61   // If caller has already acquired the lock, cannot lock it again.\r
62   //\r
63   if (!FatIsLocked ()) {\r
64     FatAcquireLock ();\r
65     LockedByMe = TRUE;\r
66   }\r
67   //\r
68   // Initialize the structure\r
69   //\r
70   Volume->Signature                   = FAT_VOLUME_SIGNATURE;\r
71   Volume->Handle                      = Handle;\r
72   Volume->DiskIo                      = DiskIo;\r
73   Volume->BlockIo                     = BlockIo;\r
74   Volume->MediaId                     = BlockIo->Media->MediaId;\r
75   Volume->ReadOnly                    = BlockIo->Media->ReadOnly;\r
76   Volume->VolumeInterface.Revision    = EFI_SIMPLE_FILE_SYSTEM_PROTOCOL_REVISION;\r
77   Volume->VolumeInterface.OpenVolume  = FatOpenVolume;\r
78   InitializeListHead (&Volume->CheckRef);\r
79   InitializeListHead (&Volume->DirCacheList);\r
80   //\r
81   // Initialize Root Directory entry\r
82   //\r
83   Volume->RootDirEnt.FileString       = Volume->RootFileString;\r
84   Volume->RootDirEnt.Entry.Attributes = FAT_ATTRIBUTE_DIRECTORY;\r
85   //\r
86   // Check to see if there's a file system on the volume\r
87   //\r
88   Status = FatOpenDevice (Volume);\r
89   if (EFI_ERROR (Status)) {\r
90     goto Done;\r
91   }\r
92   //\r
93   // Initialize cache\r
94   //\r
95   Status = FatInitializeDiskCache (Volume);\r
96   if (EFI_ERROR (Status)) {\r
97     goto Done;\r
98   }\r
99   //\r
100   // Install our protocol interfaces on the device's handle\r
101   //\r
102   Status = gBS->InstallMultipleProtocolInterfaces (\r
103                   &Volume->Handle,\r
104                   &gEfiSimpleFileSystemProtocolGuid,\r
105                   &Volume->VolumeInterface,\r
106                   NULL\r
107                   );\r
108   if (EFI_ERROR (Status)) {\r
109     goto Done;\r
110   }\r
111   //\r
112   // Volume installed\r
113   //\r
114   DEBUG ((EFI_D_INIT, "Installed Fat filesystem on %p\n", Handle));\r
115   Volume->Valid = TRUE;\r
116 \r
117 Done:\r
118   //\r
119   // Unlock if locked by myself.\r
120   //\r
121   if (LockedByMe) {\r
122     FatReleaseLock ();\r
123   }\r
124 \r
125   if (EFI_ERROR (Status)) {\r
126     FatFreeVolume (Volume);\r
127   }\r
128 \r
129   return Status;\r
130 }\r
131 \r
132 EFI_STATUS\r
133 FatAbandonVolume (\r
134   IN FAT_VOLUME *Volume\r
135   )\r
136 /*++\r
137 \r
138 Routine Description:\r
139 \r
140   Called by FatDriverBindingStop(), Abandon the volume.\r
141 \r
142 Arguments:\r
143 \r
144   Volume                - The volume to be abandoned.\r
145 \r
146 Returns:\r
147 \r
148   EFI_SUCCESS           - Abandoned the volume successfully.\r
149   Others                - Can not uninstall the protocol interfaces.\r
150 \r
151 --*/\r
152 {\r
153   EFI_STATUS  Status;\r
154   BOOLEAN     LockedByMe;\r
155 \r
156   //\r
157   // Uninstall the protocol interface.\r
158   //\r
159   if (Volume->Handle != NULL) {\r
160     Status = gBS->UninstallMultipleProtocolInterfaces (\r
161                     Volume->Handle,\r
162                     &gEfiSimpleFileSystemProtocolGuid,\r
163                     &Volume->VolumeInterface,\r
164                     NULL\r
165                     );\r
166     if (EFI_ERROR (Status)) {\r
167       return Status;\r
168     }\r
169   }\r
170 \r
171   LockedByMe = FALSE;\r
172 \r
173   //\r
174   // Acquire the lock.\r
175   // If the caller has already acquired the lock (which\r
176   // means we are in the process of some Fat operation),\r
177   // we can not acquire again.\r
178   //\r
179   if (!FatIsLocked ()) {\r
180     LockedByMe = TRUE;\r
181     FatAcquireLock ();\r
182   }\r
183   //\r
184   // The volume is still being used. Hence, set error flag for all OFiles still in\r
185   // use. In two cases, we could get here. One is EFI_MEDIA_CHANGED, the other is\r
186   // EFI_NO_MEDIA.\r
187   //\r
188   if (Volume->Root != NULL) {\r
189     FatSetVolumeError (\r
190       Volume->Root,\r
191       Volume->BlockIo->Media->MediaPresent ? EFI_MEDIA_CHANGED : EFI_NO_MEDIA\r
192       );\r
193   }\r
194 \r
195   Volume->Valid = FALSE;\r
196 \r
197   //\r
198   // Release the lock.\r
199   // If locked by me, this means DriverBindingStop is NOT\r
200   // called within an on-going Fat operation, so we should\r
201   // take responsibility to cleanup and free the volume.\r
202   // Otherwise, the DriverBindingStop is called within an on-going\r
203   // Fat operation, we shouldn't check reference, so just let outer\r
204   // FatCleanupVolume do the task.\r
205   //\r
206   if (LockedByMe) {\r
207     FatCleanupVolume (Volume, NULL, EFI_SUCCESS);\r
208     FatReleaseLock ();\r
209   }\r
210 \r
211   return EFI_SUCCESS;\r
212 }\r
213 \r
214 EFI_STATUS\r
215 FatOpenDevice (\r
216   IN OUT FAT_VOLUME           *Volume\r
217   )\r
218 /*++\r
219 \r
220 Routine Description:\r
221 \r
222   Detects FAT file system on Disk and set relevant fields of Volume\r
223 \r
224 Arguments:\r
225 \r
226   Volume                - The volume structure.\r
227 \r
228 Returns:\r
229 \r
230   EFI_SUCCESS           - The Fat File System is detected successfully\r
231   EFI_UNSUPPORTED       - The volume is not FAT file system.\r
232   EFI_VOLUME_CORRUPTED  - The volume is corrupted.\r
233 \r
234 --*/\r
235 {\r
236   EFI_STATUS            Status;\r
237   UINT32                BlockSize;\r
238   UINT32                DirtyMask;\r
239   EFI_DISK_IO_PROTOCOL  *DiskIo;\r
240   FAT_BOOT_SECTOR       FatBs;\r
241   FAT_VOLUME_TYPE       FatType;\r
242   UINTN                 RootDirSectors;\r
243   UINTN                 FatLba;\r
244   UINTN                 RootLba;\r
245   UINTN                 FirstClusterLba;\r
246   UINTN                 Sectors;\r
247   UINTN                 SectorsPerFat;\r
248   UINT8                 SectorsPerClusterAlignment;\r
249   UINT8                 BlockAlignment;\r
250 \r
251   //\r
252   // Read the FAT_BOOT_SECTOR BPB info\r
253   // This is the only part of FAT code that uses parent DiskIo,\r
254   // Others use FatDiskIo which utilizes a Cache.\r
255   //\r
256   DiskIo  = Volume->DiskIo;\r
257   Status  = DiskIo->ReadDisk (DiskIo, Volume->MediaId, 0, sizeof (FatBs), &FatBs);\r
258 \r
259   if (EFI_ERROR (Status)) {\r
260     DEBUG ((EFI_D_INIT, "FatOpenDevice: read of part_lba failed %r\n", Status));\r
261     return Status;\r
262   }\r
263 \r
264   FatType = FatUndefined;\r
265 \r
266   //\r
267   // Use LargeSectors if Sectors is 0\r
268   //\r
269   Sectors = FatBs.FatBsb.Sectors;\r
270   if (Sectors == 0) {\r
271     Sectors = FatBs.FatBsb.LargeSectors;\r
272   }\r
273 \r
274   SectorsPerFat = FatBs.FatBsb.SectorsPerFat;\r
275   if (SectorsPerFat == 0) {\r
276     SectorsPerFat = FatBs.FatBse.Fat32Bse.LargeSectorsPerFat;\r
277     FatType       = FAT32;\r
278   }\r
279   //\r
280   // Is boot sector a fat sector?\r
281   // (Note that so far we only know if the sector is FAT32 or not, we don't\r
282   // know if the sector is Fat16 or Fat12 until later when we can compute\r
283   // the volume size)\r
284   //\r
285   if (FatBs.FatBsb.ReservedSectors == 0 || FatBs.FatBsb.NumFats == 0 || Sectors == 0) {\r
286     return EFI_UNSUPPORTED;\r
287   }\r
288 \r
289   if ((FatBs.FatBsb.SectorSize & (FatBs.FatBsb.SectorSize - 1)) != 0) {\r
290     return EFI_UNSUPPORTED;\r
291   }\r
292 \r
293   BlockAlignment = (UINT8) HighBitSet32 (FatBs.FatBsb.SectorSize);\r
294   if (BlockAlignment > MAX_BLOCK_ALIGNMENT || BlockAlignment < MIN_BLOCK_ALIGNMENT) {\r
295     return EFI_UNSUPPORTED;\r
296   }\r
297 \r
298   if ((FatBs.FatBsb.SectorsPerCluster & (FatBs.FatBsb.SectorsPerCluster - 1)) != 0) {\r
299     return EFI_UNSUPPORTED;\r
300   }\r
301 \r
302   SectorsPerClusterAlignment = (UINT8) HighBitSet32 (FatBs.FatBsb.SectorsPerCluster);\r
303   if (SectorsPerClusterAlignment > MAX_SECTORS_PER_CLUSTER_ALIGNMENT) {\r
304     return EFI_UNSUPPORTED;\r
305   }\r
306 \r
307   if (FatBs.FatBsb.Media <= 0xf7 &&\r
308       FatBs.FatBsb.Media != 0xf0 &&\r
309       FatBs.FatBsb.Media != 0x00 &&\r
310       FatBs.FatBsb.Media != 0x01\r
311       ) {\r
312     return EFI_UNSUPPORTED;\r
313   }\r
314   //\r
315   // Initialize fields the volume information for this FatType\r
316   //\r
317   if (FatType != FAT32) {\r
318     if (FatBs.FatBsb.RootEntries == 0) {\r
319       return EFI_UNSUPPORTED;\r
320     }\r
321     //\r
322     // Unpack fat12, fat16 info\r
323     //\r
324     Volume->RootEntries = FatBs.FatBsb.RootEntries;\r
325   } else {\r
326     //\r
327     // If this is fat32, refuse to mount mirror-disabled volumes\r
328     //\r
329     if ((SectorsPerFat == 0 || FatBs.FatBse.Fat32Bse.FsVersion != 0) || (FatBs.FatBse.Fat32Bse.ExtendedFlags & 0x80)) {\r
330       return EFI_UNSUPPORTED;\r
331     }\r
332     //\r
333     // Unpack fat32 info\r
334     //\r
335     Volume->RootCluster = FatBs.FatBse.Fat32Bse.RootDirFirstCluster;\r
336   }\r
337 \r
338   Volume->NumFats           = FatBs.FatBsb.NumFats;\r
339   //\r
340   // Compute some fat locations\r
341   //\r
342   BlockSize                 = FatBs.FatBsb.SectorSize;\r
343   RootDirSectors            = ((Volume->RootEntries * sizeof (FAT_DIRECTORY_ENTRY)) + (BlockSize - 1)) / BlockSize;\r
344 \r
345   FatLba                    = FatBs.FatBsb.ReservedSectors;\r
346   RootLba                   = FatBs.FatBsb.NumFats * SectorsPerFat + FatLba;\r
347   FirstClusterLba           = RootLba + RootDirSectors;\r
348 \r
349   Volume->FatPos            = FatLba * BlockSize;\r
350   Volume->FatSize           = SectorsPerFat * BlockSize;\r
351 \r
352   Volume->VolumeSize        = LShiftU64 (Sectors, BlockAlignment);\r
353   Volume->RootPos           = LShiftU64 (RootLba, BlockAlignment);\r
354   Volume->FirstClusterPos   = LShiftU64 (FirstClusterLba, BlockAlignment);\r
355   Volume->MaxCluster        = (Sectors - FirstClusterLba) >> SectorsPerClusterAlignment;\r
356   Volume->ClusterAlignment  = (UINT8)(BlockAlignment + SectorsPerClusterAlignment);\r
357   Volume->ClusterSize       = (UINTN)1 << (Volume->ClusterAlignment);\r
358 \r
359   //\r
360   // If this is not a fat32, determine if it's a fat16 or fat12\r
361   //\r
362   if (FatType != FAT32) {\r
363     if (Volume->MaxCluster >= FAT_MAX_FAT16_CLUSTER) {\r
364       return EFI_VOLUME_CORRUPTED;\r
365     }\r
366 \r
367     FatType = Volume->MaxCluster < FAT_MAX_FAT12_CLUSTER ? FAT12 : FAT16;\r
368     //\r
369     // fat12 & fat16 fat-entries are 2 bytes\r
370     //\r
371     Volume->FatEntrySize = sizeof (UINT16);\r
372     DirtyMask            = FAT16_DIRTY_MASK;\r
373   } else {\r
374     if (Volume->MaxCluster < FAT_MAX_FAT16_CLUSTER) {\r
375       return EFI_VOLUME_CORRUPTED;\r
376     }\r
377     //\r
378     // fat32 fat-entries are 4 bytes\r
379     //\r
380     Volume->FatEntrySize = sizeof (UINT32);\r
381     DirtyMask            = FAT32_DIRTY_MASK;\r
382   }\r
383   //\r
384   // Get the DirtyValue and NotDirtyValue\r
385   // We should keep the initial value as the NotDirtyValue\r
386   // in case the volume is dirty already\r
387   //\r
388   if (FatType != FAT12) {\r
389     Status = FatAccessVolumeDirty (Volume, READ_DISK, &Volume->NotDirtyValue);\r
390     if (EFI_ERROR (Status)) {\r
391       return Status;\r
392     }\r
393 \r
394     Volume->DirtyValue = Volume->NotDirtyValue & DirtyMask;\r
395   }\r
396   //\r
397   // If present, read the fat hint info\r
398   //\r
399   if (FatType == FAT32) {\r
400     Volume->FreeInfoPos = FatBs.FatBse.Fat32Bse.FsInfoSector * BlockSize;\r
401     if (FatBs.FatBse.Fat32Bse.FsInfoSector != 0) {\r
402       FatDiskIo (Volume, READ_DISK, Volume->FreeInfoPos, sizeof (FAT_INFO_SECTOR), &Volume->FatInfoSector);\r
403       if (Volume->FatInfoSector.Signature == FAT_INFO_SIGNATURE &&\r
404           Volume->FatInfoSector.InfoBeginSignature == FAT_INFO_BEGIN_SIGNATURE &&\r
405           Volume->FatInfoSector.InfoEndSignature == FAT_INFO_END_SIGNATURE &&\r
406           Volume->FatInfoSector.FreeInfo.ClusterCount <= Volume->MaxCluster\r
407           ) {\r
408         Volume->FreeInfoValid = TRUE;\r
409       }\r
410     }\r
411   }\r
412   //\r
413   // Just make up a FreeInfo.NextCluster for use by allocate cluster\r
414   //\r
415   if (FAT_MIN_CLUSTER > Volume->FatInfoSector.FreeInfo.NextCluster ||\r
416      Volume->FatInfoSector.FreeInfo.NextCluster > Volume->MaxCluster + 1\r
417      ) {\r
418     Volume->FatInfoSector.FreeInfo.NextCluster = FAT_MIN_CLUSTER;\r
419   }\r
420   //\r
421   // We are now defining FAT Type\r
422   //\r
423   Volume->FatType = FatType;\r
424   ASSERT (FatType != FatUndefined);\r
425 \r
426   return EFI_SUCCESS;\r
427 }\r