1 /** @file
2   Implementation of the EFI Block IO Protocol for ISA Floppy driver
3 
4 Copyright (c) 2006 - 2018, Intel Corporation. All rights reserved.<BR>
5 SPDX-License-Identifier: BSD-2-Clause-Patent
6 
7 **/
8 
9 #include "IsaFloppy.h"
10 
11 /**
12   Reset the Block Device.
13 
14   @param  This                 Indicates a pointer to the calling context.
15   @param  ExtendedVerification Driver may perform diagnostics on reset.
16 
17   @retval EFI_SUCCESS          The device was reset.
18   @retval EFI_DEVICE_ERROR     The device is not functioning properly and could
19                                not be reset.
20 **/
21 EFI_STATUS
22 EFIAPI
FdcReset(IN EFI_BLOCK_IO_PROTOCOL * This,IN BOOLEAN ExtendedVerification)23 FdcReset (
24   IN  EFI_BLOCK_IO_PROTOCOL  *This,
25   IN  BOOLEAN                ExtendedVerification
26   )
27 {
28   FDC_BLK_IO_DEV  *FdcDev;
29 
30   //
31   // Reset the Floppy Disk Controller
32   //
33   FdcDev = FDD_BLK_IO_FROM_THIS (This);
34 
35   REPORT_STATUS_CODE_WITH_DEVICE_PATH (
36     EFI_PROGRESS_CODE,
37     EFI_P_PC_RESET | EFI_PERIPHERAL_REMOVABLE_MEDIA,
38     FdcDev->DevicePath
39     );
40 
41   return FddReset (FdcDev);
42 }
43 
44 /**
45   Flush the Block Device.
46 
47   @param  This              Indicates a pointer to the calling context.
48 
49   @retval EFI_SUCCESS       All outstanding data was written to the device
50   @retval EFI_DEVICE_ERROR  The device reported an error while writting back the data
51   @retval EFI_NO_MEDIA      There is no media in the device.
52 
53 **/
54 EFI_STATUS
55 EFIAPI
FddFlushBlocks(IN EFI_BLOCK_IO_PROTOCOL * This)56 FddFlushBlocks (
57   IN  EFI_BLOCK_IO_PROTOCOL  *This
58   )
59 {
60   //
61   // Not supported yet
62   //
63   return EFI_SUCCESS;
64 }
65 
66 /**
67   Common report status code interface.
68 
69   @param This  Pointer of FDC_BLK_IO_DEV instance
70   @param Read  Read or write operation when error occurrs
71 **/
72 VOID
FddReportStatus(IN EFI_BLOCK_IO_PROTOCOL * This,IN BOOLEAN Read)73 FddReportStatus (
74   IN  EFI_BLOCK_IO_PROTOCOL  *This,
75   IN  BOOLEAN                Read
76   )
77 {
78   FDC_BLK_IO_DEV  *FdcDev;
79 
80   FdcDev = FDD_BLK_IO_FROM_THIS (This);
81 
82   REPORT_STATUS_CODE_WITH_DEVICE_PATH (
83     EFI_ERROR_CODE,
84     ((Read) ? EFI_P_EC_INPUT_ERROR : EFI_P_EC_OUTPUT_ERROR) | EFI_PERIPHERAL_REMOVABLE_MEDIA,
85     FdcDev->DevicePath
86     );
87 }
88 
89 /**
90   Read BufferSize bytes from Lba into Buffer.
91 
92   @param  This       Indicates a pointer to the calling context.
93   @param  MediaId    Id of the media, changes every time the media is replaced.
94   @param  Lba        The starting Logical Block Address to read from
95   @param  BufferSize Size of Buffer, must be a multiple of device block size.
96   @param  Buffer     A pointer to the destination buffer for the data. The caller is
97                      responsible for either having implicit or explicit ownership of the buffer.
98 
99   @retval EFI_SUCCESS           The data was read correctly from the device.
100   @retval EFI_DEVICE_ERROR      The device reported an error while performing the read.
101   @retval EFI_NO_MEDIA          There is no media in the device.
102   @retval EFI_MEDIA_CHANGED     The MediaId does not matched the current device.
103   @retval EFI_BAD_BUFFER_SIZE   The Buffer was not a multiple of the block size of the device.
104   @retval EFI_INVALID_PARAMETER The read request contains LBAs that are not valid,
105                                 or the buffer is not on proper alignment.
106 
107 **/
108 EFI_STATUS
109 EFIAPI
FddReadBlocks(IN EFI_BLOCK_IO_PROTOCOL * This,IN UINT32 MediaId,IN EFI_LBA Lba,IN UINTN BufferSize,OUT VOID * Buffer)110 FddReadBlocks (
111   IN  EFI_BLOCK_IO_PROTOCOL  *This,
112   IN  UINT32                 MediaId,
113   IN  EFI_LBA                Lba,
114   IN  UINTN                  BufferSize,
115   OUT VOID                   *Buffer
116   )
117 {
118   EFI_STATUS  Status;
119 
120   Status = FddReadWriteBlocks (This, MediaId, Lba, BufferSize, READ, Buffer);
121 
122   if (EFI_ERROR (Status)) {
123     FddReportStatus (This, TRUE);
124   }
125 
126   return Status;
127 }
128 
129 /**
130   Write BufferSize bytes from Lba into Buffer.
131 
132   @param  This       Indicates a pointer to the calling context.
133   @param  MediaId    The media ID that the write request is for.
134   @param  Lba        The starting logical block address to be written. The caller is
135                      responsible for writing to only legitimate locations.
136   @param  BufferSize Size of Buffer, must be a multiple of device block size.
137   @param  Buffer     A pointer to the source buffer for the data.
138 
139   @retval EFI_SUCCESS           The data was written correctly to the device.
140   @retval EFI_WRITE_PROTECTED   The device can not be written to.
141   @retval EFI_DEVICE_ERROR      The device reported an error while performing the write.
142   @retval EFI_NO_MEDIA          There is no media in the device.
143   @retval EFI_MEDIA_CHNAGED     The MediaId does not matched the current device.
144   @retval EFI_BAD_BUFFER_SIZE   The Buffer was not a multiple of the block size of the device.
145   @retval EFI_INVALID_PARAMETER The write request contains LBAs that are not valid,
146                                 or the buffer is not on proper alignment.
147 
148 **/
149 EFI_STATUS
150 EFIAPI
FddWriteBlocks(IN EFI_BLOCK_IO_PROTOCOL * This,IN UINT32 MediaId,IN EFI_LBA Lba,IN UINTN BufferSize,IN VOID * Buffer)151 FddWriteBlocks (
152   IN EFI_BLOCK_IO_PROTOCOL  *This,
153   IN UINT32                 MediaId,
154   IN EFI_LBA                Lba,
155   IN UINTN                  BufferSize,
156   IN VOID                   *Buffer
157   )
158 {
159   EFI_STATUS  Status;
160 
161   Status = FddReadWriteBlocks (This, MediaId, Lba, BufferSize, WRITE, Buffer);
162 
163   if (EFI_ERROR (Status)) {
164     FddReportStatus (This, FALSE);
165   }
166 
167   return Status;
168 }
169 
170 /**
171   Read or Write a number of blocks to floppy disk
172 
173   @param  This       Indicates a pointer to the calling context.
174   @param  MediaId    Id of the media, changes every time the media is replaced.
175   @param  Lba        The starting Logical Block Address to read from
176   @param  BufferSize Size of Buffer, must be a multiple of device block size.
177   @param  Operation  Specifies the read or write operation.
178   @param  Buffer     A pointer to the destination buffer for the data. The caller is
179                      responsible for either having implicit or explicit ownership of the buffer.
180 
181   @retval EFI_SUCCESS           The data was read correctly from the device.
182   @retval EFI_DEVICE_ERROR      The device reported an error while performing the read.
183   @retval EFI_NO_MEDIA          There is no media in the device.
184   @retval EFI_MEDIA_CHANGED     The MediaId does not matched the current device.
185   @retval EFI_BAD_BUFFER_SIZE   The Buffer was not a multiple of the block size of the device.
186   @retval EFI_INVALID_PARAMETER The read request contains LBAs that are not valid,
187                                 or the buffer is not on proper alignment.
188   @retval EFI_WRITE_PROTECTED   The device can not be written to.
189 
190 **/
191 EFI_STATUS
FddReadWriteBlocks(IN EFI_BLOCK_IO_PROTOCOL * This,IN UINT32 MediaId,IN EFI_LBA Lba,IN UINTN BufferSize,IN BOOLEAN Operation,OUT VOID * Buffer)192 FddReadWriteBlocks (
193   IN  EFI_BLOCK_IO_PROTOCOL  *This,
194   IN  UINT32                 MediaId,
195   IN  EFI_LBA                Lba,
196   IN  UINTN                  BufferSize,
197   IN  BOOLEAN                Operation,
198   OUT VOID                   *Buffer
199   )
200 {
201   EFI_BLOCK_IO_MEDIA  *Media;
202   FDC_BLK_IO_DEV      *FdcDev;
203   UINTN               BlockSize;
204   UINTN               NumberOfBlocks;
205   UINTN               BlockCount;
206   EFI_STATUS          Status;
207   EFI_LBA             Lba0;
208   UINT8               *Pointer;
209 
210   //
211   // Get the intrinsic block size
212   //
213   Media     = This->Media;
214   BlockSize = Media->BlockSize;
215   FdcDev    = FDD_BLK_IO_FROM_THIS (This);
216 
217   if (Operation == WRITE) {
218     if (Lba == 0) {
219       FdcFreeCache (FdcDev);
220     }
221   }
222 
223   //
224   // Set the drive motor on
225   //
226   Status = MotorOn (FdcDev);
227   if (EFI_ERROR (Status)) {
228     return EFI_DEVICE_ERROR;
229   }
230   //
231   // Check to see if media can be detected
232   //
233   Status = DetectMedia (FdcDev);
234   if (EFI_ERROR (Status)) {
235     MotorOff (FdcDev);
236     FdcFreeCache (FdcDev);
237     return EFI_DEVICE_ERROR;
238   }
239   //
240   // Check to see if media is present
241   //
242   if (!(Media->MediaPresent)) {
243     MotorOff (FdcDev);
244     FdcFreeCache (FdcDev);
245     return EFI_NO_MEDIA;
246   }
247   //
248   // Check to see if media has been changed
249   //
250   if (MediaId != Media->MediaId) {
251     MotorOff (FdcDev);
252     FdcFreeCache (FdcDev);
253     return EFI_MEDIA_CHANGED;
254   }
255 
256   if (BufferSize == 0) {
257     MotorOff (FdcDev);
258     return EFI_SUCCESS;
259   }
260 
261   if (Operation == WRITE) {
262     if (Media->ReadOnly) {
263       MotorOff (FdcDev);
264       return EFI_WRITE_PROTECTED;
265     }
266   }
267   //
268   // Check the parameters for this read/write operation
269   //
270   if (Buffer == NULL) {
271     MotorOff (FdcDev);
272     return EFI_INVALID_PARAMETER;
273   }
274 
275   if (BufferSize % BlockSize != 0) {
276     MotorOff (FdcDev);
277     return EFI_BAD_BUFFER_SIZE;
278   }
279 
280   if (Lba > Media->LastBlock) {
281     MotorOff (FdcDev);
282     return EFI_INVALID_PARAMETER;
283   }
284 
285   if (((BufferSize / BlockSize) + Lba - 1) > Media->LastBlock) {
286     MotorOff (FdcDev);
287     return EFI_INVALID_PARAMETER;
288   }
289 
290   if (Operation == READ) {
291     //
292     // See if the data that is being read is already in the cache
293     //
294     if (FdcDev->Cache != NULL) {
295       if (Lba == 0 && BufferSize == BlockSize) {
296         MotorOff (FdcDev);
297         CopyMem ((UINT8 *) Buffer, (UINT8 *) FdcDev->Cache, BlockSize);
298         return EFI_SUCCESS;
299       }
300     }
301   }
302   //
303   // Set up Floppy Disk Controller
304   //
305   Status = Setup (FdcDev);
306   if (EFI_ERROR (Status)) {
307     MotorOff (FdcDev);
308     return EFI_DEVICE_ERROR;
309   }
310 
311   NumberOfBlocks  = BufferSize / BlockSize;
312   Lba0            = Lba;
313   Pointer         = Buffer;
314 
315   //
316   // read blocks in the same cylinder.
317   // in a cylinder , there are 18 * 2 = 36 blocks
318   //
319   BlockCount = GetTransferBlockCount (FdcDev, Lba, NumberOfBlocks);
320   while ((BlockCount != 0) && !EFI_ERROR (Status)) {
321     Status = ReadWriteDataSector (FdcDev, Buffer, Lba, BlockCount, Operation);
322     if (EFI_ERROR (Status)) {
323       MotorOff (FdcDev);
324       FddReset (FdcDev);
325       return EFI_DEVICE_ERROR;
326     }
327 
328     Lba += BlockCount;
329     NumberOfBlocks -= BlockCount;
330     Buffer      = (VOID *) ((UINTN) Buffer + BlockCount * BlockSize);
331     BlockCount  = GetTransferBlockCount (FdcDev, Lba, NumberOfBlocks);
332   }
333 
334   Buffer = Pointer;
335 
336   //
337   // Turn the motor off
338   //
339   MotorOff (FdcDev);
340 
341   if (Operation == READ) {
342     //
343     // Cache the data read
344     //
345     if (Lba0 == 0 && FdcDev->Cache == NULL) {
346       FdcDev->Cache = AllocateCopyPool (BlockSize, Buffer);
347     }
348   }
349 
350   return EFI_SUCCESS;
351 
352 }
353 
354 /**
355   Free cache for a floppy disk.
356 
357   @param FdcDev  A Pointer to FDC_BLK_IO_DEV instance
358 
359 **/
360 VOID
FdcFreeCache(IN FDC_BLK_IO_DEV * FdcDev)361 FdcFreeCache (
362   IN FDC_BLK_IO_DEV  *FdcDev
363   )
364 {
365   if (FdcDev->Cache != NULL) {
366     FreePool (FdcDev->Cache);
367     FdcDev->Cache = NULL;
368   }
369 }
370