Home | History | Annotate | Download | only in XenPvBlkDxe
      1 /** @file
      2   BlockIo implementation for Xen PV Block driver.
      3 
      4   This file is implementing the interface between the actual driver in
      5   BlockFront.c to the BlockIo protocol.
      6 
      7   Copyright (C) 2014, Citrix Ltd.
      8 
      9   This program and the accompanying materials
     10   are licensed and made available under the terms and conditions of the BSD License
     11   which accompanies this distribution.  The full text of the license may be found at
     12   http://opensource.org/licenses/bsd-license.php
     13 
     14   THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
     15   WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
     16 
     17 **/
     18 
     19 #include "XenPvBlkDxe.h"
     20 
     21 #include "BlockFront.h"
     22 
     23 ///
     24 /// Block I/O Media structure
     25 ///
     26 GLOBAL_REMOVE_IF_UNREFERENCED
     27 EFI_BLOCK_IO_MEDIA  gXenPvBlkDxeBlockIoMedia = {
     28   0,      // MediaId
     29   FALSE,  // RemovableMedia
     30   FALSE,  // MediaPresent
     31   FALSE,  // LogicalPartition
     32   TRUE,   // ReadOnly
     33   FALSE,  // WriteCaching
     34   512,    // BlockSize
     35   512,    // IoAlign, BlockFront does not support less than 512 bits-aligned.
     36   0,      // LastBlock
     37   0,      // LowestAlignedLba
     38   0,      // LogicalBlocksPerPhysicalBlock
     39   0       // OptimalTransferLengthGranularity
     40 };
     41 
     42 ///
     43 /// Block I/O Protocol instance
     44 ///
     45 GLOBAL_REMOVE_IF_UNREFERENCED
     46 EFI_BLOCK_IO_PROTOCOL  gXenPvBlkDxeBlockIo = {
     47   EFI_BLOCK_IO_PROTOCOL_REVISION3,          // Revision
     48   &gXenPvBlkDxeBlockIoMedia,                // Media
     49   XenPvBlkDxeBlockIoReset,                  // Reset
     50   XenPvBlkDxeBlockIoReadBlocks,             // ReadBlocks
     51   XenPvBlkDxeBlockIoWriteBlocks,            // WriteBlocks
     52   XenPvBlkDxeBlockIoFlushBlocks             // FlushBlocks
     53 };
     54 
     55 
     56 
     57 
     58 /**
     59   Read/Write BufferSize bytes from Lba into Buffer.
     60 
     61   This function is commun to XenPvBlkDxeBlockIoReadBlocks and
     62   XenPvBlkDxeBlockIoWriteBlocks.
     63 
     64   @param  This       Indicates a pointer to the calling context.
     65   @param  MediaId    Id of the media, changes every time the media is replaced.
     66   @param  Lba        The starting Logical Block Address to read from/write to.
     67   @param  BufferSize Size of Buffer, must be a multiple of device block size.
     68   @param  Buffer     A pointer to the destination/source buffer for the data.
     69   @param  IsWrite    Indicate if the operation is write or read.
     70 
     71   @return See description of XenPvBlkDxeBlockIoReadBlocks and
     72           XenPvBlkDxeBlockIoWriteBlocks.
     73 **/
     74 STATIC
     75 EFI_STATUS
     76 XenPvBlkDxeBlockIoReadWriteBlocks (
     77   IN     EFI_BLOCK_IO_PROTOCOL  *This,
     78   IN     UINT32                 MediaId,
     79   IN     EFI_LBA                Lba,
     80   IN     UINTN                  BufferSize,
     81   IN OUT VOID                   *Buffer,
     82   IN     BOOLEAN                IsWrite
     83   )
     84 {
     85   XEN_BLOCK_FRONT_IO IoData;
     86   EFI_BLOCK_IO_MEDIA *Media = This->Media;
     87   UINTN Sector;
     88   EFI_STATUS Status;
     89 
     90   if (Buffer == NULL) {
     91     return EFI_INVALID_PARAMETER;
     92   }
     93   if (BufferSize == 0) {
     94     return EFI_SUCCESS;
     95   }
     96 
     97   if (BufferSize % Media->BlockSize != 0) {
     98     DEBUG ((EFI_D_ERROR, "XenPvBlkDxe: Bad buffer size: 0x%Lx\n",
     99       (UINT64)BufferSize));
    100     return EFI_BAD_BUFFER_SIZE;
    101   }
    102 
    103   if (Lba > Media->LastBlock ||
    104       (BufferSize / Media->BlockSize) - 1 > Media->LastBlock - Lba) {
    105     DEBUG ((EFI_D_ERROR,
    106       "XenPvBlkDxe: %a with invalid LBA: 0x%Lx, size: 0x%Lx\n",
    107       IsWrite ? "Write" : "Read", Lba, (UINT64)BufferSize));
    108     return EFI_INVALID_PARAMETER;
    109   }
    110 
    111   if (IsWrite && Media->ReadOnly) {
    112     return EFI_WRITE_PROTECTED;
    113   }
    114 
    115   if ((Media->IoAlign > 1) && (UINTN)Buffer & (Media->IoAlign - 1)) {
    116     //
    117     // Grub2 does not appear to respect IoAlign of 512, so reallocate the
    118     // buffer here.
    119     //
    120     VOID *NewBuffer;
    121 
    122     //
    123     // Try again with a properly aligned buffer.
    124     //
    125     NewBuffer = AllocateAlignedPages((BufferSize + EFI_PAGE_SIZE) / EFI_PAGE_SIZE,
    126                                      Media->IoAlign);
    127     if (!IsWrite) {
    128       Status = XenPvBlkDxeBlockIoReadBlocks (This, MediaId,
    129                                              Lba, BufferSize, NewBuffer);
    130       CopyMem (Buffer, NewBuffer, BufferSize);
    131     } else {
    132       CopyMem (NewBuffer, Buffer, BufferSize);
    133       Status = XenPvBlkDxeBlockIoWriteBlocks (This, MediaId,
    134                                               Lba, BufferSize, NewBuffer);
    135     }
    136     FreeAlignedPages (NewBuffer, (BufferSize + EFI_PAGE_SIZE) / EFI_PAGE_SIZE);
    137     return Status;
    138   }
    139 
    140   IoData.Dev = XEN_BLOCK_FRONT_FROM_BLOCK_IO (This);
    141   Sector = (UINTN)MultU64x32 (Lba, Media->BlockSize / 512);
    142 
    143   while (BufferSize > 0) {
    144     if (((UINTN)Buffer & EFI_PAGE_MASK) == 0) {
    145       IoData.Size = MIN (BLKIF_MAX_SEGMENTS_PER_REQUEST * EFI_PAGE_SIZE,
    146                          BufferSize);
    147     } else {
    148       IoData.Size = MIN ((BLKIF_MAX_SEGMENTS_PER_REQUEST - 1) * EFI_PAGE_SIZE,
    149                          BufferSize);
    150     }
    151 
    152     IoData.Buffer = Buffer;
    153     IoData.Sector = Sector;
    154     BufferSize -= IoData.Size;
    155     Buffer = (VOID*) ((UINTN) Buffer + IoData.Size);
    156     Sector += IoData.Size / 512;
    157     Status = XenPvBlockIo (&IoData, IsWrite);
    158     if (EFI_ERROR (Status)) {
    159       DEBUG ((EFI_D_ERROR, "XenPvBlkDxe: Error during %a operation.\n",
    160               IsWrite ? "write" : "read"));
    161       return Status;
    162     }
    163   }
    164   return EFI_SUCCESS;
    165 }
    166 
    167 
    168 /**
    169   Read BufferSize bytes from Lba into Buffer.
    170 
    171   @param  This       Indicates a pointer to the calling context.
    172   @param  MediaId    Id of the media, changes every time the media is replaced.
    173   @param  Lba        The starting Logical Block Address to read from
    174   @param  BufferSize Size of Buffer, must be a multiple of device block size.
    175   @param  Buffer     A pointer to the destination buffer for the data. The caller is
    176                      responsible for either having implicit or explicit ownership of the buffer.
    177 
    178   @retval EFI_SUCCESS           The data was read correctly from the device.
    179   @retval EFI_DEVICE_ERROR      The device reported an error while performing the read.
    180   @retval EFI_NO_MEDIA          There is no media in the device.
    181   @retval EFI_MEDIA_CHANGED     The MediaId does not matched the current device.
    182   @retval EFI_BAD_BUFFER_SIZE   The Buffer was not a multiple of the block size of the device.
    183   @retval EFI_INVALID_PARAMETER The read request contains LBAs that are not valid,
    184                                 or the buffer is not on proper alignment.
    185 
    186 **/
    187 EFI_STATUS
    188 EFIAPI
    189 XenPvBlkDxeBlockIoReadBlocks (
    190   IN  EFI_BLOCK_IO_PROTOCOL         *This,
    191   IN  UINT32                        MediaId,
    192   IN  EFI_LBA                       Lba,
    193   IN  UINTN                         BufferSize,
    194   OUT VOID                          *Buffer
    195   )
    196 {
    197   return XenPvBlkDxeBlockIoReadWriteBlocks (This,
    198       MediaId, Lba, BufferSize, Buffer, FALSE);
    199 }
    200 
    201 /**
    202   Write BufferSize bytes from Lba into Buffer.
    203 
    204   @param  This       Indicates a pointer to the calling context.
    205   @param  MediaId    The media ID that the write request is for.
    206   @param  Lba        The starting logical block address to be written. The caller is
    207                      responsible for writing to only legitimate locations.
    208   @param  BufferSize Size of Buffer, must be a multiple of device block size.
    209   @param  Buffer     A pointer to the source buffer for the data.
    210 
    211   @retval EFI_SUCCESS           The data was written correctly to the device.
    212   @retval EFI_WRITE_PROTECTED   The device can not be written to.
    213   @retval EFI_DEVICE_ERROR      The device reported an error while performing the write.
    214   @retval EFI_NO_MEDIA          There is no media in the device.
    215   @retval EFI_MEDIA_CHNAGED     The MediaId does not matched the current device.
    216   @retval EFI_BAD_BUFFER_SIZE   The Buffer was not a multiple of the block size of the device.
    217   @retval EFI_INVALID_PARAMETER The write request contains LBAs that are not valid,
    218                                 or the buffer is not on proper alignment.
    219 
    220 **/
    221 EFI_STATUS
    222 EFIAPI
    223 XenPvBlkDxeBlockIoWriteBlocks (
    224   IN EFI_BLOCK_IO_PROTOCOL          *This,
    225   IN UINT32                         MediaId,
    226   IN EFI_LBA                        Lba,
    227   IN UINTN                          BufferSize,
    228   IN VOID                           *Buffer
    229   )
    230 {
    231   return XenPvBlkDxeBlockIoReadWriteBlocks (This,
    232       MediaId, Lba, BufferSize, Buffer, TRUE);
    233 }
    234 
    235 /**
    236   Flush the Block Device.
    237 
    238   @param  This              Indicates a pointer to the calling context.
    239 
    240   @retval EFI_SUCCESS       All outstanding data was written to the device
    241   @retval EFI_DEVICE_ERROR  The device reported an error while writting back the data
    242   @retval EFI_NO_MEDIA      There is no media in the device.
    243 
    244 **/
    245 EFI_STATUS
    246 EFIAPI
    247 XenPvBlkDxeBlockIoFlushBlocks (
    248   IN EFI_BLOCK_IO_PROTOCOL  *This
    249   )
    250 {
    251   XenPvBlockSync (XEN_BLOCK_FRONT_FROM_BLOCK_IO (This));
    252   return EFI_SUCCESS;
    253 }
    254 
    255 /**
    256   Reset the block device hardware.
    257 
    258   @param[in]  This                 Indicates a pointer to the calling context.
    259   @param[in]  ExtendedVerification Not used.
    260 
    261   @retval EFI_SUCCESS          The device was reset.
    262 
    263 **/
    264 EFI_STATUS
    265 EFIAPI
    266 XenPvBlkDxeBlockIoReset (
    267   IN EFI_BLOCK_IO_PROTOCOL   *This,
    268   IN BOOLEAN                 ExtendedVerification
    269   )
    270 {
    271   //
    272   // Since the initialization of the devices is done, then the device is
    273   // working correctly.
    274   //
    275   return EFI_SUCCESS;
    276 }
    277