8df584506e0028431bcd5fe2af8a267458d060d7
[efi/edk2/.git] / edk2 / MdeModulePkg / Bus / Ata / AtaBusDxe / AtaPassThruExecute.c
1 /** @file\r
2   This file implements ATA pass through transaction for ATA bus driver.\r
3 \r
4   This file implements the low level execution of ATA pass through transaction.\r
5   It transforms the high level identity, read/write, reset command to ATA pass\r
6   through command and protocol. \r
7     \r
8   Copyright (c) 2009 - 2010 Intel Corporation. <BR>\r
9   All rights reserved. This program and the accompanying materials\r
10   are licensed and made available under the terms and conditions of the BSD License\r
11   which accompanies this distribution.  The full text of the license may be found at\r
12   http://opensource.org/licenses/bsd-license.php\r
13 \r
14   THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
15   WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
16 \r
17 \r
18 **/\r
19 \r
20 #include "AtaBus.h"\r
21 \r
22 //\r
23 // Look up table (UdmaValid, IsWrite) for EFI_ATA_PASS_THRU_CMD_PROTOCOL\r
24 //\r
25 EFI_ATA_PASS_THRU_CMD_PROTOCOL mAtaPassThruCmdProtocols[][2] = {\r
26   {\r
27     EFI_ATA_PASS_THRU_PROTOCOL_PIO_DATA_IN,\r
28     EFI_ATA_PASS_THRU_PROTOCOL_PIO_DATA_OUT\r
29   },\r
30   {\r
31     EFI_ATA_PASS_THRU_PROTOCOL_UDMA_DATA_IN,\r
32     EFI_ATA_PASS_THRU_PROTOCOL_UDMA_DATA_OUT,\r
33   }\r
34 };\r
35 \r
36 //\r
37 // Look up table (UdmaValid, Lba48Bit, IsIsWrite) for ATA_CMD\r
38 //\r
39 UINT8 mAtaCommands[][2][2] = {\r
40   {\r
41     {\r
42       ATA_CMD_READ_SECTORS,            // 28-bit LBA; PIO read\r
43       ATA_CMD_WRITE_SECTORS            // 28-bit LBA; PIO write\r
44     },\r
45     {\r
46       ATA_CMD_READ_SECTORS_EXT,        // 48-bit LBA; PIO read\r
47       ATA_CMD_WRITE_SECTORS_EXT        // 48-bit LBA; PIO write\r
48     }\r
49   },\r
50   {\r
51     {\r
52       ATA_CMD_READ_DMA,                // 28-bit LBA; DMA read\r
53       ATA_CMD_WRITE_DMA                // 28-bit LBA; DMA write\r
54     },\r
55     {\r
56       ATA_CMD_READ_DMA_EXT,            // 48-bit LBA; DMA read\r
57       ATA_CMD_WRITE_DMA_EXT            // 48-bit LBA; DMA write\r
58     }\r
59   }\r
60 };\r
61 \r
62 //\r
63 // Look up table (Lba48Bit) for maximum transfer block number\r
64 //\r
65 UINTN mMaxTransferBlockNumber[] = {\r
66   MAX_28BIT_TRANSFER_BLOCK_NUM,\r
67   MAX_48BIT_TRANSFER_BLOCK_NUM\r
68 };\r
69 \r
70 \r
71 /**\r
72   Wrapper for EFI_ATA_PASS_THRU_PROTOCOL.PassThru().\r
73 \r
74   This function wraps the PassThru() invocation for ATA pass through function\r
75   for an ATA device. It assembles the ATA pass through command packet for ATA\r
76   transaction.\r
77 \r
78   @param  AtaDevice         The ATA child device involved for the operation.\r
79 \r
80   @return The return status from EFI_ATA_PASS_THRU_PROTOCOL.PassThru().\r
81 \r
82 **/\r
83 EFI_STATUS\r
84 AtaDevicePassThru (\r
85   IN OUT ATA_DEVICE                       *AtaDevice\r
86   )\r
87 {\r
88   EFI_STATUS                              Status;\r
89   EFI_ATA_PASS_THRU_PROTOCOL              *AtaPassThru;\r
90   EFI_ATA_PASS_THRU_COMMAND_PACKET        *Packet;\r
91 \r
92   //\r
93   // Assemble packet\r
94   //\r
95   Packet = &AtaDevice->Packet;\r
96   Packet->Asb = AtaDevice->Asb;\r
97   Packet->Acb = &AtaDevice->Acb;\r
98   Packet->Timeout = ATA_TIMEOUT;\r
99 \r
100   AtaPassThru = AtaDevice->AtaBusDriverData->AtaPassThru;\r
101 \r
102   Status = AtaPassThru->PassThru (\r
103                           AtaPassThru,\r
104                           AtaDevice->Port,\r
105                           AtaDevice->PortMultiplierPort,\r
106                           Packet,\r
107                           NULL\r
108                           );\r
109   //\r
110   // Ensure ATA pass through caller and callee have the same\r
111   // interpretation of ATA pass through protocol. \r
112   //\r
113   ASSERT (Status != EFI_INVALID_PARAMETER);\r
114   ASSERT (Status != EFI_BAD_BUFFER_SIZE);\r
115 \r
116   return Status;\r
117 }\r
118 \r
119 \r
120 /**\r
121   Wrapper for EFI_ATA_PASS_THRU_PROTOCOL.ResetDevice().\r
122 \r
123   This function wraps the ResetDevice() invocation for ATA pass through function\r
124   for an ATA device. \r
125 \r
126   @param  AtaDevice         The ATA child device involved for the operation.\r
127 \r
128   @return The return status from EFI_ATA_PASS_THRU_PROTOCOL.PassThru().\r
129 \r
130 **/\r
131 EFI_STATUS\r
132 ResetAtaDevice (\r
133   IN ATA_DEVICE                           *AtaDevice\r
134   )\r
135 {\r
136   EFI_ATA_PASS_THRU_PROTOCOL              *AtaPassThru;\r
137   \r
138   AtaPassThru = AtaDevice->AtaBusDriverData->AtaPassThru;\r
139   \r
140   return AtaPassThru->ResetDevice (\r
141                         AtaPassThru,\r
142                         AtaDevice->Port,\r
143                         AtaDevice->PortMultiplierPort\r
144                         );\r
145 }\r
146 \r
147 \r
148 /**\r
149   Prints ATA model name to ATA device structure.\r
150 \r
151   This function converts ATA device model name from ATA identify data \r
152   to a string in ATA device structure. It needs to change the character\r
153   order in the original model name string.\r
154 \r
155   @param  AtaDevice         The ATA child device involved for the operation.\r
156 \r
157 **/\r
158 VOID\r
159 PrintAtaModelName (\r
160   IN OUT ATA_DEVICE  *AtaDevice\r
161   )\r
162 {\r
163   UINTN   Index;\r
164   CHAR8   *Source;\r
165   CHAR16  *Destination;\r
166 \r
167   Source = AtaDevice->IdentifyData->ModelName;\r
168   Destination = AtaDevice->ModelName;\r
169 \r
170   //\r
171   // Swap the byte order in the original module name.\r
172   //\r
173   for (Index = 0; Index < MAX_MODEL_NAME_LEN; Index += 2) {\r
174     Destination[Index]      = Source[Index + 1];\r
175     Destination[Index + 1]  = Source[Index];\r
176   }\r
177   AtaDevice->ModelName[MAX_MODEL_NAME_LEN] = L'\0';\r
178 }\r
179 \r
180 \r
181 /**\r
182   Gets ATA device Capacity according to ATA 6.\r
183 \r
184   This function returns the capacity of the ATA device if it follows\r
185   ATA 6 to support 48 bit addressing.\r
186 \r
187   @param  AtaDevice         The ATA child device involved for the operation.\r
188 \r
189   @return The capacity of the ATA device or 0 if the device does not support\r
190           48-bit addressing defined in ATA 6.\r
191 \r
192 **/\r
193 EFI_LBA\r
194 GetAtapi6Capacity (\r
195   IN ATA_DEVICE                 *AtaDevice\r
196   )\r
197 {\r
198   EFI_LBA                       Capacity;\r
199   EFI_LBA                       TmpLba;\r
200   UINTN                         Index;\r
201   ATA_IDENTIFY_DATA             *IdentifyData;\r
202 \r
203   IdentifyData = AtaDevice->IdentifyData;\r
204   if ((IdentifyData->command_set_supported_83 & BIT10) == 0) {\r
205     //\r
206     // The device doesn't support 48 bit addressing\r
207     //\r
208     return 0;\r
209   }\r
210 \r
211   //\r
212   // 48 bit address feature set is supported, get maximum capacity\r
213   //\r
214   Capacity = 0;\r
215   for (Index = 0; Index < 4; Index++) {\r
216     //\r
217     // Lower byte goes first: word[100] is the lowest word, word[103] is highest\r
218     //\r
219     TmpLba = IdentifyData->maximum_lba_for_48bit_addressing[Index];\r
220     Capacity |= LShiftU64 (TmpLba, 16 * Index);\r
221   }\r
222 \r
223   return Capacity;\r
224 }\r
225 \r
226 \r
227 /**\r
228   Identifies ATA device via the Identify data.\r
229 \r
230   This function identifies the ATA device and initializes the Media information in \r
231   Block IO protocol interface.\r
232 \r
233   @param  AtaDevice         The ATA child device involved for the operation.\r
234 \r
235   @retval EFI_UNSUPPORTED   The device is not a valid ATA device (hard disk).\r
236   @retval EFI_SUCCESS       The device is successfully identified and Media information\r
237                             is correctly initialized.\r
238 \r
239 **/\r
240 EFI_STATUS\r
241 IdentifyAtaDevice (\r
242   IN OUT ATA_DEVICE                 *AtaDevice\r
243   )\r
244 {\r
245   ATA_IDENTIFY_DATA                 *IdentifyData;\r
246   EFI_BLOCK_IO_MEDIA                *BlockMedia;\r
247   EFI_LBA                           Capacity;\r
248   UINT16                            PhyLogicSectorSupport;\r
249   UINT16                            UdmaMode;\r
250 \r
251   IdentifyData = AtaDevice->IdentifyData;\r
252 \r
253   if ((IdentifyData->config & BIT15) != 0) {\r
254     //\r
255     // This is not an hard disk\r
256     //\r
257     return EFI_UNSUPPORTED;\r
258   }\r
259 \r
260   //\r
261   // Check whether the WORD 88 (supported UltraDMA by drive) is valid\r
262   //\r
263   if ((IdentifyData->field_validity & BIT2) != 0) {\r
264     UdmaMode = IdentifyData->ultra_dma_mode;\r
265     if ((UdmaMode & (BIT0 | BIT1 | BIT2 | BIT3 | BIT4 | BIT5 | BIT6)) != 0) {\r
266       //\r
267       // If BIT0~BIT6 is selected, then UDMA is supported\r
268       //\r
269       AtaDevice->UdmaValid = TRUE;\r
270     }\r
271   }\r
272 \r
273   Capacity = GetAtapi6Capacity (AtaDevice);\r
274   if (Capacity > MAX_28BIT_ADDRESSING_CAPACITY) {\r
275     //\r
276     // Capacity exceeds 120GB. 48-bit addressing is really needed\r
277     //\r
278     AtaDevice->Lba48Bit = TRUE;\r
279   } else {\r
280     //\r
281     // This is a hard disk <= 120GB capacity, treat it as normal hard disk\r
282     //\r
283     Capacity = ((UINT32)IdentifyData->user_addressable_sectors_hi << 16) | IdentifyData->user_addressable_sectors_lo;\r
284     AtaDevice->Lba48Bit = FALSE;\r
285   }\r
286 \r
287   //\r
288   // Block Media Information:\r
289   //\r
290   BlockMedia = &AtaDevice->BlockMedia;\r
291   BlockMedia->LastBlock = Capacity - 1;\r
292   BlockMedia->IoAlign = AtaDevice->AtaBusDriverData->AtaPassThru->Mode->IoAlign;\r
293   //\r
294   // Check whether Long Physical Sector Feature is supported\r
295   //\r
296   PhyLogicSectorSupport = IdentifyData->phy_logic_sector_support;\r
297   if ((PhyLogicSectorSupport & (BIT14 | BIT15)) == BIT14) {\r
298     //\r
299     // Check whether one physical block contains multiple physical blocks\r
300     //\r
301     if ((PhyLogicSectorSupport & BIT13) != 0) {\r
302       BlockMedia->LogicalBlocksPerPhysicalBlock = (UINT32) (1 << (PhyLogicSectorSupport & 0x000f));\r
303       //\r
304       // Check lowest alignment of logical blocks within physical block\r
305       //\r
306       if ((IdentifyData->alignment_logic_in_phy_blocks & (BIT14 | BIT15)) == BIT14) {\r
307         BlockMedia->LowestAlignedLba = (EFI_LBA) (IdentifyData->alignment_logic_in_phy_blocks & 0x3fff);\r
308       }\r
309     }\r
310     //\r
311     // Check logical block size\r
312     //\r
313     if ((PhyLogicSectorSupport & BIT12) != 0) {\r
314       BlockMedia->BlockSize = (UINT32) (((IdentifyData->logic_sector_size_hi << 16) | IdentifyData->logic_sector_size_lo) * sizeof (UINT16));\r
315     }\r
316     AtaDevice->BlockIo.Revision = EFI_BLOCK_IO_PROTOCOL_REVISION2;\r
317   }\r
318   //\r
319   // Get ATA model name from identify data structure. \r
320   //\r
321   PrintAtaModelName (AtaDevice); \r
322 \r
323   return EFI_SUCCESS;\r
324 }\r
325 \r
326 \r
327 /**\r
328   Discovers whether it is a valid ATA device.\r
329 \r
330   This function issues ATA_CMD_IDENTIFY_DRIVE command to the ATA device to identify it.\r
331   If the command is executed successfully, it then identifies it and initializes\r
332   the Media information in Block IO protocol interface.\r
333 \r
334   @param  AtaDevice         The ATA child device involved for the operation.\r
335 \r
336   @retval EFI_SUCCESS       The device is successfully identified and Media information\r
337                             is correctly initialized.\r
338   @return others            Some error occurs when discovering the ATA device. \r
339 \r
340 **/\r
341 EFI_STATUS\r
342 DiscoverAtaDevice (\r
343   IN OUT ATA_DEVICE                 *AtaDevice\r
344   )\r
345 {\r
346   EFI_STATUS                        Status;\r
347   EFI_ATA_COMMAND_BLOCK             *Acb;\r
348   EFI_ATA_PASS_THRU_COMMAND_PACKET  *Packet;\r
349   UINTN                             Retry;\r
350 \r
351   //\r
352   // Prepare for ATA command block.\r
353   //\r
354   Acb = ZeroMem (&AtaDevice->Acb, sizeof (*Acb));\r
355   Acb->AtaCommand = ATA_CMD_IDENTIFY_DRIVE;\r
356 \r
357   //\r
358   // Prepare for ATA pass through packet.\r
359   //\r
360   Packet = ZeroMem (&AtaDevice->Packet, sizeof (*Packet));\r
361   Packet->InDataBuffer = AtaDevice->IdentifyData;\r
362   Packet->InTransferLength = sizeof (*AtaDevice->IdentifyData);\r
363   Packet->Protocol = EFI_ATA_PASS_THRU_PROTOCOL_PIO_DATA_IN;\r
364   Packet->Length = EFI_ATA_PASS_THRU_LENGTH_BYTES | EFI_ATA_PASS_THRU_LENGTH_SECTOR_COUNT;\r
365 \r
366   Retry = MAX_RETRY_TIMES;\r
367   do {\r
368     Status = AtaDevicePassThru (AtaDevice);\r
369     if (!EFI_ERROR (Status)) {\r
370       //\r
371       // The command is issued successfully\r
372       //\r
373       Status = IdentifyAtaDevice (AtaDevice);\r
374       if (!EFI_ERROR (Status)) {\r
375         return Status;\r
376       }\r
377     }\r
378   } while (Retry-- > 0);\r
379 \r
380   return Status;\r
381 }\r
382 \r
383 /**\r
384   Transfer data from ATA device.\r
385 \r
386   This function performs one ATA pass through transaction to transfer data from/to\r
387   ATA device. It chooses the appropriate ATA command and protocol to invoke PassThru\r
388   interface of ATA pass through.\r
389 \r
390   @param  AtaDevice         The ATA child device involved for the operation.\r
391   @param  Buffer            The pointer to the current transaction buffer.\r
392   @param  StartLba          The starting logical block address to be accessed.\r
393   @param  TransferLength    The block number or sector count of the transfer.\r
394   @param  IsWrite           Indicates whether it is a write operation.\r
395 \r
396   @retval EFI_SUCCESS       The data transfer is complete successfully.\r
397   @return others            Some error occurs when transferring data. \r
398 \r
399 **/\r
400 EFI_STATUS\r
401 TransferAtaDevice (\r
402   IN OUT ATA_DEVICE                 *AtaDevice,\r
403   IN OUT VOID                       *Buffer,\r
404   IN EFI_LBA                        StartLba,\r
405   IN UINT32                         TransferLength,\r
406   IN BOOLEAN                        IsWrite\r
407   )\r
408 {\r
409   EFI_ATA_COMMAND_BLOCK             *Acb;\r
410   EFI_ATA_PASS_THRU_COMMAND_PACKET  *Packet;\r
411 \r
412   //\r
413   // Ensure AtaDevice->UdmaValid, AtaDevice->Lba48Bit and IsWrite are valid boolean values \r
414   //\r
415   ASSERT ((UINTN) AtaDevice->UdmaValid < 2);\r
416   ASSERT ((UINTN) AtaDevice->Lba48Bit < 2);\r
417   ASSERT ((UINTN) IsWrite < 2);\r
418   //\r
419   // Prepare for ATA command block.\r
420   //\r
421   Acb = ZeroMem (&AtaDevice->Acb, sizeof (*Acb));\r
422   Acb->AtaCommand = mAtaCommands[AtaDevice->UdmaValid][AtaDevice->Lba48Bit][IsWrite];\r
423   Acb->AtaSectorNumber = (UINT8) StartLba;\r
424   Acb->AtaCylinderLow = (UINT8) RShiftU64 (StartLba, 8);\r
425   Acb->AtaCylinderHigh = (UINT8) RShiftU64 (StartLba, 16);\r
426   Acb->AtaDeviceHead = (UINT8) (BIT7 | BIT6 | BIT5 | (AtaDevice->PortMultiplierPort << 4)); \r
427   Acb->AtaSectorCount = (UINT8) TransferLength;\r
428   if (AtaDevice->Lba48Bit) {\r
429     Acb->AtaSectorNumberExp = (UINT8) RShiftU64 (StartLba, 24);\r
430     Acb->AtaCylinderLowExp = (UINT8) RShiftU64 (StartLba, 32);\r
431     Acb->AtaCylinderHighExp = (UINT8) RShiftU64 (StartLba, 40);\r
432     Acb->AtaSectorCountExp = (UINT8) (TransferLength >> 8);\r
433   } else {\r
434     Acb->AtaDeviceHead = (UINT8) (Acb->AtaDeviceHead | RShiftU64 (StartLba, 24));\r
435   }\r
436 \r
437   //\r
438   // Prepare for ATA pass through packet.\r
439   //\r
440   Packet = ZeroMem (&AtaDevice->Packet, sizeof (*Packet));\r
441   if (IsWrite) {\r
442     Packet->OutDataBuffer = Buffer;\r
443     Packet->OutTransferLength = TransferLength;\r
444   } else {\r
445     Packet->InDataBuffer = Buffer;\r
446     Packet->InTransferLength = TransferLength;\r
447   }\r
448   Packet->Protocol = mAtaPassThruCmdProtocols[AtaDevice->UdmaValid][IsWrite];\r
449   Packet->Length = EFI_ATA_PASS_THRU_LENGTH_SECTOR_COUNT;\r
450 \r
451   return AtaDevicePassThru (AtaDevice); \r
452 }\r
453 \r
454 /**\r
455   Read or write a number of blocks from ATA device.\r
456 \r
457   This function performs ATA pass through transactions to read/write data from/to\r
458   ATA device. It may separate the read/write request into several ATA pass through\r
459   transactions.\r
460 \r
461   @param  AtaDevice         The ATA child device involved for the operation.\r
462   @param  Buffer            The pointer to the current transaction buffer.\r
463   @param  StartLba          The starting logical block address to be accessed.\r
464   @param  NumberOfBlocks    The block number or sector count of the transfer.\r
465   @param  IsWrite           Indicates whether it is a write operation.\r
466 \r
467   @retval EFI_SUCCESS       The data transfer is complete successfully.\r
468   @return others            Some error occurs when transferring data. \r
469 \r
470 **/\r
471 EFI_STATUS \r
472 AccessAtaDevice(\r
473   IN OUT ATA_DEVICE                 *AtaDevice,\r
474   IN OUT UINT8                      *Buffer,\r
475   IN EFI_LBA                        StartLba,\r
476   IN UINTN                          NumberOfBlocks,\r
477   IN BOOLEAN                        IsWrite\r
478   )\r
479 {\r
480   EFI_STATUS                        Status;\r
481   UINTN                             MaxTransferBlockNumber;\r
482   UINTN                             TransferBlockNumber;\r
483   UINTN                             BlockSize;\r
484  \r
485   //\r
486   // Ensure AtaDevice->Lba48Bit is a valid boolean value \r
487   //\r
488   ASSERT ((UINTN) AtaDevice->Lba48Bit < 2);\r
489   MaxTransferBlockNumber = mMaxTransferBlockNumber[AtaDevice->Lba48Bit];\r
490   BlockSize = AtaDevice->BlockMedia.BlockSize;\r
491   do {\r
492     if (NumberOfBlocks > MaxTransferBlockNumber) {\r
493       TransferBlockNumber = MaxTransferBlockNumber;\r
494       NumberOfBlocks -= MaxTransferBlockNumber;\r
495     } else  {\r
496       TransferBlockNumber = NumberOfBlocks;\r
497       NumberOfBlocks  = 0;\r
498     }\r
499 \r
500     Status = TransferAtaDevice (AtaDevice, Buffer, StartLba, (UINT32) TransferBlockNumber, IsWrite);\r
501     if (EFI_ERROR (Status)) {\r
502       return Status;\r
503     }\r
504     StartLba += TransferBlockNumber;\r
505     Buffer   += TransferBlockNumber * BlockSize;\r
506   } while (NumberOfBlocks > 0);\r
507 \r
508   return Status;\r
509 }\r