Add check to see if there is a media in the disk device. If not return EFI_NO_MEDIA directly. Contributed-under: TianoCore Contribution Agreement 1.0 Signed-off-by: Hao Wu <hao.a.wu@intel.com> Reviewed-by: Feng Tian <feng.tian@intel.com> git-svn-id: https://svn.code.sf.net/p/edk2/code/trunk/edk2@19446 6f19259b-4bc3-4df7-8a09-765794883524
		
			
				
	
	
		
			1281 lines
		
	
	
		
			43 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			1281 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->MediaPresent)) {
 | 
						|
    return EFI_NO_MEDIA;
 | 
						|
  }
 | 
						|
 | 
						|
  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;
 | 
						|
}
 |