| /** @file | |
| DiskIo driver that lays on every BlockIo protocol in the system. | |
| DiskIo converts a block oriented device to a byte oriented device. | |
| Disk access may have to handle unaligned request about sector boundaries. | |
| There are three cases: | |
| UnderRun - The first byte is not on a sector boundary or the read request is | |
| less than a sector in length. | |
| Aligned - A read of N contiguous sectors. | |
| OverRun - The last byte is not on a sector boundary. | |
| Copyright (c) 2006 - 2016, Intel Corporation. All rights reserved.<BR> | |
| This program and the accompanying materials | |
| are licensed and made available under the terms and conditions of the BSD License | |
| which accompanies this distribution. The full text of the license may be found at | |
| http://opensource.org/licenses/bsd-license.php | |
| THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, | |
| WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. | |
| **/ | |
| #include "DiskIo.h" | |
| // | |
| // Driver binding protocol implementation for DiskIo driver. | |
| // | |
| EFI_DRIVER_BINDING_PROTOCOL gDiskIoDriverBinding = { | |
| DiskIoDriverBindingSupported, | |
| DiskIoDriverBindingStart, | |
| DiskIoDriverBindingStop, | |
| 0xa, | |
| NULL, | |
| NULL | |
| }; | |
| // | |
| // Template for DiskIo private data structure. | |
| // The pointer to BlockIo protocol interface is assigned dynamically. | |
| // | |
| DISK_IO_PRIVATE_DATA gDiskIoPrivateDataTemplate = { | |
| DISK_IO_PRIVATE_DATA_SIGNATURE, | |
| { | |
| EFI_DISK_IO_PROTOCOL_REVISION, | |
| DiskIoReadDisk, | |
| DiskIoWriteDisk | |
| }, | |
| { | |
| EFI_DISK_IO2_PROTOCOL_REVISION, | |
| DiskIo2Cancel, | |
| DiskIo2ReadDiskEx, | |
| DiskIo2WriteDiskEx, | |
| DiskIo2FlushDiskEx | |
| } | |
| }; | |
| /** | |
| Test to see if this driver supports ControllerHandle. | |
| @param This Protocol instance pointer. | |
| @param ControllerHandle Handle of device to test | |
| @param RemainingDevicePath Optional parameter use to pick a specific child | |
| device to start. | |
| @retval EFI_SUCCESS This driver supports this device | |
| @retval EFI_ALREADY_STARTED This driver is already running on this device | |
| @retval other This driver does not support this device | |
| **/ | |
| EFI_STATUS | |
| EFIAPI | |
| DiskIoDriverBindingSupported ( | |
| IN EFI_DRIVER_BINDING_PROTOCOL *This, | |
| IN EFI_HANDLE ControllerHandle, | |
| IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath OPTIONAL | |
| ) | |
| { | |
| EFI_STATUS Status; | |
| EFI_BLOCK_IO_PROTOCOL *BlockIo; | |
| // | |
| // Open the IO Abstraction(s) needed to perform the supported test. | |
| // | |
| Status = gBS->OpenProtocol ( | |
| ControllerHandle, | |
| &gEfiBlockIoProtocolGuid, | |
| (VOID **) &BlockIo, | |
| This->DriverBindingHandle, | |
| ControllerHandle, | |
| EFI_OPEN_PROTOCOL_BY_DRIVER | |
| ); | |
| if (EFI_ERROR (Status)) { | |
| return Status; | |
| } | |
| // | |
| // Close the I/O Abstraction(s) used to perform the supported test. | |
| // | |
| gBS->CloseProtocol ( | |
| ControllerHandle, | |
| &gEfiBlockIoProtocolGuid, | |
| This->DriverBindingHandle, | |
| ControllerHandle | |
| ); | |
| return EFI_SUCCESS; | |
| } | |
| /** | |
| Start this driver on ControllerHandle by opening a Block IO protocol and | |
| installing a Disk IO protocol on ControllerHandle. | |
| @param This Protocol instance pointer. | |
| @param ControllerHandle Handle of device to bind driver to | |
| @param RemainingDevicePath Optional parameter use to pick a specific child | |
| device to start. | |
| @retval EFI_SUCCESS This driver is added to ControllerHandle | |
| @retval EFI_ALREADY_STARTED This driver is already running on ControllerHandle | |
| @retval other This driver does not support this device | |
| **/ | |
| EFI_STATUS | |
| EFIAPI | |
| DiskIoDriverBindingStart ( | |
| IN EFI_DRIVER_BINDING_PROTOCOL *This, | |
| IN EFI_HANDLE ControllerHandle, | |
| IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath OPTIONAL | |
| ) | |
| { | |
| EFI_STATUS Status; | |
| DISK_IO_PRIVATE_DATA *Instance; | |
| EFI_TPL OldTpl; | |
| Instance = NULL; | |
| OldTpl = gBS->RaiseTPL (TPL_CALLBACK); | |
| // | |
| // Connect to the Block IO and Block IO2 interface on ControllerHandle. | |
| // | |
| Status = gBS->OpenProtocol ( | |
| ControllerHandle, | |
| &gEfiBlockIoProtocolGuid, | |
| (VOID **) &gDiskIoPrivateDataTemplate.BlockIo, | |
| This->DriverBindingHandle, | |
| ControllerHandle, | |
| EFI_OPEN_PROTOCOL_BY_DRIVER | |
| ); | |
| if (EFI_ERROR (Status)) { | |
| goto ErrorExit1; | |
| } | |
| Status = gBS->OpenProtocol ( | |
| ControllerHandle, | |
| &gEfiBlockIo2ProtocolGuid, | |
| (VOID **) &gDiskIoPrivateDataTemplate.BlockIo2, | |
| This->DriverBindingHandle, | |
| ControllerHandle, | |
| EFI_OPEN_PROTOCOL_BY_DRIVER | |
| ); | |
| if (EFI_ERROR (Status)) { | |
| gDiskIoPrivateDataTemplate.BlockIo2 = NULL; | |
| } | |
| // | |
| // Initialize the Disk IO device instance. | |
| // | |
| Instance = AllocateCopyPool (sizeof (DISK_IO_PRIVATE_DATA), &gDiskIoPrivateDataTemplate); | |
| if (Instance == NULL) { | |
| Status = EFI_OUT_OF_RESOURCES; | |
| goto ErrorExit; | |
| } | |
| // | |
| // The BlockSize and IoAlign of BlockIo and BlockIo2 should equal. | |
| // | |
| ASSERT ((Instance->BlockIo2 == NULL) || | |
| ((Instance->BlockIo->Media->IoAlign == Instance->BlockIo2->Media->IoAlign) && | |
| (Instance->BlockIo->Media->BlockSize == Instance->BlockIo2->Media->BlockSize) | |
| )); | |
| InitializeListHead (&Instance->TaskQueue); | |
| EfiInitializeLock (&Instance->TaskQueueLock, TPL_NOTIFY); | |
| Instance->SharedWorkingBuffer = AllocateAlignedPages ( | |
| EFI_SIZE_TO_PAGES (PcdGet32 (PcdDiskIoDataBufferBlockNum) * Instance->BlockIo->Media->BlockSize), | |
| Instance->BlockIo->Media->IoAlign | |
| ); | |
| if (Instance->SharedWorkingBuffer == NULL) { | |
| Status = EFI_OUT_OF_RESOURCES; | |
| goto ErrorExit; | |
| } | |
| // | |
| // Install protocol interfaces for the Disk IO device. | |
| // | |
| if (Instance->BlockIo2 != NULL) { | |
| Status = gBS->InstallMultipleProtocolInterfaces ( | |
| &ControllerHandle, | |
| &gEfiDiskIoProtocolGuid, &Instance->DiskIo, | |
| &gEfiDiskIo2ProtocolGuid, &Instance->DiskIo2, | |
| NULL | |
| ); | |
| } else { | |
| Status = gBS->InstallMultipleProtocolInterfaces ( | |
| &ControllerHandle, | |
| &gEfiDiskIoProtocolGuid, &Instance->DiskIo, | |
| NULL | |
| ); | |
| } | |
| ErrorExit: | |
| if (EFI_ERROR (Status)) { | |
| if (Instance != NULL && Instance->SharedWorkingBuffer != NULL) { | |
| FreeAlignedPages ( | |
| Instance->SharedWorkingBuffer, | |
| EFI_SIZE_TO_PAGES (PcdGet32 (PcdDiskIoDataBufferBlockNum) * Instance->BlockIo->Media->BlockSize) | |
| ); | |
| } | |
| if (Instance != NULL) { | |
| FreePool (Instance); | |
| } | |
| gBS->CloseProtocol ( | |
| ControllerHandle, | |
| &gEfiBlockIoProtocolGuid, | |
| This->DriverBindingHandle, | |
| ControllerHandle | |
| ); | |
| } | |
| ErrorExit1: | |
| gBS->RestoreTPL (OldTpl); | |
| return Status; | |
| } | |
| /** | |
| Stop this driver on ControllerHandle by removing Disk IO protocol and closing | |
| the Block IO protocol on ControllerHandle. | |
| @param This Protocol instance pointer. | |
| @param ControllerHandle Handle of device to stop driver on | |
| @param NumberOfChildren Number of Handles in ChildHandleBuffer. If number of | |
| children is zero stop the entire bus driver. | |
| @param ChildHandleBuffer List of Child Handles to Stop. | |
| @retval EFI_SUCCESS This driver is removed ControllerHandle | |
| @retval other This driver was not removed from this device | |
| **/ | |
| EFI_STATUS | |
| EFIAPI | |
| DiskIoDriverBindingStop ( | |
| IN EFI_DRIVER_BINDING_PROTOCOL *This, | |
| IN EFI_HANDLE ControllerHandle, | |
| IN UINTN NumberOfChildren, | |
| IN EFI_HANDLE *ChildHandleBuffer | |
| ) | |
| { | |
| EFI_STATUS Status; | |
| EFI_DISK_IO_PROTOCOL *DiskIo; | |
| EFI_DISK_IO2_PROTOCOL *DiskIo2; | |
| DISK_IO_PRIVATE_DATA *Instance; | |
| BOOLEAN AllTaskDone; | |
| // | |
| // Get our context back. | |
| // | |
| Status = gBS->OpenProtocol ( | |
| ControllerHandle, | |
| &gEfiDiskIoProtocolGuid, | |
| (VOID **) &DiskIo, | |
| This->DriverBindingHandle, | |
| ControllerHandle, | |
| EFI_OPEN_PROTOCOL_GET_PROTOCOL | |
| ); | |
| if (EFI_ERROR (Status)) { | |
| return Status; | |
| } | |
| Status = gBS->OpenProtocol ( | |
| ControllerHandle, | |
| &gEfiDiskIo2ProtocolGuid, | |
| (VOID **) &DiskIo2, | |
| This->DriverBindingHandle, | |
| ControllerHandle, | |
| EFI_OPEN_PROTOCOL_GET_PROTOCOL | |
| ); | |
| if (EFI_ERROR (Status)) { | |
| DiskIo2 = NULL; | |
| } | |
| Instance = DISK_IO_PRIVATE_DATA_FROM_DISK_IO (DiskIo); | |
| if (DiskIo2 != NULL) { | |
| // | |
| // Call BlockIo2::Reset() to terminate any in-flight non-blocking I/O requests | |
| // | |
| ASSERT (Instance->BlockIo2 != NULL); | |
| Status = Instance->BlockIo2->Reset (Instance->BlockIo2, FALSE); | |
| if (EFI_ERROR (Status)) { | |
| return Status; | |
| } | |
| Status = gBS->UninstallMultipleProtocolInterfaces ( | |
| ControllerHandle, | |
| &gEfiDiskIoProtocolGuid, &Instance->DiskIo, | |
| &gEfiDiskIo2ProtocolGuid, &Instance->DiskIo2, | |
| NULL | |
| ); | |
| } else { | |
| Status = gBS->UninstallMultipleProtocolInterfaces ( | |
| ControllerHandle, | |
| &gEfiDiskIoProtocolGuid, &Instance->DiskIo, | |
| NULL | |
| ); | |
| } | |
| if (!EFI_ERROR (Status)) { | |
| do { | |
| EfiAcquireLock (&Instance->TaskQueueLock); | |
| AllTaskDone = IsListEmpty (&Instance->TaskQueue); | |
| EfiReleaseLock (&Instance->TaskQueueLock); | |
| } while (!AllTaskDone); | |
| FreeAlignedPages ( | |
| Instance->SharedWorkingBuffer, | |
| EFI_SIZE_TO_PAGES (PcdGet32 (PcdDiskIoDataBufferBlockNum) * Instance->BlockIo->Media->BlockSize) | |
| ); | |
| Status = gBS->CloseProtocol ( | |
| ControllerHandle, | |
| &gEfiBlockIoProtocolGuid, | |
| This->DriverBindingHandle, | |
| ControllerHandle | |
| ); | |
| ASSERT_EFI_ERROR (Status); | |
| if (DiskIo2 != NULL) { | |
| Status = gBS->CloseProtocol ( | |
| ControllerHandle, | |
| &gEfiBlockIo2ProtocolGuid, | |
| This->DriverBindingHandle, | |
| ControllerHandle | |
| ); | |
| ASSERT_EFI_ERROR (Status); | |
| } | |
| FreePool (Instance); | |
| } | |
| return Status; | |
| } | |
| /** | |
| Destroy the sub task. | |
| @param Instance Pointer to the DISK_IO_PRIVATE_DATA. | |
| @param Subtask Subtask. | |
| @return LIST_ENTRY * Pointer to the next link of subtask. | |
| **/ | |
| LIST_ENTRY * | |
| DiskIoDestroySubtask ( | |
| IN DISK_IO_PRIVATE_DATA *Instance, | |
| IN DISK_IO_SUBTASK *Subtask | |
| ) | |
| { | |
| LIST_ENTRY *Link; | |
| if (Subtask->Task != NULL) { | |
| EfiAcquireLock (&Subtask->Task->SubtasksLock); | |
| } | |
| Link = RemoveEntryList (&Subtask->Link); | |
| if (Subtask->Task != NULL) { | |
| EfiReleaseLock (&Subtask->Task->SubtasksLock); | |
| } | |
| if (!Subtask->Blocking) { | |
| if (Subtask->WorkingBuffer != NULL) { | |
| FreeAlignedPages ( | |
| Subtask->WorkingBuffer, | |
| Subtask->Length < Instance->BlockIo->Media->BlockSize | |
| ? EFI_SIZE_TO_PAGES (Instance->BlockIo->Media->BlockSize) | |
| : EFI_SIZE_TO_PAGES (Subtask->Length) | |
| ); | |
| } | |
| if (Subtask->BlockIo2Token.Event != NULL) { | |
| gBS->CloseEvent (Subtask->BlockIo2Token.Event); | |
| } | |
| } | |
| FreePool (Subtask); | |
| return Link; | |
| } | |
| /** | |
| The callback for the BlockIo2 ReadBlocksEx/WriteBlocksEx. | |
| @param Event Event whose notification function is being invoked. | |
| @param Context The pointer to the notification function's context, | |
| which points to the DISK_IO_SUBTASK instance. | |
| **/ | |
| VOID | |
| EFIAPI | |
| DiskIo2OnReadWriteComplete ( | |
| IN EFI_EVENT Event, | |
| IN VOID *Context | |
| ) | |
| { | |
| DISK_IO_SUBTASK *Subtask; | |
| DISK_IO2_TASK *Task; | |
| EFI_STATUS TransactionStatus; | |
| DISK_IO_PRIVATE_DATA *Instance; | |
| Subtask = (DISK_IO_SUBTASK *) Context; | |
| TransactionStatus = Subtask->BlockIo2Token.TransactionStatus; | |
| Task = Subtask->Task; | |
| Instance = Task->Instance; | |
| ASSERT (Subtask->Signature == DISK_IO_SUBTASK_SIGNATURE); | |
| ASSERT (Instance->Signature == DISK_IO_PRIVATE_DATA_SIGNATURE); | |
| ASSERT (Task->Signature == DISK_IO2_TASK_SIGNATURE); | |
| if ((Subtask->WorkingBuffer != NULL) && !EFI_ERROR (TransactionStatus) && | |
| (Task->Token != NULL) && !Subtask->Write | |
| ) { | |
| CopyMem (Subtask->Buffer, Subtask->WorkingBuffer + Subtask->Offset, Subtask->Length); | |
| } | |
| DiskIoDestroySubtask (Instance, Subtask); | |
| if (EFI_ERROR (TransactionStatus) || IsListEmpty (&Task->Subtasks)) { | |
| if (Task->Token != NULL) { | |
| // | |
| // Signal error status once the subtask is failed. | |
| // Or signal the last status once the last subtask is finished. | |
| // | |
| Task->Token->TransactionStatus = TransactionStatus; | |
| gBS->SignalEvent (Task->Token->Event); | |
| // | |
| // Mark token to NULL indicating the Task is a dead task. | |
| // | |
| Task->Token = NULL; | |
| } | |
| } | |
| } | |
| /** | |
| Create the subtask. | |
| @param Write TRUE: Write request; FALSE: Read request. | |
| @param Lba The starting logical block address to read from on the device. | |
| @param Offset The starting byte offset to read from the LBA. | |
| @param Length The number of bytes to read from the device. | |
| @param WorkingBuffer The aligned buffer to hold the data for reading or writing. | |
| @param Buffer The buffer to hold the data for reading or writing. | |
| @param Blocking TRUE: Blocking request; FALSE: Non-blocking request. | |
| @return A pointer to the created subtask. | |
| **/ | |
| DISK_IO_SUBTASK * | |
| DiskIoCreateSubtask ( | |
| IN BOOLEAN Write, | |
| IN UINT64 Lba, | |
| IN UINT32 Offset, | |
| IN UINTN Length, | |
| IN VOID *WorkingBuffer, OPTIONAL | |
| IN VOID *Buffer, | |
| IN BOOLEAN Blocking | |
| ) | |
| { | |
| DISK_IO_SUBTASK *Subtask; | |
| EFI_STATUS Status; | |
| Subtask = AllocateZeroPool (sizeof (DISK_IO_SUBTASK)); | |
| if (Subtask == NULL) { | |
| return NULL; | |
| } | |
| Subtask->Signature = DISK_IO_SUBTASK_SIGNATURE; | |
| Subtask->Write = Write; | |
| Subtask->Lba = Lba; | |
| Subtask->Offset = Offset; | |
| Subtask->Length = Length; | |
| Subtask->WorkingBuffer = WorkingBuffer; | |
| Subtask->Buffer = Buffer; | |
| Subtask->Blocking = Blocking; | |
| if (!Blocking) { | |
| Status = gBS->CreateEvent ( | |
| EVT_NOTIFY_SIGNAL, | |
| TPL_NOTIFY, | |
| DiskIo2OnReadWriteComplete, | |
| Subtask, | |
| &Subtask->BlockIo2Token.Event | |
| ); | |
| if (EFI_ERROR (Status)) { | |
| FreePool (Subtask); | |
| return NULL; | |
| } | |
| } | |
| DEBUG (( | |
| EFI_D_BLKIO, | |
| " %c:Lba/Offset/Length/WorkingBuffer/Buffer = %016lx/%08x/%08x/%08x/%08x\n", | |
| Write ? 'W': 'R', Lba, Offset, Length, WorkingBuffer, Buffer | |
| )); | |
| return Subtask; | |
| } | |
| /** | |
| Create the subtask list. | |
| @param Instance Pointer to the DISK_IO_PRIVATE_DATA. | |
| @param Write TRUE: Write request; FALSE: Read request. | |
| @param Offset The starting byte offset to read from the device. | |
| @param BufferSize The size in bytes of Buffer. The number of bytes to read from the device. | |
| @param Buffer A pointer to the buffer for the data. | |
| @param Blocking TRUE: Blocking request; FALSE: Non-blocking request. | |
| @param SharedWorkingBuffer The aligned buffer to hold the data for reading or writing. | |
| @param Subtasks The subtask list header. | |
| @retval TRUE The subtask list is created successfully. | |
| @retval FALSE The subtask list is not created. | |
| **/ | |
| BOOLEAN | |
| DiskIoCreateSubtaskList ( | |
| IN DISK_IO_PRIVATE_DATA *Instance, | |
| IN BOOLEAN Write, | |
| IN UINT64 Offset, | |
| IN UINTN BufferSize, | |
| IN VOID *Buffer, | |
| IN BOOLEAN Blocking, | |
| IN VOID *SharedWorkingBuffer, | |
| IN OUT LIST_ENTRY *Subtasks | |
| ) | |
| { | |
| UINT32 BlockSize; | |
| UINT32 IoAlign; | |
| UINT64 Lba; | |
| UINT64 OverRunLba; | |
| UINT32 UnderRun; | |
| UINT32 OverRun; | |
| UINT8 *BufferPtr; | |
| UINTN Length; | |
| UINTN DataBufferSize; | |
| DISK_IO_SUBTASK *Subtask; | |
| VOID *WorkingBuffer; | |
| LIST_ENTRY *Link; | |
| DEBUG ((EFI_D_BLKIO, "DiskIo: Create subtasks for task: Offset/BufferSize/Buffer = %016lx/%08x/%08x\n", Offset, BufferSize, Buffer)); | |
| BlockSize = Instance->BlockIo->Media->BlockSize; | |
| IoAlign = Instance->BlockIo->Media->IoAlign; | |
| if (IoAlign == 0) { | |
| IoAlign = 1; | |
| } | |
| Lba = DivU64x32Remainder (Offset, BlockSize, &UnderRun); | |
| BufferPtr = (UINT8 *) Buffer; | |
| // | |
| // Special handling for zero BufferSize | |
| // | |
| if (BufferSize == 0) { | |
| Subtask = DiskIoCreateSubtask (Write, Lba, UnderRun, 0, NULL, BufferPtr, Blocking); | |
| if (Subtask == NULL) { | |
| goto Done; | |
| } | |
| InsertTailList (Subtasks, &Subtask->Link); | |
| return TRUE; | |
| } | |
| if (UnderRun != 0) { | |
| Length = MIN (BlockSize - UnderRun, BufferSize); | |
| if (Blocking) { | |
| WorkingBuffer = SharedWorkingBuffer; | |
| } else { | |
| WorkingBuffer = AllocateAlignedPages (EFI_SIZE_TO_PAGES (BlockSize), IoAlign); | |
| if (WorkingBuffer == NULL) { | |
| goto Done; | |
| } | |
| } | |
| if (Write) { | |
| // | |
| // A half write operation can be splitted to a blocking block-read and half write operation | |
| // This can simplify the sub task processing logic | |
| // | |
| Subtask = DiskIoCreateSubtask (FALSE, Lba, 0, BlockSize, NULL, WorkingBuffer, TRUE); | |
| if (Subtask == NULL) { | |
| goto Done; | |
| } | |
| InsertTailList (Subtasks, &Subtask->Link); | |
| } | |
| Subtask = DiskIoCreateSubtask (Write, Lba, UnderRun, Length, WorkingBuffer, BufferPtr, Blocking); | |
| if (Subtask == NULL) { | |
| goto Done; | |
| } | |
| InsertTailList (Subtasks, &Subtask->Link); | |
| BufferPtr += Length; | |
| Offset += Length; | |
| BufferSize -= Length; | |
| Lba ++; | |
| } | |
| OverRunLba = Lba + DivU64x32Remainder (BufferSize, BlockSize, &OverRun); | |
| BufferSize -= OverRun; | |
| if (OverRun != 0) { | |
| if (Blocking) { | |
| WorkingBuffer = SharedWorkingBuffer; | |
| } else { | |
| WorkingBuffer = AllocateAlignedPages (EFI_SIZE_TO_PAGES (BlockSize), IoAlign); | |
| if (WorkingBuffer == NULL) { | |
| goto Done; | |
| } | |
| } | |
| if (Write) { | |
| // | |
| // A half write operation can be splitted to a blocking block-read and half write operation | |
| // This can simplify the sub task processing logic | |
| // | |
| Subtask = DiskIoCreateSubtask (FALSE, OverRunLba, 0, BlockSize, NULL, WorkingBuffer, TRUE); | |
| if (Subtask == NULL) { | |
| goto Done; | |
| } | |
| InsertTailList (Subtasks, &Subtask->Link); | |
| } | |
| Subtask = DiskIoCreateSubtask (Write, OverRunLba, 0, OverRun, WorkingBuffer, BufferPtr + BufferSize, Blocking); | |
| if (Subtask == NULL) { | |
| goto Done; | |
| } | |
| InsertTailList (Subtasks, &Subtask->Link); | |
| } | |
| if (OverRunLba > Lba) { | |
| // | |
| // If the DiskIo maps directly to a BlockIo device do the read. | |
| // | |
| if (ALIGN_POINTER (BufferPtr, IoAlign) == BufferPtr) { | |
| Subtask = DiskIoCreateSubtask (Write, Lba, 0, BufferSize, NULL, BufferPtr, Blocking); | |
| if (Subtask == NULL) { | |
| goto Done; | |
| } | |
| InsertTailList (Subtasks, &Subtask->Link); | |
| BufferPtr += BufferSize; | |
| Offset += BufferSize; | |
| BufferSize -= BufferSize; | |
| } else { | |
| if (Blocking) { | |
| // | |
| // Use the allocated buffer instead of the original buffer | |
| // to avoid alignment issue. | |
| // | |
| for (; Lba < OverRunLba; Lba += PcdGet32 (PcdDiskIoDataBufferBlockNum)) { | |
| DataBufferSize = MIN (BufferSize, PcdGet32 (PcdDiskIoDataBufferBlockNum) * BlockSize); | |
| Subtask = DiskIoCreateSubtask (Write, Lba, 0, DataBufferSize, SharedWorkingBuffer, BufferPtr, Blocking); | |
| if (Subtask == NULL) { | |
| goto Done; | |
| } | |
| InsertTailList (Subtasks, &Subtask->Link); | |
| BufferPtr += DataBufferSize; | |
| Offset += DataBufferSize; | |
| BufferSize -= DataBufferSize; | |
| } | |
| } else { | |
| WorkingBuffer = AllocateAlignedPages (EFI_SIZE_TO_PAGES (BufferSize), IoAlign); | |
| if (WorkingBuffer == NULL) { | |
| // | |
| // If there is not enough memory, downgrade to blocking access | |
| // | |
| DEBUG ((EFI_D_VERBOSE, "DiskIo: No enough memory so downgrade to blocking access\n")); | |
| if (!DiskIoCreateSubtaskList (Instance, Write, Offset, BufferSize, BufferPtr, TRUE, SharedWorkingBuffer, Subtasks)) { | |
| goto Done; | |
| } | |
| } else { | |
| Subtask = DiskIoCreateSubtask (Write, Lba, 0, BufferSize, WorkingBuffer, BufferPtr, Blocking); | |
| if (Subtask == NULL) { | |
| goto Done; | |
| } | |
| InsertTailList (Subtasks, &Subtask->Link); | |
| } | |
| BufferPtr += BufferSize; | |
| Offset += BufferSize; | |
| BufferSize -= BufferSize; | |
| } | |
| } | |
| } | |
| ASSERT (BufferSize == 0); | |
| return TRUE; | |
| Done: | |
| // | |
| // Remove all the subtasks. | |
| // | |
| for (Link = GetFirstNode (Subtasks); !IsNull (Subtasks, Link); ) { | |
| Subtask = CR (Link, DISK_IO_SUBTASK, Link, DISK_IO_SUBTASK_SIGNATURE); | |
| Link = DiskIoDestroySubtask (Instance, Subtask); | |
| } | |
| return FALSE; | |
| } | |
| /** | |
| Terminate outstanding asynchronous requests to a device. | |
| @param This Indicates a pointer to the calling context. | |
| @retval EFI_SUCCESS All outstanding requests were successfully terminated. | |
| @retval EFI_DEVICE_ERROR The device reported an error while performing the cancel | |
| operation. | |
| **/ | |
| EFI_STATUS | |
| EFIAPI | |
| DiskIo2Cancel ( | |
| IN EFI_DISK_IO2_PROTOCOL *This | |
| ) | |
| { | |
| DISK_IO_PRIVATE_DATA *Instance; | |
| DISK_IO2_TASK *Task; | |
| LIST_ENTRY *Link; | |
| Instance = DISK_IO_PRIVATE_DATA_FROM_DISK_IO2 (This); | |
| EfiAcquireLock (&Instance->TaskQueueLock); | |
| for (Link = GetFirstNode (&Instance->TaskQueue) | |
| ; !IsNull (&Instance->TaskQueue, Link) | |
| ; Link = GetNextNode (&Instance->TaskQueue, Link) | |
| ) { | |
| Task = CR (Link, DISK_IO2_TASK, Link, DISK_IO2_TASK_SIGNATURE); | |
| if (Task->Token != NULL) { | |
| Task->Token->TransactionStatus = EFI_ABORTED; | |
| gBS->SignalEvent (Task->Token->Event); | |
| // | |
| // Set Token to NULL so that the further BlockIo2 responses will be ignored | |
| // | |
| Task->Token = NULL; | |
| } | |
| } | |
| EfiReleaseLock (&Instance->TaskQueueLock); | |
| return EFI_SUCCESS; | |
| } | |
| /** | |
| Remove the completed tasks from Instance->TaskQueue. Completed tasks are those who don't have any subtasks. | |
| @param Instance Pointer to the DISK_IO_PRIVATE_DATA. | |
| @retval TRUE The Instance->TaskQueue is empty after the completed tasks are removed. | |
| @retval FALSE The Instance->TaskQueue is not empty after the completed tasks are removed. | |
| **/ | |
| BOOLEAN | |
| DiskIo2RemoveCompletedTask ( | |
| IN DISK_IO_PRIVATE_DATA *Instance | |
| ) | |
| { | |
| BOOLEAN QueueEmpty; | |
| LIST_ENTRY *Link; | |
| DISK_IO2_TASK *Task; | |
| QueueEmpty = TRUE; | |
| EfiAcquireLock (&Instance->TaskQueueLock); | |
| for (Link = GetFirstNode (&Instance->TaskQueue); !IsNull (&Instance->TaskQueue, Link); ) { | |
| Task = CR (Link, DISK_IO2_TASK, Link, DISK_IO2_TASK_SIGNATURE); | |
| if (IsListEmpty (&Task->Subtasks)) { | |
| Link = RemoveEntryList (&Task->Link); | |
| ASSERT (Task->Token == NULL); | |
| FreePool (Task); | |
| } else { | |
| Link = GetNextNode (&Instance->TaskQueue, Link); | |
| QueueEmpty = FALSE; | |
| } | |
| } | |
| EfiReleaseLock (&Instance->TaskQueueLock); | |
| return QueueEmpty; | |
| } | |
| /** | |
| Common routine to access the disk. | |
| @param Instance Pointer to the DISK_IO_PRIVATE_DATA. | |
| @param Write TRUE: Write operation; FALSE: Read operation. | |
| @param MediaId ID of the medium to access. | |
| @param Offset The starting byte offset on the logical block I/O device to access. | |
| @param Token A pointer to the token associated with the transaction. | |
| If this field is NULL, synchronous/blocking IO is performed. | |
| @param BufferSize The size in bytes of Buffer. The number of bytes to read from the device. | |
| @param Buffer A pointer to the destination buffer for the data. | |
| The caller is responsible either having implicit or explicit ownership of the buffer. | |
| **/ | |
| EFI_STATUS | |
| DiskIo2ReadWriteDisk ( | |
| IN DISK_IO_PRIVATE_DATA *Instance, | |
| IN BOOLEAN Write, | |
| IN UINT32 MediaId, | |
| IN UINT64 Offset, | |
| IN EFI_DISK_IO2_TOKEN *Token, | |
| IN UINTN BufferSize, | |
| IN UINT8 *Buffer | |
| ) | |
| { | |
| EFI_STATUS Status; | |
| EFI_BLOCK_IO_PROTOCOL *BlockIo; | |
| EFI_BLOCK_IO2_PROTOCOL *BlockIo2; | |
| EFI_BLOCK_IO_MEDIA *Media; | |
| LIST_ENTRY *Link; | |
| LIST_ENTRY *NextLink; | |
| LIST_ENTRY Subtasks; | |
| DISK_IO_SUBTASK *Subtask; | |
| DISK_IO2_TASK *Task; | |
| EFI_TPL OldTpl; | |
| BOOLEAN Blocking; | |
| BOOLEAN SubtaskBlocking; | |
| LIST_ENTRY *SubtasksPtr; | |
| Task = NULL; | |
| BlockIo = Instance->BlockIo; | |
| BlockIo2 = Instance->BlockIo2; | |
| Media = BlockIo->Media; | |
| Status = EFI_SUCCESS; | |
| Blocking = (BOOLEAN) ((Token == NULL) || (Token->Event == NULL)); | |
| if (Blocking) { | |
| // | |
| // Wait till pending async task is completed. | |
| // | |
| while (!DiskIo2RemoveCompletedTask (Instance)); | |
| SubtasksPtr = &Subtasks; | |
| } else { | |
| DiskIo2RemoveCompletedTask (Instance); | |
| Task = AllocatePool (sizeof (DISK_IO2_TASK)); | |
| if (Task == NULL) { | |
| return EFI_OUT_OF_RESOURCES; | |
| } | |
| EfiAcquireLock (&Instance->TaskQueueLock); | |
| InsertTailList (&Instance->TaskQueue, &Task->Link); | |
| EfiReleaseLock (&Instance->TaskQueueLock); | |
| Task->Signature = DISK_IO2_TASK_SIGNATURE; | |
| Task->Instance = Instance; | |
| Task->Token = Token; | |
| EfiInitializeLock (&Task->SubtasksLock, TPL_NOTIFY); | |
| SubtasksPtr = &Task->Subtasks; | |
| } | |
| InitializeListHead (SubtasksPtr); | |
| if (!DiskIoCreateSubtaskList (Instance, Write, Offset, BufferSize, Buffer, Blocking, Instance->SharedWorkingBuffer, SubtasksPtr)) { | |
| if (Task != NULL) { | |
| FreePool (Task); | |
| } | |
| return EFI_OUT_OF_RESOURCES; | |
| } | |
| ASSERT (!IsListEmpty (SubtasksPtr)); | |
| OldTpl = gBS->RaiseTPL (TPL_CALLBACK); | |
| for ( Link = GetFirstNode (SubtasksPtr), NextLink = GetNextNode (SubtasksPtr, Link) | |
| ; !IsNull (SubtasksPtr, Link) | |
| ; Link = NextLink, NextLink = GetNextNode (SubtasksPtr, NextLink) | |
| ) { | |
| Subtask = CR (Link, DISK_IO_SUBTASK, Link, DISK_IO_SUBTASK_SIGNATURE); | |
| Subtask->Task = Task; | |
| SubtaskBlocking = Subtask->Blocking; | |
| ASSERT ((Subtask->Length % Media->BlockSize == 0) || (Subtask->Length < Media->BlockSize)); | |
| if (Subtask->Write) { | |
| // | |
| // Write | |
| // | |
| if (Subtask->WorkingBuffer != NULL) { | |
| // | |
| // A sub task before this one should be a block read operation, causing the WorkingBuffer filled with the entire one block data. | |
| // | |
| CopyMem (Subtask->WorkingBuffer + Subtask->Offset, Subtask->Buffer, Subtask->Length); | |
| } | |
| if (SubtaskBlocking) { | |
| Status = BlockIo->WriteBlocks ( | |
| BlockIo, | |
| MediaId, | |
| Subtask->Lba, | |
| (Subtask->Length % Media->BlockSize == 0) ? Subtask->Length : Media->BlockSize, | |
| (Subtask->WorkingBuffer != NULL) ? Subtask->WorkingBuffer : Subtask->Buffer | |
| ); | |
| } else { | |
| Status = BlockIo2->WriteBlocksEx ( | |
| BlockIo2, | |
| MediaId, | |
| Subtask->Lba, | |
| &Subtask->BlockIo2Token, | |
| (Subtask->Length % Media->BlockSize == 0) ? Subtask->Length : Media->BlockSize, | |
| (Subtask->WorkingBuffer != NULL) ? Subtask->WorkingBuffer : Subtask->Buffer | |
| ); | |
| } | |
| } else { | |
| // | |
| // Read | |
| // | |
| if (SubtaskBlocking) { | |
| Status = BlockIo->ReadBlocks ( | |
| BlockIo, | |
| MediaId, | |
| Subtask->Lba, | |
| (Subtask->Length % Media->BlockSize == 0) ? Subtask->Length : Media->BlockSize, | |
| (Subtask->WorkingBuffer != NULL) ? Subtask->WorkingBuffer : Subtask->Buffer | |
| ); | |
| if (!EFI_ERROR (Status) && (Subtask->WorkingBuffer != NULL)) { | |
| CopyMem (Subtask->Buffer, Subtask->WorkingBuffer + Subtask->Offset, Subtask->Length); | |
| } | |
| } else { | |
| Status = BlockIo2->ReadBlocksEx ( | |
| BlockIo2, | |
| MediaId, | |
| Subtask->Lba, | |
| &Subtask->BlockIo2Token, | |
| (Subtask->Length % Media->BlockSize == 0) ? Subtask->Length : Media->BlockSize, | |
| (Subtask->WorkingBuffer != NULL) ? Subtask->WorkingBuffer : Subtask->Buffer | |
| ); | |
| } | |
| } | |
| if (SubtaskBlocking || EFI_ERROR (Status)) { | |
| // | |
| // Make sure the subtask list only contains non-blocking subtasks. | |
| // Remove failed non-blocking subtasks as well because the callback won't be called. | |
| // | |
| DiskIoDestroySubtask (Instance, Subtask); | |
| } | |
| if (EFI_ERROR (Status)) { | |
| break; | |
| } | |
| } | |
| gBS->RaiseTPL (TPL_NOTIFY); | |
| // | |
| // Remove all the remaining subtasks when failure. | |
| // We shouldn't remove all the tasks because the non-blocking requests have been submitted and cannot be canceled. | |
| // | |
| if (EFI_ERROR (Status)) { | |
| while (!IsNull (SubtasksPtr, NextLink)) { | |
| Subtask = CR (NextLink, DISK_IO_SUBTASK, Link, DISK_IO_SUBTASK_SIGNATURE); | |
| NextLink = DiskIoDestroySubtask (Instance, Subtask); | |
| } | |
| } | |
| // | |
| // It's possible that the non-blocking subtasks finish before raising TPL to NOTIFY, | |
| // so the subtasks list might be empty at this point. | |
| // | |
| if (!Blocking && IsListEmpty (SubtasksPtr)) { | |
| EfiAcquireLock (&Instance->TaskQueueLock); | |
| RemoveEntryList (&Task->Link); | |
| EfiReleaseLock (&Instance->TaskQueueLock); | |
| if (!EFI_ERROR (Status) && (Task->Token != NULL)) { | |
| // | |
| // Task->Token should be set to NULL by the DiskIo2OnReadWriteComplete | |
| // It it's not, that means the non-blocking request was downgraded to blocking request. | |
| // | |
| DEBUG ((EFI_D_VERBOSE, "DiskIo: Non-blocking request was downgraded to blocking request, signal event directly.\n")); | |
| Task->Token->TransactionStatus = Status; | |
| gBS->SignalEvent (Task->Token->Event); | |
| } | |
| FreePool (Task); | |
| } | |
| gBS->RestoreTPL (OldTpl); | |
| return Status; | |
| } | |
| /** | |
| Reads a specified number of bytes from a device. | |
| @param This Indicates a pointer to the calling context. | |
| @param MediaId ID of the medium to be read. | |
| @param Offset The starting byte offset on the logical block I/O device to read from. | |
| @param Token A pointer to the token associated with the transaction. | |
| If this field is NULL, synchronous/blocking IO is performed. | |
| @param BufferSize The size in bytes of Buffer. The number of bytes to read from the device. | |
| @param Buffer A pointer to the destination buffer for the data. | |
| The caller is responsible either having implicit or explicit ownership of the buffer. | |
| @retval EFI_SUCCESS If Event is NULL (blocking I/O): The data was read correctly from the device. | |
| If Event is not NULL (asynchronous I/O): The request was successfully queued for processing. | |
| Event will be signaled upon completion. | |
| @retval EFI_DEVICE_ERROR The device reported an error while performing the write. | |
| @retval EFI_NO_MEDIA There is no medium in the device. | |
| @retval EFI_MEDIA_CHNAGED The MediaId is not for the current medium. | |
| @retval EFI_INVALID_PARAMETER The read request contains device addresses that are not valid for the device. | |
| @retval EFI_OUT_OF_RESOURCES The request could not be completed due to a lack of resources. | |
| **/ | |
| EFI_STATUS | |
| EFIAPI | |
| DiskIo2ReadDiskEx ( | |
| IN EFI_DISK_IO2_PROTOCOL *This, | |
| IN UINT32 MediaId, | |
| IN UINT64 Offset, | |
| IN OUT EFI_DISK_IO2_TOKEN *Token, | |
| IN UINTN BufferSize, | |
| OUT VOID *Buffer | |
| ) | |
| { | |
| return DiskIo2ReadWriteDisk ( | |
| DISK_IO_PRIVATE_DATA_FROM_DISK_IO2 (This), | |
| FALSE, MediaId, Offset, Token, BufferSize, (UINT8 *) Buffer | |
| ); | |
| } | |
| /** | |
| Writes a specified number of bytes to a device. | |
| @param This Indicates a pointer to the calling context. | |
| @param MediaId ID of the medium to be written. | |
| @param Offset The starting byte offset on the logical block I/O device to write to. | |
| @param Token A pointer to the token associated with the transaction. | |
| If this field is NULL, synchronous/blocking IO is performed. | |
| @param BufferSize The size in bytes of Buffer. The number of bytes to write to the device. | |
| @param Buffer A pointer to the buffer containing the data to be written. | |
| @retval EFI_SUCCESS If Event is NULL (blocking I/O): The data was written correctly to the device. | |
| If Event is not NULL (asynchronous I/O): The request was successfully queued for processing. | |
| Event will be signaled upon completion. | |
| @retval EFI_WRITE_PROTECTED The device cannot be written to. | |
| @retval EFI_DEVICE_ERROR The device reported an error while performing the write operation. | |
| @retval EFI_NO_MEDIA There is no medium in the device. | |
| @retval EFI_MEDIA_CHNAGED The MediaId is not for the current medium. | |
| @retval EFI_INVALID_PARAMETER The write request contains device addresses that are not valid for the device. | |
| @retval EFI_OUT_OF_RESOURCES The request could not be completed due to a lack of resources. | |
| **/ | |
| EFI_STATUS | |
| EFIAPI | |
| DiskIo2WriteDiskEx ( | |
| IN EFI_DISK_IO2_PROTOCOL *This, | |
| IN UINT32 MediaId, | |
| IN UINT64 Offset, | |
| IN OUT EFI_DISK_IO2_TOKEN *Token, | |
| IN UINTN BufferSize, | |
| IN VOID *Buffer | |
| ) | |
| { | |
| return DiskIo2ReadWriteDisk ( | |
| DISK_IO_PRIVATE_DATA_FROM_DISK_IO2 (This), | |
| TRUE, MediaId, Offset, Token, BufferSize, (UINT8 *) Buffer | |
| ); | |
| } | |
| /** | |
| The callback for the BlockIo2 FlushBlocksEx. | |
| @param Event Event whose notification function is being invoked. | |
| @param Context The pointer to the notification function's context, | |
| which points to the DISK_IO2_FLUSH_TASK instance. | |
| **/ | |
| VOID | |
| EFIAPI | |
| DiskIo2OnFlushComplete ( | |
| IN EFI_EVENT Event, | |
| IN VOID *Context | |
| ) | |
| { | |
| DISK_IO2_FLUSH_TASK *Task; | |
| gBS->CloseEvent (Event); | |
| Task = (DISK_IO2_FLUSH_TASK *) Context; | |
| ASSERT (Task->Signature == DISK_IO2_FLUSH_TASK_SIGNATURE); | |
| Task->Token->TransactionStatus = Task->BlockIo2Token.TransactionStatus; | |
| gBS->SignalEvent (Task->Token->Event); | |
| FreePool (Task); | |
| } | |
| /** | |
| Flushes all modified data to the physical device. | |
| @param This Indicates a pointer to the calling context. | |
| @param Token A pointer to the token associated with the transaction. | |
| If this field is NULL, synchronous/blocking IO is performed. | |
| @retval EFI_SUCCESS If Event is NULL (blocking I/O): The data was flushed successfully to the device. | |
| If Event is not NULL (asynchronous I/O): The request was successfully queued for processing. | |
| Event will be signaled upon completion. | |
| @retval EFI_WRITE_PROTECTED The device cannot be written to. | |
| @retval EFI_DEVICE_ERROR The device reported an error while performing the write operation. | |
| @retval EFI_NO_MEDIA There is no medium in the device. | |
| @retval EFI_OUT_OF_RESOURCES The request could not be completed due to a lack of resources. | |
| **/ | |
| EFI_STATUS | |
| EFIAPI | |
| DiskIo2FlushDiskEx ( | |
| IN EFI_DISK_IO2_PROTOCOL *This, | |
| IN OUT EFI_DISK_IO2_TOKEN *Token | |
| ) | |
| { | |
| EFI_STATUS Status; | |
| DISK_IO2_FLUSH_TASK *Task; | |
| DISK_IO_PRIVATE_DATA *Private; | |
| Private = DISK_IO_PRIVATE_DATA_FROM_DISK_IO2 (This); | |
| if ((Token != NULL) && (Token->Event != NULL)) { | |
| Task = AllocatePool (sizeof (DISK_IO2_FLUSH_TASK)); | |
| if (Task == NULL) { | |
| return EFI_OUT_OF_RESOURCES; | |
| } | |
| Status = gBS->CreateEvent ( | |
| EVT_NOTIFY_SIGNAL, | |
| TPL_CALLBACK, | |
| DiskIo2OnFlushComplete, | |
| Task, | |
| &Task->BlockIo2Token.Event | |
| ); | |
| if (EFI_ERROR (Status)) { | |
| FreePool (Task); | |
| return Status; | |
| } | |
| Task->Signature = DISK_IO2_FLUSH_TASK_SIGNATURE; | |
| Task->Token = Token; | |
| Status = Private->BlockIo2->FlushBlocksEx (Private->BlockIo2, &Task->BlockIo2Token); | |
| if (EFI_ERROR (Status)) { | |
| gBS->CloseEvent (Task->BlockIo2Token.Event); | |
| FreePool (Task); | |
| } | |
| } else { | |
| Status = Private->BlockIo2->FlushBlocksEx (Private->BlockIo2, NULL); | |
| } | |
| return Status; | |
| } | |
| /** | |
| Read BufferSize bytes from Offset into Buffer. | |
| Reads may support reads that are not aligned on | |
| sector boundaries. There are three cases: | |
| UnderRun - The first byte is not on a sector boundary or the read request is | |
| less than a sector in length. | |
| Aligned - A read of N contiguous sectors. | |
| OverRun - The last byte is not on a sector boundary. | |
| @param This Protocol instance pointer. | |
| @param MediaId Id of the media, changes every time the media is replaced. | |
| @param Offset The starting byte offset to read from | |
| @param BufferSize Size of Buffer | |
| @param Buffer Buffer containing read data | |
| @retval EFI_SUCCESS The data was read correctly from the device. | |
| @retval EFI_DEVICE_ERROR The device reported an error while performing the read. | |
| @retval EFI_NO_MEDIA There is no media in the device. | |
| @retval EFI_MEDIA_CHNAGED The MediaId does not matched the current device. | |
| @retval EFI_INVALID_PARAMETER The read request contains device addresses that are not | |
| valid for the device. | |
| **/ | |
| EFI_STATUS | |
| EFIAPI | |
| DiskIoReadDisk ( | |
| IN EFI_DISK_IO_PROTOCOL *This, | |
| IN UINT32 MediaId, | |
| IN UINT64 Offset, | |
| IN UINTN BufferSize, | |
| OUT VOID *Buffer | |
| ) | |
| { | |
| return DiskIo2ReadWriteDisk ( | |
| DISK_IO_PRIVATE_DATA_FROM_DISK_IO (This), | |
| FALSE, MediaId, Offset, NULL, BufferSize, (UINT8 *) Buffer | |
| ); | |
| } | |
| /** | |
| Writes BufferSize bytes from Buffer into Offset. | |
| Writes may require a read modify write to support writes that are not | |
| aligned on sector boundaries. There are three cases: | |
| UnderRun - The first byte is not on a sector boundary or the write request | |
| is less than a sector in length. Read modify write is required. | |
| Aligned - A write of N contiguous sectors. | |
| OverRun - The last byte is not on a sector boundary. Read modified write | |
| required. | |
| @param This Protocol instance pointer. | |
| @param MediaId Id of the media, changes every time the media is replaced. | |
| @param Offset The starting byte offset to read from | |
| @param BufferSize Size of Buffer | |
| @param Buffer Buffer containing read data | |
| @retval EFI_SUCCESS The data was written correctly to the device. | |
| @retval EFI_WRITE_PROTECTED The device can not be written to. | |
| @retval EFI_DEVICE_ERROR The device reported an error while performing the write. | |
| @retval EFI_NO_MEDIA There is no media in the device. | |
| @retval EFI_MEDIA_CHNAGED The MediaId does not matched the current device. | |
| @retval EFI_INVALID_PARAMETER The write request contains device addresses that are not | |
| valid for the device. | |
| **/ | |
| EFI_STATUS | |
| EFIAPI | |
| DiskIoWriteDisk ( | |
| IN EFI_DISK_IO_PROTOCOL *This, | |
| IN UINT32 MediaId, | |
| IN UINT64 Offset, | |
| IN UINTN BufferSize, | |
| IN VOID *Buffer | |
| ) | |
| { | |
| return DiskIo2ReadWriteDisk ( | |
| DISK_IO_PRIVATE_DATA_FROM_DISK_IO (This), | |
| TRUE, MediaId, Offset, NULL, BufferSize, (UINT8 *) Buffer | |
| ); | |
| } | |
| /** | |
| The user Entry Point for module DiskIo. The user code starts with this function. | |
| @param[in] ImageHandle The firmware allocated handle for the EFI image. | |
| @param[in] SystemTable A pointer to the EFI System Table. | |
| @retval EFI_SUCCESS The entry point is executed successfully. | |
| @retval other Some error occurs when executing this entry point. | |
| **/ | |
| EFI_STATUS | |
| EFIAPI | |
| InitializeDiskIo ( | |
| IN EFI_HANDLE ImageHandle, | |
| IN EFI_SYSTEM_TABLE *SystemTable | |
| ) | |
| { | |
| EFI_STATUS Status; | |
| // | |
| // Install driver model protocol(s). | |
| // | |
| Status = EfiLibInstallDriverBindingComponentName2 ( | |
| ImageHandle, | |
| SystemTable, | |
| &gDiskIoDriverBinding, | |
| ImageHandle, | |
| &gDiskIoComponentName, | |
| &gDiskIoComponentName2 | |
| ); | |
| ASSERT_EFI_ERROR (Status); | |
| return Status; | |
| } |