• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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
XenPvBlkDxeBlockIoReadWriteBlocks(IN EFI_BLOCK_IO_PROTOCOL * This,IN UINT32 MediaId,IN EFI_LBA Lba,IN UINTN BufferSize,IN OUT VOID * Buffer,IN BOOLEAN IsWrite)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
XenPvBlkDxeBlockIoReadBlocks(IN EFI_BLOCK_IO_PROTOCOL * This,IN UINT32 MediaId,IN EFI_LBA Lba,IN UINTN BufferSize,OUT VOID * Buffer)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
XenPvBlkDxeBlockIoWriteBlocks(IN EFI_BLOCK_IO_PROTOCOL * This,IN UINT32 MediaId,IN EFI_LBA Lba,IN UINTN BufferSize,IN VOID * Buffer)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
XenPvBlkDxeBlockIoFlushBlocks(IN EFI_BLOCK_IO_PROTOCOL * This)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
XenPvBlkDxeBlockIoReset(IN EFI_BLOCK_IO_PROTOCOL * This,IN BOOLEAN ExtendedVerification)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