mirror of https://github.com/acidanthera/audk.git
1277 lines
43 KiB
C
1277 lines
43 KiB
C
/** @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 - 2014, 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 (Media->MediaId != MediaId) {
|
|
return EFI_MEDIA_CHANGED;
|
|
}
|
|
|
|
if (Write && Media->ReadOnly) {
|
|
return EFI_WRITE_PROTECTED;
|
|
}
|
|
|
|
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;
|
|
}
|