git-svn-id: https://edk2.svn.sourceforge.net/svnroot/edk2/trunk/edk2@2480 6f19259b-4bc3-4df7-8a09-765794883524
		
			
				
	
	
		
			730 lines
		
	
	
		
			20 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			730 lines
		
	
	
		
			20 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
/*++
 | 
						|
 | 
						|
Copyright (c) 2006, Intel Corporation
 | 
						|
All rights reserved. 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.
 | 
						|
 | 
						|
Module Name:
 | 
						|
 | 
						|
  DiskIo.c
 | 
						|
 | 
						|
Abstract:
 | 
						|
 | 
						|
  DiskIo driver that layers it's self on every Block IO protocol in the system.
 | 
						|
  DiskIo converts a block oriented device to a byte oriented device.
 | 
						|
 | 
						|
  ReadDisk may have to do 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.
 | 
						|
 | 
						|
--*/
 | 
						|
 | 
						|
#include "DiskIo.h"
 | 
						|
 | 
						|
EFI_DRIVER_BINDING_PROTOCOL gDiskIoDriverBinding = {
 | 
						|
  DiskIoDriverBindingSupported,
 | 
						|
  DiskIoDriverBindingStart,
 | 
						|
  DiskIoDriverBindingStop,
 | 
						|
  0xa,
 | 
						|
  NULL,
 | 
						|
  NULL
 | 
						|
};
 | 
						|
 | 
						|
DISK_IO_PRIVATE_DATA        gDiskIoPrivateDataTemplate = {
 | 
						|
  DISK_IO_PRIVATE_DATA_SIGNATURE,
 | 
						|
  {
 | 
						|
    EFI_DISK_IO_PROTOCOL_REVISION,
 | 
						|
    DiskIoReadDisk,
 | 
						|
    DiskIoWriteDisk
 | 
						|
  },
 | 
						|
  NULL
 | 
						|
};
 | 
						|
 | 
						|
EFI_STATUS
 | 
						|
EFIAPI
 | 
						|
DiskIoDriverBindingSupported (
 | 
						|
  IN EFI_DRIVER_BINDING_PROTOCOL  *This,
 | 
						|
  IN EFI_HANDLE                   ControllerHandle,
 | 
						|
  IN EFI_DEVICE_PATH_PROTOCOL     *RemainingDevicePath OPTIONAL
 | 
						|
  )
 | 
						|
/*++
 | 
						|
 | 
						|
  Routine Description:
 | 
						|
    Test to see if this driver supports ControllerHandle. Any ControllerHandle
 | 
						|
    than contains a BlockIo protocol can be supported.
 | 
						|
 | 
						|
  Arguments:
 | 
						|
    This                - Protocol instance pointer.
 | 
						|
    ControllerHandle    - Handle of device to test.
 | 
						|
    RemainingDevicePath - Not used.
 | 
						|
 | 
						|
  Returns:
 | 
						|
    EFI_SUCCESS         - This driver supports this device.
 | 
						|
    EFI_ALREADY_STARTED - This driver is already running on this device.
 | 
						|
    other               - This driver does not support this device.
 | 
						|
 | 
						|
--*/
 | 
						|
{
 | 
						|
  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;
 | 
						|
}
 | 
						|
 | 
						|
EFI_STATUS
 | 
						|
EFIAPI
 | 
						|
DiskIoDriverBindingStart (
 | 
						|
  IN EFI_DRIVER_BINDING_PROTOCOL  *This,
 | 
						|
  IN EFI_HANDLE                   ControllerHandle,
 | 
						|
  IN EFI_DEVICE_PATH_PROTOCOL     *RemainingDevicePath OPTIONAL
 | 
						|
  )
 | 
						|
/*++
 | 
						|
 | 
						|
  Routine Description:
 | 
						|
    Start this driver on ControllerHandle by opening a Block IO protocol and
 | 
						|
    installing a Disk IO protocol on ControllerHandle.
 | 
						|
 | 
						|
  Arguments:
 | 
						|
    This                - Protocol instance pointer.
 | 
						|
    ControllerHandle    - Handle of device to bind driver to.
 | 
						|
    RemainingDevicePath - Not used, always produce all possible children.
 | 
						|
 | 
						|
  Returns:
 | 
						|
    EFI_SUCCESS         - This driver is added to ControllerHandle.
 | 
						|
    EFI_ALREADY_STARTED - This driver is already running on ControllerHandle.
 | 
						|
    other               - This driver does not support this device.
 | 
						|
 | 
						|
--*/
 | 
						|
{
 | 
						|
  EFI_STATUS            Status;
 | 
						|
  DISK_IO_PRIVATE_DATA  *Private;
 | 
						|
 | 
						|
  Private = NULL;
 | 
						|
 | 
						|
  //
 | 
						|
  // Connect to the Block IO interface on ControllerHandle.
 | 
						|
  //
 | 
						|
  Status = gBS->OpenProtocol (
 | 
						|
                  ControllerHandle,
 | 
						|
                  &gEfiBlockIoProtocolGuid,
 | 
						|
                  (VOID **) &gDiskIoPrivateDataTemplate.BlockIo,
 | 
						|
                  This->DriverBindingHandle,
 | 
						|
                  ControllerHandle,
 | 
						|
                  EFI_OPEN_PROTOCOL_BY_DRIVER
 | 
						|
                  );
 | 
						|
  if (EFI_ERROR (Status)) {
 | 
						|
    return Status;
 | 
						|
  }
 | 
						|
  //
 | 
						|
  // Initialize the Disk IO device instance.
 | 
						|
  //
 | 
						|
  Private = AllocateCopyPool (sizeof (DISK_IO_PRIVATE_DATA), &gDiskIoPrivateDataTemplate);
 | 
						|
  if (Private == NULL) {
 | 
						|
    Status = EFI_OUT_OF_RESOURCES;
 | 
						|
    goto ErrorExit;
 | 
						|
  }
 | 
						|
  //
 | 
						|
  // Install protocol interfaces for the Disk IO device.
 | 
						|
  //
 | 
						|
  Status = gBS->InstallProtocolInterface (
 | 
						|
                  &ControllerHandle,
 | 
						|
                  &gEfiDiskIoProtocolGuid,
 | 
						|
                  EFI_NATIVE_INTERFACE,
 | 
						|
                  &Private->DiskIo
 | 
						|
                  );
 | 
						|
 | 
						|
ErrorExit:
 | 
						|
  if (EFI_ERROR (Status)) {
 | 
						|
 | 
						|
    if (Private != NULL) {
 | 
						|
      FreePool (Private);
 | 
						|
    }
 | 
						|
 | 
						|
    gBS->CloseProtocol (
 | 
						|
          ControllerHandle,
 | 
						|
          &gEfiBlockIoProtocolGuid,
 | 
						|
          This->DriverBindingHandle,
 | 
						|
          ControllerHandle
 | 
						|
          );
 | 
						|
  }
 | 
						|
 | 
						|
  return Status;
 | 
						|
}
 | 
						|
 | 
						|
EFI_STATUS
 | 
						|
EFIAPI
 | 
						|
DiskIoDriverBindingStop (
 | 
						|
  IN  EFI_DRIVER_BINDING_PROTOCOL    *This,
 | 
						|
  IN  EFI_HANDLE                     ControllerHandle,
 | 
						|
  IN  UINTN                          NumberOfChildren,
 | 
						|
  IN  EFI_HANDLE                     *ChildHandleBuffer
 | 
						|
  )
 | 
						|
/*++
 | 
						|
 | 
						|
  Routine Description:
 | 
						|
    Stop this driver on ControllerHandle by removing Disk IO protocol and closing
 | 
						|
    the Block IO protocol on ControllerHandle.
 | 
						|
 | 
						|
  Arguments:
 | 
						|
    This              - Protocol instance pointer.
 | 
						|
    ControllerHandle  - Handle of device to stop driver on.
 | 
						|
    NumberOfChildren  - Not used.
 | 
						|
    ChildHandleBuffer - Not used.
 | 
						|
 | 
						|
  Returns:
 | 
						|
    EFI_SUCCESS         - This driver is removed ControllerHandle.
 | 
						|
    other               - This driver was not removed from this device.
 | 
						|
    EFI_UNSUPPORTED
 | 
						|
 | 
						|
--*/
 | 
						|
{
 | 
						|
  EFI_STATUS            Status;
 | 
						|
  EFI_DISK_IO_PROTOCOL  *DiskIo;
 | 
						|
  DISK_IO_PRIVATE_DATA  *Private;
 | 
						|
 | 
						|
  //
 | 
						|
  // Get our context back.
 | 
						|
  //
 | 
						|
  Status = gBS->OpenProtocol (
 | 
						|
                  ControllerHandle,
 | 
						|
                  &gEfiDiskIoProtocolGuid,
 | 
						|
                  (VOID **) &DiskIo,
 | 
						|
                  This->DriverBindingHandle,
 | 
						|
                  ControllerHandle,
 | 
						|
                  EFI_OPEN_PROTOCOL_GET_PROTOCOL
 | 
						|
                  );
 | 
						|
  if (EFI_ERROR (Status)) {
 | 
						|
    return EFI_UNSUPPORTED;
 | 
						|
  }
 | 
						|
 | 
						|
  Private = DISK_IO_PRIVATE_DATA_FROM_THIS (DiskIo);
 | 
						|
 | 
						|
  Status = gBS->UninstallProtocolInterface (
 | 
						|
                  ControllerHandle,
 | 
						|
                  &gEfiDiskIoProtocolGuid,
 | 
						|
                  &Private->DiskIo
 | 
						|
                  );
 | 
						|
  if (!EFI_ERROR (Status)) {
 | 
						|
 | 
						|
    Status = gBS->CloseProtocol (
 | 
						|
                    ControllerHandle,
 | 
						|
                    &gEfiBlockIoProtocolGuid,
 | 
						|
                    This->DriverBindingHandle,
 | 
						|
                    ControllerHandle
 | 
						|
                    );
 | 
						|
  }
 | 
						|
 | 
						|
  if (!EFI_ERROR (Status)) {
 | 
						|
    FreePool (Private);
 | 
						|
  }
 | 
						|
 | 
						|
  return Status;
 | 
						|
}
 | 
						|
 | 
						|
EFI_STATUS
 | 
						|
EFIAPI
 | 
						|
DiskIoReadDisk (
 | 
						|
  IN EFI_DISK_IO_PROTOCOL  *This,
 | 
						|
  IN UINT32                MediaId,
 | 
						|
  IN UINT64                Offset,
 | 
						|
  IN UINTN                 BufferSize,
 | 
						|
  OUT VOID                 *Buffer
 | 
						|
  )
 | 
						|
/*++
 | 
						|
 | 
						|
  Routine Description:
 | 
						|
    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.
 | 
						|
 | 
						|
 | 
						|
  Arguments:
 | 
						|
    This       - Protocol instance pointer.
 | 
						|
    MediaId    - Id of the media, changes every time the media is replaced.
 | 
						|
    Offset     - The starting byte offset to read from.
 | 
						|
    BufferSize - Size of Buffer.
 | 
						|
    Buffer     - Buffer containing read data.
 | 
						|
 | 
						|
  Returns:
 | 
						|
    EFI_SUCCESS           - The data was read correctly from the device.
 | 
						|
    EFI_DEVICE_ERROR      - The device reported an error while performing the read.
 | 
						|
    EFI_NO_MEDIA          - There is no media in the device.
 | 
						|
    EFI_MEDIA_CHNAGED     - The MediaId does not matched the current device.
 | 
						|
    EFI_INVALID_PARAMETER - The read request contains device addresses that are not
 | 
						|
                            valid for the device.
 | 
						|
    EFI_OUT_OF_RESOURCES
 | 
						|
 | 
						|
--*/
 | 
						|
{
 | 
						|
  EFI_STATUS            Status;
 | 
						|
  DISK_IO_PRIVATE_DATA  *Private;
 | 
						|
  EFI_BLOCK_IO_PROTOCOL *BlockIo;
 | 
						|
  EFI_BLOCK_IO_MEDIA    *Media;
 | 
						|
  UINT32                BlockSize;
 | 
						|
  UINT64                Lba;
 | 
						|
  UINT64                OverRunLba;
 | 
						|
  UINT32                UnderRun;
 | 
						|
  UINT32                OverRun;
 | 
						|
  BOOLEAN               TransactionComplete;
 | 
						|
  UINTN                 WorkingBufferSize;
 | 
						|
  UINT8                 *WorkingBuffer;
 | 
						|
  UINTN                 Length;
 | 
						|
  UINT8                 *Data;
 | 
						|
  UINT8                 *PreData;
 | 
						|
  UINTN                 IsBufferAligned;
 | 
						|
  UINTN                 DataBufferSize;
 | 
						|
  BOOLEAN               LastRead;
 | 
						|
 | 
						|
  Private   = DISK_IO_PRIVATE_DATA_FROM_THIS (This);
 | 
						|
 | 
						|
  BlockIo   = Private->BlockIo;
 | 
						|
  Media     = BlockIo->Media;
 | 
						|
  BlockSize = Media->BlockSize;
 | 
						|
 | 
						|
  if (Media->MediaId != MediaId) {
 | 
						|
    return EFI_MEDIA_CHANGED;
 | 
						|
  }
 | 
						|
 | 
						|
  WorkingBuffer     = Buffer;
 | 
						|
  WorkingBufferSize = BufferSize;
 | 
						|
 | 
						|
  //
 | 
						|
  // Allocate a temporary buffer for operation
 | 
						|
  //
 | 
						|
  DataBufferSize = BlockSize * DATA_BUFFER_BLOCK_NUM;
 | 
						|
 | 
						|
  if (Media->IoAlign > 1) {
 | 
						|
    PreData = AllocatePool (DataBufferSize + Media->IoAlign);
 | 
						|
    Data    = PreData - ((UINTN) PreData & (Media->IoAlign - 1)) + Media->IoAlign;
 | 
						|
  } else {
 | 
						|
    PreData = AllocatePool (DataBufferSize);
 | 
						|
    Data    = PreData;
 | 
						|
  }
 | 
						|
 | 
						|
  if (PreData == NULL) {
 | 
						|
    return EFI_OUT_OF_RESOURCES;
 | 
						|
  }
 | 
						|
 | 
						|
  Lba                 = DivU64x32Remainder (Offset, BlockSize, &UnderRun);
 | 
						|
 | 
						|
  Length              = BlockSize - UnderRun;
 | 
						|
  TransactionComplete = FALSE;
 | 
						|
 | 
						|
  Status              = EFI_SUCCESS;
 | 
						|
  if (UnderRun != 0) {
 | 
						|
    //
 | 
						|
    // Offset starts in the middle of an Lba, so read the entire block.
 | 
						|
    //
 | 
						|
    Status = BlockIo->ReadBlocks (
 | 
						|
                        BlockIo,
 | 
						|
                        MediaId,
 | 
						|
                        Lba,
 | 
						|
                        BlockSize,
 | 
						|
                        Data
 | 
						|
                        );
 | 
						|
 | 
						|
    if (EFI_ERROR (Status)) {
 | 
						|
      goto Done;
 | 
						|
    }
 | 
						|
 | 
						|
    if (Length > BufferSize) {
 | 
						|
      Length              = BufferSize;
 | 
						|
      TransactionComplete = TRUE;
 | 
						|
    }
 | 
						|
 | 
						|
    CopyMem (WorkingBuffer, Data + UnderRun, Length);
 | 
						|
 | 
						|
    WorkingBuffer += Length;
 | 
						|
 | 
						|
    WorkingBufferSize -= Length;
 | 
						|
    if (WorkingBufferSize == 0) {
 | 
						|
      goto Done;
 | 
						|
    }
 | 
						|
 | 
						|
    Lba += 1;
 | 
						|
  }
 | 
						|
 | 
						|
  OverRunLba = Lba + DivU64x32Remainder (WorkingBufferSize, BlockSize, &OverRun);
 | 
						|
 | 
						|
  if (!TransactionComplete && WorkingBufferSize >= BlockSize) {
 | 
						|
    //
 | 
						|
    // If the DiskIo maps directly to a BlockIo device do the read.
 | 
						|
    //
 | 
						|
    if (OverRun != 0) {
 | 
						|
      WorkingBufferSize -= OverRun;
 | 
						|
    }
 | 
						|
    //
 | 
						|
    // Check buffer alignment
 | 
						|
    //
 | 
						|
    IsBufferAligned = (UINTN) WorkingBuffer & (UINTN) (Media->IoAlign - 1);
 | 
						|
 | 
						|
    if (Media->IoAlign <= 1 || IsBufferAligned == 0) {
 | 
						|
      //
 | 
						|
      // Alignment is satisfied, so read them together
 | 
						|
      //
 | 
						|
      Status = BlockIo->ReadBlocks (
 | 
						|
                          BlockIo,
 | 
						|
                          MediaId,
 | 
						|
                          Lba,
 | 
						|
                          WorkingBufferSize,
 | 
						|
                          WorkingBuffer
 | 
						|
                          );
 | 
						|
 | 
						|
      if (EFI_ERROR (Status)) {
 | 
						|
        goto Done;
 | 
						|
      }
 | 
						|
 | 
						|
      WorkingBuffer += WorkingBufferSize;
 | 
						|
 | 
						|
    } else {
 | 
						|
      //
 | 
						|
      // Use the allocated buffer instead of the original buffer
 | 
						|
      // to avoid alignment issue.
 | 
						|
      // Here, the allocated buffer (8-byte align) can satisfy the alignment
 | 
						|
      //
 | 
						|
      LastRead = FALSE;
 | 
						|
      do {
 | 
						|
        if (WorkingBufferSize <= DataBufferSize) {
 | 
						|
          //
 | 
						|
          // It is the last calling to readblocks in this loop
 | 
						|
          //
 | 
						|
          DataBufferSize  = WorkingBufferSize;
 | 
						|
          LastRead        = TRUE;
 | 
						|
        }
 | 
						|
 | 
						|
        Status = BlockIo->ReadBlocks (
 | 
						|
                            BlockIo,
 | 
						|
                            MediaId,
 | 
						|
                            Lba,
 | 
						|
                            DataBufferSize,
 | 
						|
                            Data
 | 
						|
                            );
 | 
						|
        if (EFI_ERROR (Status)) {
 | 
						|
          goto Done;
 | 
						|
        }
 | 
						|
 | 
						|
        CopyMem (WorkingBuffer, Data, DataBufferSize);
 | 
						|
        WorkingBufferSize -= DataBufferSize;
 | 
						|
        WorkingBuffer += DataBufferSize;
 | 
						|
        Lba += DATA_BUFFER_BLOCK_NUM;
 | 
						|
      } while (!LastRead);
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  if (!TransactionComplete && OverRun != 0) {
 | 
						|
    //
 | 
						|
    // Last read is not a complete block.
 | 
						|
    //
 | 
						|
    Status = BlockIo->ReadBlocks (
 | 
						|
                        BlockIo,
 | 
						|
                        MediaId,
 | 
						|
                        OverRunLba,
 | 
						|
                        BlockSize,
 | 
						|
                        Data
 | 
						|
                        );
 | 
						|
 | 
						|
    if (EFI_ERROR (Status)) {
 | 
						|
      goto Done;
 | 
						|
    }
 | 
						|
 | 
						|
    CopyMem (WorkingBuffer, Data, OverRun);
 | 
						|
  }
 | 
						|
 | 
						|
Done:
 | 
						|
  if (PreData != NULL) {
 | 
						|
    FreePool (PreData);
 | 
						|
  }
 | 
						|
 | 
						|
  return Status;
 | 
						|
}
 | 
						|
 | 
						|
EFI_STATUS
 | 
						|
EFIAPI
 | 
						|
DiskIoWriteDisk (
 | 
						|
  IN EFI_DISK_IO_PROTOCOL  *This,
 | 
						|
  IN UINT32                MediaId,
 | 
						|
  IN UINT64                Offset,
 | 
						|
  IN UINTN                 BufferSize,
 | 
						|
  IN VOID                  *Buffer
 | 
						|
  )
 | 
						|
/*++
 | 
						|
 | 
						|
  Routine Description:
 | 
						|
    Read BufferSize bytes from Offset into Buffer.
 | 
						|
 | 
						|
    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.
 | 
						|
 | 
						|
  Arguments:
 | 
						|
    This       - Protocol instance pointer.
 | 
						|
    MediaId    - Id of the media, changes every time the media is replaced.
 | 
						|
    Offset     - The starting byte offset to read from.
 | 
						|
    BufferSize - Size of Buffer.
 | 
						|
    Buffer     - Buffer containing read data.
 | 
						|
 | 
						|
  Returns:
 | 
						|
    EFI_SUCCESS           - The data was written correctly to the device.
 | 
						|
    EFI_WRITE_PROTECTED   - The device can not be written to.
 | 
						|
    EFI_DEVICE_ERROR      - The device reported an error while performing the write.
 | 
						|
    EFI_NO_MEDIA          - There is no media in the device.
 | 
						|
    EFI_MEDIA_CHNAGED     - The MediaId does not matched the current device.
 | 
						|
    EFI_INVALID_PARAMETER - The write request contains device addresses that are not
 | 
						|
                            valid for the device.
 | 
						|
    EFI_OUT_OF_RESOURCES
 | 
						|
 | 
						|
--*/
 | 
						|
{
 | 
						|
  EFI_STATUS            Status;
 | 
						|
  DISK_IO_PRIVATE_DATA  *Private;
 | 
						|
  EFI_BLOCK_IO_PROTOCOL *BlockIo;
 | 
						|
  EFI_BLOCK_IO_MEDIA    *Media;
 | 
						|
  UINT32                BlockSize;
 | 
						|
  UINT64                Lba;
 | 
						|
  UINT64                OverRunLba;
 | 
						|
  UINT32                UnderRun;
 | 
						|
  UINT32                OverRun;
 | 
						|
  BOOLEAN               TransactionComplete;
 | 
						|
  UINTN                 WorkingBufferSize;
 | 
						|
  UINT8                 *WorkingBuffer;
 | 
						|
  UINTN                 Length;
 | 
						|
  UINT8                 *Data;
 | 
						|
  UINT8                 *PreData;
 | 
						|
  UINTN                 IsBufferAligned;
 | 
						|
  UINTN                 DataBufferSize;
 | 
						|
  BOOLEAN               LastWrite;
 | 
						|
 | 
						|
  Private   = DISK_IO_PRIVATE_DATA_FROM_THIS (This);
 | 
						|
 | 
						|
  BlockIo   = Private->BlockIo;
 | 
						|
  Media     = BlockIo->Media;
 | 
						|
  BlockSize = Media->BlockSize;
 | 
						|
 | 
						|
  if (Media->ReadOnly) {
 | 
						|
    return EFI_WRITE_PROTECTED;
 | 
						|
  }
 | 
						|
 | 
						|
  if (Media->MediaId != MediaId) {
 | 
						|
    return EFI_MEDIA_CHANGED;
 | 
						|
  }
 | 
						|
 | 
						|
  DataBufferSize = BlockSize * DATA_BUFFER_BLOCK_NUM;
 | 
						|
 | 
						|
  if (Media->IoAlign > 1) {
 | 
						|
    PreData = AllocatePool (DataBufferSize + Media->IoAlign);
 | 
						|
    Data    = PreData - ((UINTN) PreData & (Media->IoAlign - 1)) + Media->IoAlign;
 | 
						|
  } else {
 | 
						|
    PreData = AllocatePool (DataBufferSize);
 | 
						|
    Data    = PreData;
 | 
						|
  }
 | 
						|
 | 
						|
  if (PreData == NULL) {
 | 
						|
    return EFI_OUT_OF_RESOURCES;
 | 
						|
  }
 | 
						|
 | 
						|
  WorkingBuffer       = Buffer;
 | 
						|
  WorkingBufferSize   = BufferSize;
 | 
						|
 | 
						|
  Lba                 = DivU64x32Remainder (Offset, BlockSize, &UnderRun);
 | 
						|
 | 
						|
  Length              = BlockSize - UnderRun;
 | 
						|
  TransactionComplete = FALSE;
 | 
						|
 | 
						|
  Status              = EFI_SUCCESS;
 | 
						|
  if (UnderRun != 0) {
 | 
						|
    //
 | 
						|
    // Offset starts in the middle of an Lba, so do read modify write.
 | 
						|
    //
 | 
						|
    Status = BlockIo->ReadBlocks (
 | 
						|
                        BlockIo,
 | 
						|
                        MediaId,
 | 
						|
                        Lba,
 | 
						|
                        BlockSize,
 | 
						|
                        Data
 | 
						|
                        );
 | 
						|
 | 
						|
    if (EFI_ERROR (Status)) {
 | 
						|
      goto Done;
 | 
						|
    }
 | 
						|
 | 
						|
    if (Length > BufferSize) {
 | 
						|
      Length              = BufferSize;
 | 
						|
      TransactionComplete = TRUE;
 | 
						|
    }
 | 
						|
 | 
						|
    CopyMem (Data + UnderRun, WorkingBuffer, Length);
 | 
						|
 | 
						|
    Status = BlockIo->WriteBlocks (
 | 
						|
                        BlockIo,
 | 
						|
                        MediaId,
 | 
						|
                        Lba,
 | 
						|
                        BlockSize,
 | 
						|
                        Data
 | 
						|
                        );
 | 
						|
    if (EFI_ERROR (Status)) {
 | 
						|
      goto Done;
 | 
						|
    }
 | 
						|
 | 
						|
    WorkingBuffer += Length;
 | 
						|
    WorkingBufferSize -= Length;
 | 
						|
    if (WorkingBufferSize == 0) {
 | 
						|
      goto Done;
 | 
						|
    }
 | 
						|
 | 
						|
    Lba += 1;
 | 
						|
  }
 | 
						|
 | 
						|
  OverRunLba = Lba + DivU64x32Remainder (WorkingBufferSize, BlockSize, &OverRun);
 | 
						|
 | 
						|
  if (!TransactionComplete && WorkingBufferSize >= BlockSize) {
 | 
						|
    //
 | 
						|
    // If the DiskIo maps directly to a BlockIo device do the write.
 | 
						|
    //
 | 
						|
    if (OverRun != 0) {
 | 
						|
      WorkingBufferSize -= OverRun;
 | 
						|
    }
 | 
						|
    //
 | 
						|
    // Check buffer alignment
 | 
						|
    //
 | 
						|
    IsBufferAligned = (UINTN) WorkingBuffer & (UINTN) (Media->IoAlign - 1);
 | 
						|
 | 
						|
    if (Media->IoAlign <= 1 || IsBufferAligned == 0) {
 | 
						|
      //
 | 
						|
      // Alignment is satisfied, so write them together
 | 
						|
      //
 | 
						|
      Status = BlockIo->WriteBlocks (
 | 
						|
                          BlockIo,
 | 
						|
                          MediaId,
 | 
						|
                          Lba,
 | 
						|
                          WorkingBufferSize,
 | 
						|
                          WorkingBuffer
 | 
						|
                          );
 | 
						|
 | 
						|
      if (EFI_ERROR (Status)) {
 | 
						|
        goto Done;
 | 
						|
      }
 | 
						|
 | 
						|
      WorkingBuffer += WorkingBufferSize;
 | 
						|
 | 
						|
    } else {
 | 
						|
      //
 | 
						|
      // The buffer parameter is not aligned with the request
 | 
						|
      // So use the allocated instead.
 | 
						|
      // It can fit almost all the cases.
 | 
						|
      //
 | 
						|
      LastWrite = FALSE;
 | 
						|
      do {
 | 
						|
        if (WorkingBufferSize <= DataBufferSize) {
 | 
						|
          //
 | 
						|
          // It is the last calling to writeblocks in this loop
 | 
						|
          //
 | 
						|
          DataBufferSize  = WorkingBufferSize;
 | 
						|
          LastWrite       = TRUE;
 | 
						|
        }
 | 
						|
 | 
						|
        CopyMem (Data, WorkingBuffer, DataBufferSize);
 | 
						|
        Status = BlockIo->WriteBlocks (
 | 
						|
                            BlockIo,
 | 
						|
                            MediaId,
 | 
						|
                            Lba,
 | 
						|
                            DataBufferSize,
 | 
						|
                            Data
 | 
						|
                            );
 | 
						|
        if (EFI_ERROR (Status)) {
 | 
						|
          goto Done;
 | 
						|
        }
 | 
						|
 | 
						|
        WorkingBufferSize -= DataBufferSize;
 | 
						|
        WorkingBuffer += DataBufferSize;
 | 
						|
        Lba += DATA_BUFFER_BLOCK_NUM;
 | 
						|
      } while (!LastWrite);
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  if (!TransactionComplete && OverRun != 0) {
 | 
						|
    //
 | 
						|
    // Last bit is not a complete block, so do a read modify write.
 | 
						|
    //
 | 
						|
    Status = BlockIo->ReadBlocks (
 | 
						|
                        BlockIo,
 | 
						|
                        MediaId,
 | 
						|
                        OverRunLba,
 | 
						|
                        BlockSize,
 | 
						|
                        Data
 | 
						|
                        );
 | 
						|
 | 
						|
    if (EFI_ERROR (Status)) {
 | 
						|
      goto Done;
 | 
						|
    }
 | 
						|
 | 
						|
    CopyMem (Data, WorkingBuffer, OverRun);
 | 
						|
 | 
						|
    Status = BlockIo->WriteBlocks (
 | 
						|
                        BlockIo,
 | 
						|
                        MediaId,
 | 
						|
                        OverRunLba,
 | 
						|
                        BlockSize,
 | 
						|
                        Data
 | 
						|
                        );
 | 
						|
    if (EFI_ERROR (Status)) {
 | 
						|
      goto Done;
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
Done:
 | 
						|
  if (PreData != NULL) {
 | 
						|
    FreePool (PreData);
 | 
						|
  }
 | 
						|
 | 
						|
  return Status;
 | 
						|
}
 |