-/*++\r
+/** @file\r
+ Miscellaneous functions.\r
\r
-Copyright (c) 2005 - 2010, Intel Corporation. All rights reserved.<BR>\r
+Copyright (c) 2005 - 2018, Intel Corporation. All rights reserved.<BR>\r
This program and the accompanying materials are licensed and made available\r
under the terms and conditions of the BSD License which accompanies this\r
distribution. The full text of the license may be found at\r
WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
\r
\r
-Module Name:\r
+**/\r
\r
- Misc.c\r
+#include "Fat.h"\r
+UINT8 mMonthDays[] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };\r
\r
-Abstract:\r
+/**\r
\r
- Miscellaneous functions\r
+ Create the task\r
\r
-Revision History\r
+ @param IFile - The instance of the open file.\r
+ @param Token - A pointer to the token associated with the transaction.\r
\r
---*/\r
+ @return FAT_TASK * - Return the task instance.\r
\r
-#include "Fat.h"\r
+**/\r
+FAT_TASK *\r
+FatCreateTask (\r
+ FAT_IFILE *IFile,\r
+ EFI_FILE_IO_TOKEN *Token\r
+ )\r
+{\r
+ FAT_TASK *Task;\r
+\r
+ Task = AllocateZeroPool (sizeof (*Task));\r
+ if (Task != NULL) {\r
+ Task->Signature = FAT_TASK_SIGNATURE;\r
+ Task->IFile = IFile;\r
+ Task->FileIoToken = Token;\r
+ InitializeListHead (&Task->Subtasks);\r
+ InitializeListHead (&Task->Link);\r
+ }\r
+ return Task;\r
+}\r
+\r
+/**\r
+\r
+ Destroy the task.\r
+\r
+ @param Task - The task to be destroyed.\r
+\r
+**/\r
+VOID\r
+FatDestroyTask (\r
+ FAT_TASK *Task\r
+ )\r
+{\r
+ LIST_ENTRY *Link;\r
+ FAT_SUBTASK *Subtask;\r
+\r
+ Link = GetFirstNode (&Task->Subtasks);\r
+ while (!IsNull (&Task->Subtasks, Link)) {\r
+ Subtask = CR (Link, FAT_SUBTASK, Link, FAT_SUBTASK_SIGNATURE);\r
+ Link = FatDestroySubtask (Subtask);\r
+ }\r
+ FreePool (Task);\r
+}\r
+\r
+/**\r
+\r
+ Wait all non-blocking requests complete.\r
+\r
+ @param IFile - The instance of the open file.\r
+\r
+**/\r
+VOID\r
+FatWaitNonblockingTask (\r
+ FAT_IFILE *IFile\r
+ )\r
+{\r
+ BOOLEAN TaskQueueEmpty;\r
+\r
+ do {\r
+ EfiAcquireLock (&FatTaskLock);\r
+ TaskQueueEmpty = IsListEmpty (&IFile->Tasks);\r
+ EfiReleaseLock (&FatTaskLock);\r
+ } while (!TaskQueueEmpty);\r
+}\r
+\r
+/**\r
+\r
+ Remove the subtask from subtask list.\r
\r
+ @param Subtask - The subtask to be removed.\r
+\r
+ @return LIST_ENTRY * - The next node in the list.\r
+\r
+**/\r
+LIST_ENTRY *\r
+FatDestroySubtask (\r
+ FAT_SUBTASK *Subtask\r
+ )\r
+{\r
+ LIST_ENTRY *Link;\r
+\r
+ gBS->CloseEvent (Subtask->DiskIo2Token.Event);\r
+\r
+ Link = RemoveEntryList (&Subtask->Link);\r
+ FreePool (Subtask);\r
+\r
+ return Link;\r
+}\r
+\r
+/**\r
+\r
+ Execute the task.\r
+\r
+ @param IFile - The instance of the open file.\r
+ @param Task - The task to be executed.\r
+\r
+ @retval EFI_SUCCESS - The task was executed sucessfully.\r
+ @return other - An error occurred when executing the task.\r
+\r
+**/\r
EFI_STATUS\r
-FatAccessVolumeDirty (\r
- IN FAT_VOLUME *Volume,\r
- IN IO_MODE IoMode,\r
- IN VOID *DirtyValue\r
+FatQueueTask (\r
+ IN FAT_IFILE *IFile,\r
+ IN FAT_TASK *Task\r
)\r
-/*++\r
+{\r
+ EFI_STATUS Status;\r
+ LIST_ENTRY *Link;\r
+ LIST_ENTRY *NextLink;\r
+ FAT_SUBTASK *Subtask;\r
+\r
+ //\r
+ // Sometimes the Task doesn't contain any subtasks, signal the event directly.\r
+ //\r
+ if (IsListEmpty (&Task->Subtasks)) {\r
+ Task->FileIoToken->Status = EFI_SUCCESS;\r
+ gBS->SignalEvent (Task->FileIoToken->Event);\r
+ FreePool (Task);\r
+ return EFI_SUCCESS;\r
+ }\r
+\r
+ EfiAcquireLock (&FatTaskLock);\r
+ InsertTailList (&IFile->Tasks, &Task->Link);\r
+ EfiReleaseLock (&FatTaskLock);\r
+\r
+ Status = EFI_SUCCESS;\r
+ //\r
+ // Use NextLink to store the next link of the list, because Link might be remove from the\r
+ // doubly-linked list and get freed in the end of current loop.\r
+ //\r
+ // Also, list operation APIs like IsNull() and GetNextNode() are avoided during the loop, since\r
+ // they may check the validity of doubly-linked lists by traversing them. These APIs cannot\r
+ // handle list elements being removed during the traverse.\r
+ //\r
+ for ( Link = GetFirstNode (&Task->Subtasks), NextLink = GetNextNode (&Task->Subtasks, Link)\r
+ ; Link != &Task->Subtasks\r
+ ; Link = NextLink, NextLink = Link->ForwardLink\r
+ ) {\r
+ Subtask = CR (Link, FAT_SUBTASK, Link, FAT_SUBTASK_SIGNATURE);\r
+ if (Subtask->Write) {\r
+\r
+ Status = IFile->OFile->Volume->DiskIo2->WriteDiskEx (\r
+ IFile->OFile->Volume->DiskIo2,\r
+ IFile->OFile->Volume->MediaId,\r
+ Subtask->Offset,\r
+ &Subtask->DiskIo2Token,\r
+ Subtask->BufferSize,\r
+ Subtask->Buffer\r
+ );\r
+ } else {\r
+ Status = IFile->OFile->Volume->DiskIo2->ReadDiskEx (\r
+ IFile->OFile->Volume->DiskIo2,\r
+ IFile->OFile->Volume->MediaId,\r
+ Subtask->Offset,\r
+ &Subtask->DiskIo2Token,\r
+ Subtask->BufferSize,\r
+ Subtask->Buffer\r
+ );\r
+ }\r
+ if (EFI_ERROR (Status)) {\r
+ break;\r
+ }\r
+ }\r
\r
-Routine Description:\r
+ if (EFI_ERROR (Status)) {\r
+ EfiAcquireLock (&FatTaskLock);\r
+ //\r
+ // Remove all the remaining subtasks when failure.\r
+ // We shouldn't remove all the tasks because the non-blocking requests have\r
+ // been submitted and cannot be canceled.\r
+ //\r
+ while (!IsNull (&Task->Subtasks, Link)) {\r
+ Subtask = CR (Link, FAT_SUBTASK, Link, FAT_SUBTASK_SIGNATURE);\r
+ Link = FatDestroySubtask (Subtask);\r
+ }\r
\r
- Set the volume as dirty or not\r
+ if (IsListEmpty (&Task->Subtasks)) {\r
+ RemoveEntryList (&Task->Link);\r
+ FreePool (Task);\r
+ } else {\r
+ //\r
+ // If one or more subtasks have been already submitted, set FileIoToken\r
+ // to NULL so that the callback won't signal the event.\r
+ //\r
+ Task->FileIoToken = NULL;\r
+ }\r
\r
-Arguments:\r
+ EfiReleaseLock (&FatTaskLock);\r
+ }\r
\r
- Volume - FAT file system volume.\r
- IoMode - The access mode.\r
- DirtyValue - Set the volume as dirty or not.\r
+ return Status;\r
+}\r
+\r
+/**\r
+\r
+ Set the volume as dirty or not.\r
\r
-Returns:\r
+ @param Volume - FAT file system volume.\r
+ @param IoMode - The access mode.\r
+ @param DirtyValue - Set the volume as dirty or not.\r
\r
- EFI_SUCCESS - Set the new FAT entry value sucessfully.\r
- other - An error occurred when operation the FAT entries.\r
+ @retval EFI_SUCCESS - Set the new FAT entry value sucessfully.\r
+ @return other - An error occurred when operation the FAT entries.\r
\r
---*/\r
+**/\r
+EFI_STATUS\r
+FatAccessVolumeDirty (\r
+ IN FAT_VOLUME *Volume,\r
+ IN IO_MODE IoMode,\r
+ IN VOID *DirtyValue\r
+ )\r
{\r
UINTN WriteCount;\r
\r
WriteCount = Volume->FatEntrySize;\r
- return FatDiskIo (Volume, IoMode, Volume->FatPos + WriteCount, WriteCount, DirtyValue);\r
+ return FatDiskIo (Volume, IoMode, Volume->FatPos + WriteCount, WriteCount, DirtyValue, NULL);\r
}\r
\r
-EFI_STATUS\r
-FatDiskIo (\r
- IN FAT_VOLUME *Volume,\r
- IN IO_MODE IoMode,\r
- IN UINT64 Offset,\r
- IN UINTN BufferSize,\r
- IN OUT VOID *Buffer\r
+/**\r
+ Invoke a notification event.\r
+\r
+ @param Event Event whose notification function is being invoked.\r
+ @param Context The pointer to the notification function's context,\r
+ which is implementation-dependent.\r
+\r
+**/\r
+VOID\r
+EFIAPI\r
+FatOnAccessComplete (\r
+ IN EFI_EVENT Event,\r
+ IN VOID *Context\r
)\r
-/*++\r
+{\r
+ EFI_STATUS Status;\r
+ FAT_SUBTASK *Subtask;\r
+ FAT_TASK *Task;\r
\r
-Routine Description:\r
+ //\r
+ // Avoid someone in future breaks the below assumption.\r
+ //\r
+ ASSERT (EfiGetCurrentTpl () == FatTaskLock.Tpl);\r
\r
- General disk access function\r
+ Subtask = (FAT_SUBTASK *) Context;\r
+ Task = Subtask->Task;\r
+ Status = Subtask->DiskIo2Token.TransactionStatus;\r
\r
-Arguments:\r
+ ASSERT (Task->Signature == FAT_TASK_SIGNATURE);\r
+ ASSERT (Subtask->Signature == FAT_SUBTASK_SIGNATURE);\r
\r
- Volume - FAT file system volume.\r
- IoMode - The access mode (disk read/write or cache access).\r
- Offset - The starting byte offset to read from.\r
- BufferSize - Size of Buffer.\r
- Buffer - Buffer containing read data.\r
+ //\r
+ // Remove the task unconditionally\r
+ //\r
+ FatDestroySubtask (Subtask);\r
\r
-Returns:\r
+ //\r
+ // Task->FileIoToken is NULL which means the task will be ignored (just recycle the subtask and task memory).\r
+ //\r
+ if (Task->FileIoToken != NULL) {\r
+ if (IsListEmpty (&Task->Subtasks) || EFI_ERROR (Status)) {\r
+ Task->FileIoToken->Status = Status;\r
+ gBS->SignalEvent (Task->FileIoToken->Event);\r
+ //\r
+ // Mark Task->FileIoToken to NULL so that the subtasks belonging to the task will be ignored.\r
+ //\r
+ Task->FileIoToken = NULL;\r
+ }\r
+ }\r
+\r
+ if (IsListEmpty (&Task->Subtasks)) {\r
+ RemoveEntryList (&Task->Link);\r
+ FreePool (Task);\r
+ }\r
+}\r
+\r
+/**\r
\r
- EFI_SUCCESS - The operation is performed successfully.\r
- EFI_VOLUME_CORRUPTED - The accesss is\r
- Others - The status of read/write the disk\r
+ General disk access function.\r
\r
---*/\r
+ @param Volume - FAT file system volume.\r
+ @param IoMode - The access mode (disk read/write or cache access).\r
+ @param Offset - The starting byte offset to read from.\r
+ @param BufferSize - Size of Buffer.\r
+ @param Buffer - Buffer containing read data.\r
+ @param Task point to task instance.\r
+\r
+ @retval EFI_SUCCESS - The operation is performed successfully.\r
+ @retval EFI_VOLUME_CORRUPTED - The accesss is\r
+ @return Others - The status of read/write the disk\r
+\r
+**/\r
+EFI_STATUS\r
+FatDiskIo (\r
+ IN FAT_VOLUME *Volume,\r
+ IN IO_MODE IoMode,\r
+ IN UINT64 Offset,\r
+ IN UINTN BufferSize,\r
+ IN OUT VOID *Buffer,\r
+ IN FAT_TASK *Task\r
+ )\r
{\r
EFI_STATUS Status;\r
EFI_DISK_IO_PROTOCOL *DiskIo;\r
EFI_DISK_READ IoFunction;\r
+ FAT_SUBTASK *Subtask;\r
\r
//\r
// Verify the IO is in devices range\r
//\r
// Access cache\r
//\r
- Status = FatAccessCache (Volume, CACHE_TYPE (IoMode), RAW_ACCESS (IoMode), Offset, BufferSize, Buffer);\r
+ Status = FatAccessCache (Volume, CACHE_TYPE (IoMode), RAW_ACCESS (IoMode), Offset, BufferSize, Buffer, Task);\r
} else {\r
//\r
// Access disk directly\r
//\r
- DiskIo = Volume->DiskIo;\r
- IoFunction = (IoMode == READ_DISK) ? DiskIo->ReadDisk : DiskIo->WriteDisk;\r
- Status = IoFunction (DiskIo, Volume->MediaId, Offset, BufferSize, Buffer);\r
+ if (Task == NULL) {\r
+ //\r
+ // Blocking access\r
+ //\r
+ DiskIo = Volume->DiskIo;\r
+ IoFunction = (IoMode == ReadDisk) ? DiskIo->ReadDisk : DiskIo->WriteDisk;\r
+ Status = IoFunction (DiskIo, Volume->MediaId, Offset, BufferSize, Buffer);\r
+ } else {\r
+ //\r
+ // Non-blocking access\r
+ //\r
+ Subtask = AllocateZeroPool (sizeof (*Subtask));\r
+ if (Subtask == NULL) {\r
+ Status = EFI_OUT_OF_RESOURCES;\r
+ } else {\r
+ Subtask->Signature = FAT_SUBTASK_SIGNATURE;\r
+ Subtask->Task = Task;\r
+ Subtask->Write = (BOOLEAN) (IoMode == WriteDisk);\r
+ Subtask->Offset = Offset;\r
+ Subtask->Buffer = Buffer;\r
+ Subtask->BufferSize = BufferSize;\r
+ Status = gBS->CreateEvent (\r
+ EVT_NOTIFY_SIGNAL,\r
+ TPL_NOTIFY,\r
+ FatOnAccessComplete,\r
+ Subtask,\r
+ &Subtask->DiskIo2Token.Event\r
+ );\r
+ if (!EFI_ERROR (Status)) {\r
+ InsertTailList (&Task->Subtasks, &Subtask->Link);\r
+ } else {\r
+ FreePool (Subtask);\r
+ }\r
+ }\r
+ }\r
}\r
}\r
\r
if (EFI_ERROR (Status)) {\r
Volume->DiskError = TRUE;\r
- DEBUG ((EFI_D_INFO, "FatDiskIo: error %r\n", Status));\r
+ DEBUG ((EFI_D_ERROR, "FatDiskIo: error %r\n", Status));\r
}\r
\r
return Status;\r
}\r
\r
+/**\r
+\r
+ Lock the volume.\r
+\r
+**/\r
VOID\r
FatAcquireLock (\r
VOID\r
)\r
-/*++\r
-\r
-Routine Description:\r
-\r
- Lock the volume.\r
-\r
-Arguments:\r
-\r
- None.\r
-\r
-Returns:\r
-\r
- None.\r
-\r
---*/\r
{\r
EfiAcquireLock (&FatFsLock);\r
}\r
\r
-EFI_STATUS\r
-FatAcquireLockOrFail (\r
- VOID\r
- )\r
-/*++\r
-\r
-Routine Description:\r
+/**\r
\r
Lock the volume.\r
If the lock is already in the acquired state, then EFI_ACCESS_DENIED is returned.\r
Otherwise, EFI_SUCCESS is returned.\r
\r
-Arguments:\r
-\r
- None.\r
-\r
-Returns:\r
+ @retval EFI_SUCCESS - The volume is locked.\r
+ @retval EFI_ACCESS_DENIED - The volume could not be locked because it is already locked.\r
\r
- EFI_SUCCESS - The volume is locked.\r
- EFI_ACCESS_DENIED - The volume could not be locked because it is already locked.\r
-\r
---*/\r
+**/\r
+EFI_STATUS\r
+FatAcquireLockOrFail (\r
+ VOID\r
+ )\r
{\r
return EfiAcquireLockOrFail (&FatFsLock);\r
}\r
\r
+/**\r
+\r
+ Unlock the volume.\r
+\r
+**/\r
VOID\r
FatReleaseLock (\r
VOID\r
)\r
-/*++\r
-\r
-Routine Description:\r
-\r
- Unlock the volume.\r
-\r
-Arguments:\r
-\r
- Null.\r
-\r
-Returns:\r
-\r
- None.\r
-\r
---*/\r
{\r
EfiReleaseLock (&FatFsLock);\r
}\r
\r
-VOID\r
-FatFreeDirEnt (\r
- IN FAT_DIRENT *DirEnt\r
- )\r
-/*++\r
-\r
-Routine Description:\r
+/**\r
\r
Free directory entry.\r
\r
-Arguments:\r
-\r
- DirEnt - The directory entry to be freed.\r
-\r
-Returns:\r
-\r
- None.\r
+ @param DirEnt - The directory entry to be freed.\r
\r
---*/\r
+**/\r
+VOID\r
+FatFreeDirEnt (\r
+ IN FAT_DIRENT *DirEnt\r
+ )\r
{\r
if (DirEnt->FileString != NULL) {\r
FreePool (DirEnt->FileString);\r
FreePool (DirEnt);\r
}\r
\r
-VOID\r
-FatFreeVolume (\r
- IN FAT_VOLUME *Volume\r
- )\r
-/*++\r
-\r
-Routine Description:\r
+/**\r
\r
Free volume structure (including the contents of directory cache and disk cache).\r
\r
-Arguments:\r
+ @param Volume - The volume structure to be freed.\r
\r
- Volume - The volume structure to be freed.\r
-\r
-Returns:\r
-\r
- None.\r
-\r
---*/\r
+**/\r
+VOID\r
+FatFreeVolume (\r
+ IN FAT_VOLUME *Volume\r
+ )\r
{\r
//\r
// Free disk cache\r
FreePool (Volume);\r
}\r
\r
+/**\r
+\r
+ Translate EFI time to FAT time.\r
+\r
+ @param ETime - The time of EFI_TIME.\r
+ @param FTime - The time of FAT_DATE_TIME.\r
+\r
+**/\r
VOID\r
FatEfiTimeToFatTime (\r
IN EFI_TIME *ETime,\r
OUT FAT_DATE_TIME *FTime\r
)\r
-/*++\r
-\r
-Routine Description:\r
-\r
- Translate EFI time to FAT time.\r
-\r
-Arguments:\r
-\r
- ETime - The time of EFI_TIME.\r
- FTime - The time of FAT_DATE_TIME.\r
-\r
-Returns:\r
-\r
- None.\r
-\r
---*/\r
{\r
//\r
// ignores timezone info in source ETime\r
FTime->Time.DoubleSecond = (UINT16) (ETime->Second / 2);\r
}\r
\r
+/**\r
+\r
+ Translate Fat time to EFI time.\r
+\r
+ @param FTime - The time of FAT_DATE_TIME.\r
+ @param ETime - The time of EFI_TIME..\r
+\r
+**/\r
VOID\r
FatFatTimeToEfiTime (\r
IN FAT_DATE_TIME *FTime,\r
OUT EFI_TIME *ETime\r
)\r
-/*++\r
-\r
-Routine Description:\r
-\r
- Translate Fat time to EFI time.\r
-\r
-Arguments:\r
-\r
- FTime - The time of FAT_DATE_TIME.\r
- ETime - The time of EFI_TIME.\r
-\r
-Returns:\r
-\r
- None.\r
-\r
---*/\r
{\r
ETime->Year = (UINT16) (FTime->Date.Year + 1980);\r
ETime->Month = (UINT8) FTime->Date.Month;\r
ETime->Daylight = 0;\r
}\r
\r
-VOID\r
-FatGetCurrentFatTime (\r
- OUT FAT_DATE_TIME *FatNow\r
- )\r
-/*++\r
-\r
-Routine Description:\r
+/**\r
\r
Get Current FAT time.\r
\r
-Arguments:\r
-\r
- FatNow - Current FAT time.\r
-\r
-Returns:\r
+ @param FatNow - Current FAT time.\r
\r
- None.\r
-\r
---*/\r
+**/\r
+VOID\r
+FatGetCurrentFatTime (\r
+ OUT FAT_DATE_TIME *FatNow\r
+ )\r
{\r
EFI_STATUS Status;\r
EFI_TIME Now;\r
}\r
}\r
\r
-BOOLEAN\r
-FatIsValidTime (\r
- IN EFI_TIME *Time\r
- )\r
-/*++\r
-\r
-Routine Description:\r
+/**\r
\r
Check whether a time is valid.\r
\r
-Arguments:\r
+ @param Time - The time of EFI_TIME.\r
\r
- Time - The time of EFI_TIME.\r
+ @retval TRUE - The time is valid.\r
+ @retval FALSE - The time is not valid.\r
\r
-Returns:\r
-\r
- TRUE - The time is valid.\r
- FALSE - The time is not valid.\r
-\r
---*/\r
+**/\r
+BOOLEAN\r
+FatIsValidTime (\r
+ IN EFI_TIME *Time\r
+ )\r
{\r
- static UINT8 MonthDays[] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };\r
UINTN Day;\r
BOOLEAN ValidTime;\r
\r
//\r
// Perform a more specific check of the day of the month\r
//\r
- Day = MonthDays[Time->Month - 1];\r
+ Day = mMonthDays[Time->Month - 1];\r
if (Time->Month == 2 && IS_LEAP_YEAR (Time->Year)) {\r
Day += 1;\r
//\r