• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /** @file
2   This driver is used for Opal Password Feature support at AHCI mode.
3 
4 Copyright (c) 2016, Intel Corporation. All rights reserved.<BR>
5 This program and the accompanying materials
6 are licensed and made available under the terms and conditions of the BSD License
7 which accompanies this distribution.  The full text of the license may be found at
8 http://opensource.org/licenses/bsd-license.php
9 
10 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
11 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
12 
13 **/
14 
15 
16 #include "OpalPasswordSmm.h"
17 
18 /**
19   Start command for give slot on specific port.
20 
21   @param  Port               The number of port.
22   @param  CommandSlot        The number of CommandSlot.
23   @param  Timeout            The timeout Value of start.
24 
25   @retval EFI_DEVICE_ERROR   The command start unsuccessfully.
26   @retval EFI_TIMEOUT        The operation is time out.
27   @retval EFI_SUCCESS        The command start successfully.
28 
29 **/
30 EFI_STATUS
31 EFIAPI
32 AhciStartCommand (
33   IN  UINT8                     Port,
34   IN  UINT8                     CommandSlot,
35   IN  UINT64                    Timeout
36   );
37 
38 /**
39   Stop command running for giving port
40 
41   @param  Port               The number of port.
42   @param  Timeout            The timeout Value of stop.
43 
44   @retval EFI_DEVICE_ERROR   The command stop unsuccessfully.
45   @retval EFI_TIMEOUT        The operation is time out.
46   @retval EFI_SUCCESS        The command stop successfully.
47 
48 **/
49 EFI_STATUS
50 EFIAPI
51 AhciStopCommand (
52   IN  UINT8                     Port,
53   IN  UINT64                    Timeout
54   );
55 
56 /**
57   Read AHCI Operation register.
58 
59   @param  Offset       The operation register offset.
60 
61   @return The register content read.
62 
63 **/
64 UINT32
65 EFIAPI
AhciReadReg(IN UINT32 Offset)66 AhciReadReg (
67   IN  UINT32              Offset
68   )
69 {
70   UINT32   Data;
71 
72   Data = 0;
73 
74   Data = MmioRead32 (mAhciBar + Offset);
75 
76   return Data;
77 }
78 
79 /**
80   Write AHCI Operation register.
81 
82   @param  Offset       The operation register offset.
83   @param  Data         The Data used to write down.
84 
85 **/
86 VOID
87 EFIAPI
AhciWriteReg(IN UINT32 Offset,IN UINT32 Data)88 AhciWriteReg (
89   IN UINT32               Offset,
90   IN UINT32               Data
91   )
92 {
93   MmioWrite32 (mAhciBar + Offset, Data);
94 
95   return ;
96 }
97 
98 /**
99   Do AND operation with the Value of AHCI Operation register.
100 
101   @param  Offset       The operation register offset.
102   @param  AndData      The Data used to do AND operation.
103 
104 **/
105 VOID
106 EFIAPI
AhciAndReg(IN UINT32 Offset,IN UINT32 AndData)107 AhciAndReg (
108   IN UINT32               Offset,
109   IN UINT32               AndData
110   )
111 {
112   UINT32 Data;
113 
114   Data  = AhciReadReg (Offset);
115 
116   Data &= AndData;
117 
118   AhciWriteReg (Offset, Data);
119 }
120 
121 /**
122   Do OR operation with the Value of AHCI Operation register.
123 
124   @param  Offset       The operation register offset.
125   @param  OrData       The Data used to do OR operation.
126 
127 **/
128 VOID
129 EFIAPI
AhciOrReg(IN UINT32 Offset,IN UINT32 OrData)130 AhciOrReg (
131   IN UINT32               Offset,
132   IN UINT32               OrData
133   )
134 {
135   UINT32 Data;
136 
137   Data  = AhciReadReg (Offset);
138 
139   Data |= OrData;
140 
141   AhciWriteReg (Offset, Data);
142 }
143 
144 /**
145   Wait for memory set to the test Value.
146 
147   @param  Offset            The memory address to test.
148   @param  MaskValue         The mask Value of memory.
149   @param  TestValue         The test Value of memory.
150   @param  Timeout           The time out Value for wait memory set.
151 
152   @retval EFI_DEVICE_ERROR  The memory is not set.
153   @retval EFI_TIMEOUT       The memory setting is time out.
154   @retval EFI_SUCCESS       The memory is correct set.
155 
156 **/
157 EFI_STATUS
158 EFIAPI
AhciWaitMmioSet(IN UINT32 Offset,IN UINT32 MaskValue,IN UINT32 TestValue,IN UINT64 Timeout)159 AhciWaitMmioSet (
160   IN  UINT32                    Offset,
161   IN  UINT32                    MaskValue,
162   IN  UINT32                    TestValue,
163   IN  UINT64                    Timeout
164   )
165 {
166   UINT32     Value;
167   UINT32     Delay;
168 
169   Delay = (UINT32) (DivU64x32(Timeout, 1000) + 1);
170 
171   do {
172     Value = AhciReadReg (Offset) & MaskValue;
173 
174     if (Value == TestValue) {
175       return EFI_SUCCESS;
176     }
177 
178     //
179     // Stall for 100 microseconds.
180     //
181     MicroSecondDelay (100);
182 
183     Delay--;
184 
185   } while (Delay > 0);
186 
187   return EFI_TIMEOUT;
188 }
189 /**
190   Wait for the Value of the specified system memory set to the test Value.
191 
192   @param  Address           The system memory address to test.
193   @param  MaskValue         The mask Value of memory.
194   @param  TestValue         The test Value of memory.
195   @param  Timeout           The time out Value for wait memory set, uses 100ns as a unit.
196 
197   @retval EFI_TIMEOUT       The system memory setting is time out.
198   @retval EFI_SUCCESS       The system memory is correct set.
199 
200 **/
201 EFI_STATUS
202 EFIAPI
AhciWaitMemSet(IN EFI_PHYSICAL_ADDRESS Address,IN UINT32 MaskValue,IN UINT32 TestValue,IN UINT64 Timeout)203 AhciWaitMemSet (
204   IN  EFI_PHYSICAL_ADDRESS      Address,
205   IN  UINT32                    MaskValue,
206   IN  UINT32                    TestValue,
207   IN  UINT64                    Timeout
208   )
209 {
210   UINT32     Value;
211   UINT32     Delay;
212 
213   Delay = (UINT32) (DivU64x32 (Timeout, 1000) + 1);
214 
215   do {
216     //
217     // Access sytem memory to see if the Value is the tested one.
218     //
219     // The system memory pointed by Address will be updated by the
220     // SATA Host Controller, "volatile" is introduced to prevent
221     // compiler from optimizing the access to the memory address
222     // to only read once.
223     //
224     Value  = *(volatile UINT32 *) (UINTN) Address;
225     Value &= MaskValue;
226 
227     if (Value == TestValue) {
228       return EFI_SUCCESS;
229     }
230 
231     //
232     // Stall for 100 microseconds.
233     //
234     MicroSecondDelay (100);
235 
236     Delay--;
237 
238   } while (Delay > 0);
239 
240   return EFI_TIMEOUT;
241 }
242 
243 /**
244   Check the memory status to the test Value.
245 
246   @param[in]       Address           The memory address to test.
247   @param[in]       MaskValue         The mask Value of memory.
248   @param[in]       TestValue         The test Value of memory.
249   @param[in, out]  RetryTimes        The retry times Value for waitting memory set. If 0, then just try once.
250 
251   @retval EFI_NOTREADY      The memory is not set.
252   @retval EFI_TIMEOUT       The memory setting retry times out.
253   @retval EFI_SUCCESS       The memory is correct set.
254 
255 **/
256 EFI_STATUS
257 EFIAPI
AhciCheckMemSet(IN UINTN Address,IN UINT32 MaskValue,IN UINT32 TestValue,IN OUT UINTN * RetryTimes OPTIONAL)258 AhciCheckMemSet (
259   IN     UINTN                     Address,
260   IN     UINT32                    MaskValue,
261   IN     UINT32                    TestValue,
262   IN OUT UINTN                     *RetryTimes OPTIONAL
263   )
264 {
265   UINT32     Value;
266 
267   if (RetryTimes != NULL) {
268     (*RetryTimes)--;
269   }
270 
271   Value  = *(volatile UINT32 *) Address;
272   Value &= MaskValue;
273 
274   if (Value == TestValue) {
275     return EFI_SUCCESS;
276   }
277 
278   if ((RetryTimes != NULL) && (*RetryTimes == 0)) {
279     return EFI_TIMEOUT;
280   } else {
281     return EFI_NOT_READY;
282   }
283 }
284 
285 /**
286   Clear the port interrupt and error status. It will also clear
287   HBA interrupt status.
288 
289   @param      Port           The number of port.
290 
291 **/
292 VOID
293 EFIAPI
AhciClearPortStatus(IN UINT8 Port)294 AhciClearPortStatus (
295   IN  UINT8                  Port
296   )
297 {
298   UINT32 Offset;
299 
300   //
301   // Clear any error status
302   //
303   Offset = EFI_AHCI_PORT_START + Port * EFI_AHCI_PORT_REG_WIDTH + EFI_AHCI_PORT_SERR;
304   AhciWriteReg (Offset, AhciReadReg (Offset));
305 
306   //
307   // Clear any port interrupt status
308   //
309   Offset = EFI_AHCI_PORT_START + Port * EFI_AHCI_PORT_REG_WIDTH + EFI_AHCI_PORT_IS;
310   AhciWriteReg (Offset, AhciReadReg (Offset));
311 
312   //
313   // Clear any HBA interrupt status
314   //
315   AhciWriteReg (EFI_AHCI_IS_OFFSET, AhciReadReg (EFI_AHCI_IS_OFFSET));
316 }
317 
318 /**
319   Enable the FIS running for giving port.
320 
321   @param      Port           The number of port.
322   @param      Timeout        The timeout Value of enabling FIS.
323 
324   @retval EFI_DEVICE_ERROR   The FIS enable setting fails.
325   @retval EFI_TIMEOUT        The FIS enable setting is time out.
326   @retval EFI_SUCCESS        The FIS enable successfully.
327 
328 **/
329 EFI_STATUS
330 EFIAPI
AhciEnableFisReceive(IN UINT8 Port,IN UINT64 Timeout)331 AhciEnableFisReceive (
332   IN  UINT8                     Port,
333   IN  UINT64                    Timeout
334   )
335 {
336   UINT32 Offset;
337 
338   Offset = EFI_AHCI_PORT_START + Port * EFI_AHCI_PORT_REG_WIDTH + EFI_AHCI_PORT_CMD;
339   AhciOrReg (Offset, EFI_AHCI_PORT_CMD_FRE);
340 
341   return AhciWaitMmioSet (
342            Offset,
343            EFI_AHCI_PORT_CMD_FR,
344            EFI_AHCI_PORT_CMD_FR,
345            Timeout
346            );
347 }
348 
349 /**
350   Disable the FIS running for giving port.
351 
352   @param      Port           The number of port.
353   @param      Timeout        The timeout Value of disabling FIS.
354 
355   @retval EFI_DEVICE_ERROR   The FIS disable setting fails.
356   @retval EFI_TIMEOUT        The FIS disable setting is time out.
357   @retval EFI_UNSUPPORTED    The port is in running state.
358   @retval EFI_SUCCESS        The FIS disable successfully.
359 
360 **/
361 EFI_STATUS
362 EFIAPI
AhciDisableFisReceive(IN UINT8 Port,IN UINT64 Timeout)363 AhciDisableFisReceive (
364   IN  UINT8                     Port,
365   IN  UINT64                    Timeout
366   )
367 {
368   UINT32 Offset;
369   UINT32 Data;
370 
371   Offset = EFI_AHCI_PORT_START + Port * EFI_AHCI_PORT_REG_WIDTH + EFI_AHCI_PORT_CMD;
372   Data   = AhciReadReg (Offset);
373 
374   //
375   // Before disabling Fis receive, the DMA engine of the port should NOT be in running status.
376   //
377   if ((Data & (EFI_AHCI_PORT_CMD_ST | EFI_AHCI_PORT_CMD_CR)) != 0) {
378     return EFI_UNSUPPORTED;
379   }
380 
381   //
382   // Check if the Fis receive DMA engine for the port is running.
383   //
384   if ((Data & EFI_AHCI_PORT_CMD_FR) != EFI_AHCI_PORT_CMD_FR) {
385     return EFI_SUCCESS;
386   }
387 
388   AhciAndReg (Offset, (UINT32)~(EFI_AHCI_PORT_CMD_FRE));
389 
390   return AhciWaitMmioSet (
391            Offset,
392            EFI_AHCI_PORT_CMD_FR,
393            0,
394            Timeout
395            );
396 }
397 
398 /**
399   Build the command list, command table and prepare the fis receiver.
400 
401   @param    AhciRegisters         The pointer to the EFI_AHCI_REGISTERS.
402   @param    Port                  The number of port.
403   @param    PortMultiplier        The timeout Value of stop.
404   @param    CommandFis            The control fis will be used for the transfer.
405   @param    CommandList           The command list will be used for the transfer.
406   @param    AtapiCommand          The atapi command will be used for the transfer.
407   @param    AtapiCommandLength    The Length of the atapi command.
408   @param    CommandSlotNumber     The command slot will be used for the transfer.
409   @param    DataPhysicalAddr      The pointer to the Data Buffer pci bus master address.
410   @param    DataLength            The Data count to be transferred.
411 
412 **/
413 VOID
414 EFIAPI
AhciBuildCommand(IN EFI_AHCI_REGISTERS * AhciRegisters,IN UINT8 Port,IN UINT8 PortMultiplier,IN EFI_AHCI_COMMAND_FIS * CommandFis,IN EFI_AHCI_COMMAND_LIST * CommandList,IN EFI_AHCI_ATAPI_COMMAND * AtapiCommand OPTIONAL,IN UINT8 AtapiCommandLength,IN UINT8 CommandSlotNumber,IN OUT VOID * DataPhysicalAddr,IN UINT64 DataLength)415 AhciBuildCommand (
416   IN     EFI_AHCI_REGISTERS         *AhciRegisters,
417   IN     UINT8                      Port,
418   IN     UINT8                      PortMultiplier,
419   IN     EFI_AHCI_COMMAND_FIS       *CommandFis,
420   IN     EFI_AHCI_COMMAND_LIST      *CommandList,
421   IN     EFI_AHCI_ATAPI_COMMAND     *AtapiCommand OPTIONAL,
422   IN     UINT8                      AtapiCommandLength,
423   IN     UINT8                      CommandSlotNumber,
424   IN OUT VOID                       *DataPhysicalAddr,
425   IN     UINT64                     DataLength
426   )
427 {
428   UINT64     BaseAddr;
429   UINT64     PrdtNumber;
430   UINTN      RemainedData;
431   UINTN      MemAddr;
432   DATA_64    Data64;
433   UINT32     Offset;
434 
435   //
436   // Filling the PRDT
437   //
438   PrdtNumber = DivU64x32 (DataLength + EFI_AHCI_MAX_DATA_PER_PRDT - 1, EFI_AHCI_MAX_DATA_PER_PRDT);
439 
440   //
441   // According to AHCI 1.3 spec, a PRDT entry can point to a maximum 4MB Data block.
442   // It also limits that the maximum amount of the PRDT entry in the command table
443   // is 65535.
444   //
445   ASSERT (PrdtNumber <= 1);
446 
447   Data64.Uint64 = (UINTN) (AhciRegisters->AhciRFis);
448 
449   BaseAddr = Data64.Uint64;
450 
451   ZeroMem ((VOID *)((UINTN) BaseAddr), sizeof (EFI_AHCI_RECEIVED_FIS));
452 
453   ZeroMem (AhciRegisters->AhciCommandTable, sizeof (EFI_AHCI_COMMAND_TABLE));
454 
455   CommandFis->AhciCFisPmNum = PortMultiplier;
456 
457   CopyMem (&AhciRegisters->AhciCommandTable->CommandFis, CommandFis, sizeof (EFI_AHCI_COMMAND_FIS));
458 
459   Offset = EFI_AHCI_PORT_START + Port * EFI_AHCI_PORT_REG_WIDTH + EFI_AHCI_PORT_CMD;
460   if (AtapiCommand != NULL) {
461     CopyMem (
462       &AhciRegisters->AhciCommandTable->AtapiCmd,
463       AtapiCommand,
464       AtapiCommandLength
465       );
466 
467     CommandList->AhciCmdA = 1;
468     CommandList->AhciCmdP = 1;
469 
470     AhciOrReg (Offset, (EFI_AHCI_PORT_CMD_DLAE | EFI_AHCI_PORT_CMD_ATAPI));
471   } else {
472     AhciAndReg (Offset, (UINT32)~(EFI_AHCI_PORT_CMD_DLAE | EFI_AHCI_PORT_CMD_ATAPI));
473   }
474 
475   RemainedData = (UINTN) DataLength;
476   MemAddr      = (UINTN) DataPhysicalAddr;
477   CommandList->AhciCmdPrdtl = (UINT32)PrdtNumber;
478 
479   AhciRegisters->AhciCommandTable->PrdtTable.AhciPrdtDbc = (UINT32)RemainedData - 1;
480 
481   Data64.Uint64 = (UINT64)MemAddr;
482   AhciRegisters->AhciCommandTable->PrdtTable.AhciPrdtDba  = Data64.Uint32.Lower32;
483   AhciRegisters->AhciCommandTable->PrdtTable.AhciPrdtDbau = Data64.Uint32.Upper32;
484 
485   //
486   // Set the last PRDT to Interrupt On Complete
487   //
488   AhciRegisters->AhciCommandTable->PrdtTable.AhciPrdtIoc = 1;
489 
490   CopyMem (
491     (VOID *) ((UINTN) AhciRegisters->AhciCmdList + (UINTN) CommandSlotNumber * sizeof (EFI_AHCI_COMMAND_LIST)),
492     CommandList,
493     sizeof (EFI_AHCI_COMMAND_LIST)
494     );
495 
496   Data64.Uint64 = (UINT64)(UINTN) AhciRegisters->AhciCommandTable;
497   AhciRegisters->AhciCmdList[CommandSlotNumber].AhciCmdCtba  = Data64.Uint32.Lower32;
498   AhciRegisters->AhciCmdList[CommandSlotNumber].AhciCmdCtbau = Data64.Uint32.Upper32;
499   AhciRegisters->AhciCmdList[CommandSlotNumber].AhciCmdPmp   = PortMultiplier;
500 
501 }
502 
503 /**
504   Buid a command FIS.
505 
506   @param  CmdFis            A pointer to the EFI_AHCI_COMMAND_FIS Data structure.
507   @param  AtaCommandBlock   A pointer to the AhciBuildCommandFis Data structure.
508 
509 **/
510 VOID
511 EFIAPI
AhciBuildCommandFis(IN OUT EFI_AHCI_COMMAND_FIS * CmdFis,IN EFI_ATA_COMMAND_BLOCK * AtaCommandBlock)512 AhciBuildCommandFis (
513   IN OUT EFI_AHCI_COMMAND_FIS    *CmdFis,
514   IN     EFI_ATA_COMMAND_BLOCK   *AtaCommandBlock
515   )
516 {
517   ZeroMem (CmdFis, sizeof (EFI_AHCI_COMMAND_FIS));
518 
519   CmdFis->AhciCFisType = EFI_AHCI_FIS_REGISTER_H2D;
520   //
521   // Indicator it's a command
522   //
523   CmdFis->AhciCFisCmdInd      = 0x1;
524   CmdFis->AhciCFisCmd         = AtaCommandBlock->AtaCommand;
525 
526   CmdFis->AhciCFisFeature     = AtaCommandBlock->AtaFeatures;
527   CmdFis->AhciCFisFeatureExp  = AtaCommandBlock->AtaFeaturesExp;
528 
529   CmdFis->AhciCFisSecNum      = AtaCommandBlock->AtaSectorNumber;
530   CmdFis->AhciCFisSecNumExp   = AtaCommandBlock->AtaSectorNumberExp;
531 
532   CmdFis->AhciCFisClyLow      = AtaCommandBlock->AtaCylinderLow;
533   CmdFis->AhciCFisClyLowExp   = AtaCommandBlock->AtaCylinderLowExp;
534 
535   CmdFis->AhciCFisClyHigh     = AtaCommandBlock->AtaCylinderHigh;
536   CmdFis->AhciCFisClyHighExp  = AtaCommandBlock->AtaCylinderHighExp;
537 
538   CmdFis->AhciCFisSecCount    = AtaCommandBlock->AtaSectorCount;
539   CmdFis->AhciCFisSecCountExp = AtaCommandBlock->AtaSectorCountExp;
540 
541   CmdFis->AhciCFisDevHead     = (UINT8) (AtaCommandBlock->AtaDeviceHead | 0xE0);
542 }
543 
544 /**
545   Start a PIO Data transfer on specific port.
546 
547   @param  AhciRegisters       The pointer to the EFI_AHCI_REGISTERS.
548   @param  Port                The number of port.
549   @param  PortMultiplier      The timeout Value of stop.
550   @param  AtapiCommand        The atapi command will be used for the transfer.
551   @param  AtapiCommandLength  The Length of the atapi command.
552   @param  Read                The transfer direction.
553   @param  AtaCommandBlock     The EFI_ATA_COMMAND_BLOCK Data.
554   @param  AtaStatusBlock      The EFI_ATA_STATUS_BLOCK Data.
555   @param  MemoryAddr          The pointer to the Data Buffer.
556   @param  DataCount           The Data count to be transferred.
557   @param  Timeout             The timeout Value of non Data transfer.
558 
559   @retval EFI_DEVICE_ERROR    The PIO Data transfer abort with error occurs.
560   @retval EFI_TIMEOUT         The operation is time out.
561   @retval EFI_UNSUPPORTED     The device is not ready for transfer.
562   @retval EFI_SUCCESS         The PIO Data transfer executes successfully.
563 
564 **/
565 EFI_STATUS
566 EFIAPI
AhciPioTransfer(IN EFI_AHCI_REGISTERS * AhciRegisters,IN UINT8 Port,IN UINT8 PortMultiplier,IN EFI_AHCI_ATAPI_COMMAND * AtapiCommand OPTIONAL,IN UINT8 AtapiCommandLength,IN BOOLEAN Read,IN EFI_ATA_COMMAND_BLOCK * AtaCommandBlock,IN OUT EFI_ATA_STATUS_BLOCK * AtaStatusBlock,IN OUT VOID * MemoryAddr,IN UINT32 DataCount,IN UINT64 Timeout)567 AhciPioTransfer (
568   IN     EFI_AHCI_REGISTERS         *AhciRegisters,
569   IN     UINT8                      Port,
570   IN     UINT8                      PortMultiplier,
571   IN     EFI_AHCI_ATAPI_COMMAND     *AtapiCommand OPTIONAL,
572   IN     UINT8                      AtapiCommandLength,
573   IN     BOOLEAN                    Read,
574   IN     EFI_ATA_COMMAND_BLOCK      *AtaCommandBlock,
575   IN OUT EFI_ATA_STATUS_BLOCK       *AtaStatusBlock,
576   IN OUT VOID                       *MemoryAddr,
577   IN     UINT32                     DataCount,
578   IN     UINT64                     Timeout
579   )
580 {
581   EFI_STATUS                    Status;
582   UINT32                        FisBaseAddr;
583   UINT32                        Offset;
584   UINT32                        Delay;
585   EFI_AHCI_COMMAND_FIS          CFis;
586   EFI_AHCI_COMMAND_LIST         CmdList;
587   UINT32                        PortTfd;
588   UINT32                        PrdCount;
589   UINT32                        OldRfisLo;
590   UINT32                        OldRfisHi;
591   UINT32                        OldCmdListLo;
592   UINT32                        OldCmdListHi;
593 
594   Offset    = EFI_AHCI_PORT_START + Port * EFI_AHCI_PORT_REG_WIDTH + EFI_AHCI_PORT_FB;
595   OldRfisLo = AhciReadReg (Offset);
596   Offset    = EFI_AHCI_PORT_START + Port * EFI_AHCI_PORT_REG_WIDTH + EFI_AHCI_PORT_FBU;
597   OldRfisHi = AhciReadReg (Offset);
598   Offset    = EFI_AHCI_PORT_START + Port * EFI_AHCI_PORT_REG_WIDTH + EFI_AHCI_PORT_FB;
599   AhciWriteReg (Offset, (UINT32)(UINTN)AhciRegisters->AhciRFis);
600   Offset    = EFI_AHCI_PORT_START + Port * EFI_AHCI_PORT_REG_WIDTH + EFI_AHCI_PORT_FBU;
601   AhciWriteReg (Offset, 0);
602 
603   //
604   // Single task envrionment, we only use one command table for all port
605   //
606   Offset = EFI_AHCI_PORT_START + Port * EFI_AHCI_PORT_REG_WIDTH + EFI_AHCI_PORT_CLB;
607   OldCmdListLo = AhciReadReg (Offset);
608   Offset = EFI_AHCI_PORT_START + Port * EFI_AHCI_PORT_REG_WIDTH + EFI_AHCI_PORT_CLBU;
609   OldCmdListHi = AhciReadReg (Offset);
610   Offset = EFI_AHCI_PORT_START + Port * EFI_AHCI_PORT_REG_WIDTH + EFI_AHCI_PORT_CLB;
611   AhciWriteReg (Offset, (UINT32)(UINTN)AhciRegisters->AhciCmdList);
612   Offset = EFI_AHCI_PORT_START + Port * EFI_AHCI_PORT_REG_WIDTH + EFI_AHCI_PORT_CLBU;
613   AhciWriteReg (Offset, 0);
614 
615   //
616   // Package read needed
617   //
618   AhciBuildCommandFis (&CFis, AtaCommandBlock);
619 
620   ZeroMem (&CmdList, sizeof (EFI_AHCI_COMMAND_LIST));
621 
622   CmdList.AhciCmdCfl = EFI_AHCI_FIS_REGISTER_H2D_LENGTH / 4;
623   CmdList.AhciCmdW   = Read ? 0 : 1;
624 
625   AhciBuildCommand (
626     AhciRegisters,
627     Port,
628     PortMultiplier,
629     &CFis,
630     &CmdList,
631     AtapiCommand,
632     AtapiCommandLength,
633     0,
634     (VOID *)(UINTN)MemoryAddr,
635     DataCount
636     );
637 
638   Status = AhciStartCommand (
639              Port,
640              0,
641              Timeout
642              );
643   if (EFI_ERROR (Status)) {
644     goto Exit;
645   }
646 
647   //
648   // Checking the status and wait the driver sending Data
649   //
650   FisBaseAddr = (UINT32)(UINTN)AhciRegisters->AhciRFis;
651   if (Read && (AtapiCommand == 0)) {
652     //
653     // Wait device sends the PIO setup fis before Data transfer
654     //
655     Status = EFI_TIMEOUT;
656     Delay  = (UINT32) (DivU64x32 (Timeout, 1000) + 1);
657     do {
658       Offset = FisBaseAddr + EFI_AHCI_PIO_FIS_OFFSET;
659 
660       Status = AhciCheckMemSet (Offset, EFI_AHCI_FIS_TYPE_MASK, EFI_AHCI_FIS_PIO_SETUP, 0);
661       if (!EFI_ERROR (Status)) {
662         Offset = EFI_AHCI_PORT_START + Port * EFI_AHCI_PORT_REG_WIDTH + EFI_AHCI_PORT_TFD;
663         PortTfd = AhciReadReg ((UINT32) Offset);
664         //
665         // PxTFD will be updated if there is a D2H or SetupFIS received.
666         // For PIO IN transfer, D2H means a device error. Therefore we only need to check the TFD after receiving a SetupFIS.
667         //
668         if ((PortTfd & EFI_AHCI_PORT_TFD_ERR) != 0) {
669           Status = EFI_DEVICE_ERROR;
670           break;
671         }
672 
673         PrdCount = *(volatile UINT32 *) (&(AhciRegisters->AhciCmdList[0].AhciCmdPrdbc));
674         if (PrdCount == DataCount) {
675           break;
676         }
677       }
678 
679       Offset = FisBaseAddr + EFI_AHCI_D2H_FIS_OFFSET;
680       Status = AhciCheckMemSet (Offset, EFI_AHCI_FIS_TYPE_MASK, EFI_AHCI_FIS_REGISTER_D2H, 0);
681       if (!EFI_ERROR (Status)) {
682         Status = EFI_DEVICE_ERROR;
683         break;
684       }
685 
686       //
687       // Stall for 100 microseconds.
688       //
689       MicroSecondDelay(100);
690 
691       Delay--;
692     } while (Delay > 0);
693   } else {
694     //
695     // Wait for D2H Fis is received
696     //
697     Offset = FisBaseAddr + EFI_AHCI_D2H_FIS_OFFSET;
698     Status = AhciWaitMemSet (
699                Offset,
700                EFI_AHCI_FIS_TYPE_MASK,
701                EFI_AHCI_FIS_REGISTER_D2H,
702                Timeout
703                );
704 
705     if (EFI_ERROR (Status)) {
706       goto Exit;
707     }
708 
709     Offset = EFI_AHCI_PORT_START + Port * EFI_AHCI_PORT_REG_WIDTH + EFI_AHCI_PORT_TFD;
710     PortTfd = AhciReadReg ((UINT32) Offset);
711     if ((PortTfd & EFI_AHCI_PORT_TFD_ERR) != 0) {
712       Status = EFI_DEVICE_ERROR;
713     }
714   }
715 
716 Exit:
717   AhciStopCommand (
718     Port,
719     Timeout
720     );
721 
722   AhciDisableFisReceive (
723     Port,
724     Timeout
725     );
726 
727   Offset    = EFI_AHCI_PORT_START + Port * EFI_AHCI_PORT_REG_WIDTH + EFI_AHCI_PORT_FB;
728   AhciWriteReg (Offset, OldRfisLo);
729   Offset    = EFI_AHCI_PORT_START + Port * EFI_AHCI_PORT_REG_WIDTH + EFI_AHCI_PORT_FBU;
730   AhciWriteReg (Offset, OldRfisHi);
731 
732   Offset    = EFI_AHCI_PORT_START + Port * EFI_AHCI_PORT_REG_WIDTH + EFI_AHCI_PORT_CLB;
733   AhciWriteReg (Offset, OldCmdListLo);
734   Offset    = EFI_AHCI_PORT_START + Port * EFI_AHCI_PORT_REG_WIDTH + EFI_AHCI_PORT_CLBU;
735   AhciWriteReg (Offset, OldCmdListHi);
736 
737   return Status;
738 }
739 
740 /**
741   Stop command running for giving port
742 
743   @param  Port               The number of port.
744   @param  Timeout            The timeout Value of stop.
745 
746   @retval EFI_DEVICE_ERROR   The command stop unsuccessfully.
747   @retval EFI_TIMEOUT        The operation is time out.
748   @retval EFI_SUCCESS        The command stop successfully.
749 
750 **/
751 EFI_STATUS
752 EFIAPI
AhciStopCommand(IN UINT8 Port,IN UINT64 Timeout)753 AhciStopCommand (
754   IN  UINT8                     Port,
755   IN  UINT64                    Timeout
756   )
757 {
758   UINT32 Offset;
759   UINT32 Data;
760 
761   Offset = EFI_AHCI_PORT_START + Port * EFI_AHCI_PORT_REG_WIDTH + EFI_AHCI_PORT_CMD;
762   Data   = AhciReadReg (Offset);
763 
764   if ((Data & (EFI_AHCI_PORT_CMD_ST |  EFI_AHCI_PORT_CMD_CR)) == 0) {
765     return EFI_SUCCESS;
766   }
767 
768   if ((Data & EFI_AHCI_PORT_CMD_ST) != 0) {
769     AhciAndReg (Offset, (UINT32)~(EFI_AHCI_PORT_CMD_ST));
770   }
771 
772   return AhciWaitMmioSet (
773            Offset,
774            EFI_AHCI_PORT_CMD_CR,
775            0,
776            Timeout
777            );
778 }
779 
780 /**
781   Start command for give slot on specific port.
782 
783   @param  Port               The number of port.
784   @param  CommandSlot        The number of CommandSlot.
785   @param  Timeout            The timeout Value of start.
786 
787   @retval EFI_DEVICE_ERROR   The command start unsuccessfully.
788   @retval EFI_TIMEOUT        The operation is time out.
789   @retval EFI_SUCCESS        The command start successfully.
790 
791 **/
792 EFI_STATUS
793 EFIAPI
AhciStartCommand(IN UINT8 Port,IN UINT8 CommandSlot,IN UINT64 Timeout)794 AhciStartCommand (
795   IN  UINT8                     Port,
796   IN  UINT8                     CommandSlot,
797   IN  UINT64                    Timeout
798   )
799 {
800   UINT32                        CmdSlotBit;
801   EFI_STATUS                    Status;
802   UINT32                        PortStatus;
803   UINT32                        StartCmd;
804   UINT32                        PortTfd;
805   UINT32                        Offset;
806   UINT32                        Capability;
807 
808   //
809   // Collect AHCI controller information
810   //
811   Capability = AhciReadReg(EFI_AHCI_CAPABILITY_OFFSET);
812 
813   CmdSlotBit = (UINT32) (1 << CommandSlot);
814 
815   AhciClearPortStatus (
816     Port
817     );
818 
819   Status = AhciEnableFisReceive (
820              Port,
821              Timeout
822              );
823 
824   if (EFI_ERROR (Status)) {
825     return Status;
826   }
827 
828   Offset = EFI_AHCI_PORT_START + Port * EFI_AHCI_PORT_REG_WIDTH + EFI_AHCI_PORT_CMD;
829   PortStatus = AhciReadReg (Offset);
830 
831   StartCmd = 0;
832   if ((PortStatus & EFI_AHCI_PORT_CMD_ALPE) != 0) {
833     StartCmd = AhciReadReg (Offset);
834     StartCmd &= ~EFI_AHCI_PORT_CMD_ICC_MASK;
835     StartCmd |= EFI_AHCI_PORT_CMD_ACTIVE;
836   }
837 
838   Offset = EFI_AHCI_PORT_START + Port * EFI_AHCI_PORT_REG_WIDTH + EFI_AHCI_PORT_TFD;
839   PortTfd = AhciReadReg (Offset);
840 
841   if ((PortTfd & (EFI_AHCI_PORT_TFD_BSY | EFI_AHCI_PORT_TFD_DRQ)) != 0) {
842     if ((Capability & BIT24) != 0) {
843       Offset = EFI_AHCI_PORT_START + Port * EFI_AHCI_PORT_REG_WIDTH + EFI_AHCI_PORT_CMD;
844       AhciOrReg (Offset, EFI_AHCI_PORT_CMD_COL);
845 
846       AhciWaitMmioSet (
847         Offset,
848         EFI_AHCI_PORT_CMD_COL,
849         0,
850         Timeout
851         );
852     }
853   }
854 
855   Offset = EFI_AHCI_PORT_START + Port * EFI_AHCI_PORT_REG_WIDTH + EFI_AHCI_PORT_CMD;
856   AhciOrReg (Offset, EFI_AHCI_PORT_CMD_ST | StartCmd);
857 
858   //
859   // Setting the command
860   //
861   Offset = EFI_AHCI_PORT_START + Port * EFI_AHCI_PORT_REG_WIDTH + EFI_AHCI_PORT_SACT;
862   AhciAndReg (Offset, 0);
863   AhciOrReg (Offset, CmdSlotBit);
864 
865   Offset = EFI_AHCI_PORT_START + Port * EFI_AHCI_PORT_REG_WIDTH + EFI_AHCI_PORT_CI;
866   AhciAndReg (Offset, 0);
867   AhciOrReg (Offset, CmdSlotBit);
868   return EFI_SUCCESS;
869 }
870 
871 
872 /**
873   Do AHCI HBA reset.
874 
875   @param[in]  Timeout        The timeout Value of reset.
876 
877   @retval EFI_DEVICE_ERROR   AHCI controller is failed to complete hardware reset.
878   @retval EFI_TIMEOUT        The reset operation is time out.
879   @retval EFI_SUCCESS        AHCI controller is reset successfully.
880 
881 **/
882 EFI_STATUS
883 EFIAPI
AhciReset(IN UINT64 Timeout)884 AhciReset (
885   IN  UINT64                    Timeout
886   )
887 {
888   UINT32                 Delay;
889   UINT32                 Value;
890   UINT32                 Capability;
891 
892   //
893   // Collect AHCI controller information
894   //
895   Capability = AhciReadReg (EFI_AHCI_CAPABILITY_OFFSET);
896 
897   //
898   // Enable AE before accessing any AHCI registers if Supports AHCI Mode Only is not set
899   //
900   if ((Capability & EFI_AHCI_CAP_SAM) == 0) {
901     AhciOrReg (EFI_AHCI_GHC_OFFSET, EFI_AHCI_GHC_ENABLE);
902   }
903 
904   AhciOrReg (EFI_AHCI_GHC_OFFSET, EFI_AHCI_GHC_RESET);
905 
906   Delay = (UINT32) (DivU64x32(Timeout, 1000) + 1);
907 
908   do {
909     Value = AhciReadReg(EFI_AHCI_GHC_OFFSET);
910     if ((Value & EFI_AHCI_GHC_RESET) == 0) {
911       return EFI_SUCCESS;
912     }
913 
914     //
915     // Stall for 100 microseconds.
916     //
917     MicroSecondDelay(100);
918 
919     Delay--;
920   } while (Delay > 0);
921 
922   return EFI_TIMEOUT;
923 
924 
925 }
926 
927 /**
928   Send Buffer cmd to specific device.
929 
930   @param[in]  AhciRegisters       The pointer to the EFI_AHCI_REGISTERS.
931   @param[in]  Port                The port number of attached ATA device.
932   @param[in]  PortMultiplier      The port number of port multiplier of attached ATA device.
933   @param[in, out]  Buffer         The Data Buffer to store IDENTIFY PACKET Data.
934 
935   @retval EFI_DEVICE_ERROR    The cmd abort with error occurs.
936   @retval EFI_TIMEOUT         The operation is time out.
937   @retval EFI_UNSUPPORTED     The device is not ready for executing.
938   @retval EFI_SUCCESS         The cmd executes successfully.
939 
940 **/
941 EFI_STATUS
942 EFIAPI
AhciIdentify(IN EFI_AHCI_REGISTERS * AhciRegisters,IN UINT8 Port,IN UINT8 PortMultiplier,IN OUT ATA_IDENTIFY_DATA * Buffer)943 AhciIdentify (
944   IN EFI_AHCI_REGISTERS       *AhciRegisters,
945   IN UINT8                    Port,
946   IN UINT8                    PortMultiplier,
947   IN OUT ATA_IDENTIFY_DATA    *Buffer
948   )
949 {
950   EFI_STATUS                   Status;
951   EFI_ATA_COMMAND_BLOCK        AtaCommandBlock;
952 
953   if (AhciRegisters == NULL || Buffer == NULL) {
954     return EFI_INVALID_PARAMETER;
955   }
956 
957   ZeroMem (&AtaCommandBlock, sizeof (EFI_ATA_COMMAND_BLOCK));
958 
959   AtaCommandBlock.AtaCommand     = ATA_CMD_IDENTIFY_DRIVE;
960   AtaCommandBlock.AtaSectorCount = 1;
961 
962   Status = AhciPioTransfer (
963              AhciRegisters,
964              Port,
965              PortMultiplier,
966              NULL,
967              0,
968              TRUE,
969              &AtaCommandBlock,
970              NULL,
971              Buffer,
972              sizeof (ATA_IDENTIFY_DATA),
973              ATA_TIMEOUT
974              );
975 
976   return Status;
977 }
978 
979 /**
980   Get AHCI mode MMIO Bar Size.
981 
982   @param[in] Bus         The bus number of ata host controller.
983   @param[in] Device      The device number of ata host controller.
984   @param[in] Function    The function number of ata host controller.
985 
986   @retval  The Size of AHCI MMIO BAR.
987 
988 **/
989 UINT32
990 EFIAPI
GetAhciBarSize(IN UINTN Bus,IN UINTN Device,IN UINTN Function)991 GetAhciBarSize (
992   IN     UINTN                       Bus,
993   IN     UINTN                       Device,
994   IN     UINTN                       Function
995   )
996 {
997   UINT32     Size;
998   UINT32     OldBar;
999 
1000   OldBar = PciRead32 (PCI_LIB_ADDRESS (Bus, Device, Function, 0x24));
1001   //
1002   // Disable PCI CMD.MSE bit before calculating MMIO Bar Size as it needs write all 1 to BAR register.
1003   //
1004   PciAnd32 (PCI_LIB_ADDRESS (Bus, Device, Function, 0x04), (UINT32)~BIT1);
1005 
1006   //
1007   // Get AHCI MMIO Bar Size.
1008   //
1009   PciWrite32 (PCI_LIB_ADDRESS (Bus, Device, Function, 0x24), 0xFFFFFFFF);
1010   Size = PciRead32 (PCI_LIB_ADDRESS (Bus, Device, Function, 0x24));
1011   Size = (~(Size & 0xFFFFFFF0)) + 1;
1012 
1013   //
1014   // Restore old MMIO Bar.
1015   //
1016   PciWrite32 (PCI_LIB_ADDRESS (Bus, Device, Function, 0x24), OldBar);
1017   //
1018   // Enable PCI CMD.MSE bit after restoring MMIO Bar.
1019   //
1020   PciOr32 (PCI_LIB_ADDRESS (Bus, Device, Function, 0x04), BIT1);
1021 
1022   return Size;
1023 }
1024 
1025 /**
1026   This function check if the memory region is in GCD MMIO region.
1027 
1028   @param Addr  The memory region start address to be checked.
1029   @param Size  The memory region length to be checked.
1030 
1031   @retval TRUE  This memory region is in GCD MMIO region.
1032   @retval FALSE This memory region is not in GCD MMIO region.
1033 **/
1034 BOOLEAN
1035 EFIAPI
OpalIsValidMmioSpace(IN EFI_PHYSICAL_ADDRESS Addr,IN UINTN Size)1036 OpalIsValidMmioSpace (
1037   IN  EFI_PHYSICAL_ADDRESS       Addr,
1038   IN  UINTN                      Size
1039   )
1040 {
1041   UINTN                           Index;
1042   EFI_GCD_MEMORY_SPACE_DESCRIPTOR *Desc;
1043 
1044   for (Index = 0; Index < mNumberOfDescriptors; Index ++) {
1045     Desc = &mGcdMemSpace[Index];
1046     if ((Desc->GcdMemoryType == EfiGcdMemoryTypeMemoryMappedIo) && (Addr >= Desc->BaseAddress) && ((Addr + Size) <= (Desc->BaseAddress + Desc->Length))) {
1047       return TRUE;
1048     }
1049   }
1050 
1051   return FALSE;
1052 }
1053 /**
1054   Get AHCI mode base address registers' Value.
1055 
1056   @param[in] Bus         The bus number of ata host controller.
1057   @param[in] Device      The device number of ata host controller.
1058   @param[in] Function    The function number of ata host controller.
1059 
1060   @retval EFI_UNSUPPORTED        Return this Value when the BARs is not IO type
1061   @retval EFI_SUCCESS            Get the Base address successfully
1062   @retval Other                  Read the pci configureation Data error
1063 
1064 **/
1065 EFI_STATUS
1066 EFIAPI
GetAhciBaseAddress(IN UINTN Bus,IN UINTN Device,IN UINTN Function)1067 GetAhciBaseAddress (
1068   IN     UINTN                       Bus,
1069   IN     UINTN                       Device,
1070   IN     UINTN                       Function
1071   )
1072 {
1073   UINT32  Size;
1074 
1075   //
1076   // Get AHCI MMIO Bar
1077   //
1078   mAhciBar = PciRead32 (PCI_LIB_ADDRESS (Bus, Device, Function, 0x24));
1079   //
1080   // Get AHCI MMIO Bar Size
1081   //
1082   Size = GetAhciBarSize (Bus, Device, Function);
1083   //
1084   // Check if the AHCI Bar region is in SMRAM to avoid malicious attack by modifying MMIO Bar to point to SMRAM.
1085   //
1086   if (!OpalIsValidMmioSpace ((EFI_PHYSICAL_ADDRESS)mAhciBar, Size)) {
1087     return EFI_UNSUPPORTED;
1088   }
1089 
1090   return EFI_SUCCESS;
1091 }
1092 
1093 /**
1094   Allocate transfer-related Data struct which is used at AHCI mode.
1095 
1096   @retval  EFI_OUT_OF_RESOURCE   The allocation is failure.
1097   @retval  EFI_SUCCESS           Successful to allocate memory.
1098 
1099 **/
1100 EFI_STATUS
1101 EFIAPI
AhciAllocateResource(VOID)1102 AhciAllocateResource (
1103   VOID
1104   )
1105 {
1106   EFI_STATUS            Status;
1107   EFI_PHYSICAL_ADDRESS  Base;
1108 
1109   //
1110   // Allocate resources required by AHCI host controller.
1111   //
1112   Base = 0xFFFFFFFF;
1113   Status = gBS->AllocatePages (
1114                   AllocateMaxAddress,
1115                   EfiACPIMemoryNVS,
1116                   EFI_SIZE_TO_PAGES (sizeof (EFI_AHCI_RECEIVED_FIS)),
1117                   &Base
1118                   );
1119   if (EFI_ERROR (Status)) {
1120     return EFI_OUT_OF_RESOURCES;
1121   }
1122 
1123   ZeroMem ((VOID *)(UINTN)Base, EFI_PAGE_SIZE * EFI_SIZE_TO_PAGES (sizeof (EFI_AHCI_RECEIVED_FIS)));
1124   mAhciRegisters.AhciRFis = (VOID *)(UINTN)Base;
1125 
1126   Base = 0xFFFFFFFF;
1127   Status = gBS->AllocatePages (
1128                   AllocateMaxAddress,
1129                   EfiACPIMemoryNVS,
1130                   EFI_SIZE_TO_PAGES (sizeof (EFI_AHCI_COMMAND_LIST)),
1131                   &Base
1132                   );
1133   if (EFI_ERROR (Status)) {
1134     gBS->FreePages ((EFI_PHYSICAL_ADDRESS)(UINTN)mAhciRegisters.AhciRFis, EFI_SIZE_TO_PAGES (sizeof (EFI_AHCI_RECEIVED_FIS)));
1135     return EFI_OUT_OF_RESOURCES;
1136   }
1137   ZeroMem ((VOID *)(UINTN)Base, EFI_PAGE_SIZE * EFI_SIZE_TO_PAGES (sizeof (EFI_AHCI_COMMAND_LIST)));
1138   mAhciRegisters.AhciCmdList = (VOID *)(UINTN)Base;
1139 
1140   Base = 0xFFFFFFFF;
1141   Status = gBS->AllocatePages (
1142                   AllocateMaxAddress,
1143                   EfiACPIMemoryNVS,
1144                   EFI_SIZE_TO_PAGES (sizeof (EFI_AHCI_COMMAND_TABLE)),
1145                   &Base
1146                   );
1147   if (EFI_ERROR (Status)) {
1148     gBS->FreePages ((EFI_PHYSICAL_ADDRESS)(UINTN)mAhciRegisters.AhciRFis, EFI_SIZE_TO_PAGES (sizeof (EFI_AHCI_RECEIVED_FIS)));
1149     gBS->FreePages ((EFI_PHYSICAL_ADDRESS)(UINTN)mAhciRegisters.AhciCmdList, EFI_SIZE_TO_PAGES (sizeof (EFI_AHCI_COMMAND_LIST)));
1150     return EFI_OUT_OF_RESOURCES;
1151   }
1152   ZeroMem ((VOID *)(UINTN)Base, EFI_PAGE_SIZE * EFI_SIZE_TO_PAGES (sizeof (EFI_AHCI_COMMAND_TABLE)));
1153   mAhciRegisters.AhciCommandTable = (VOID *)(UINTN)Base;
1154   return EFI_SUCCESS;
1155 }
1156 
1157 /**
1158   Free allocated transfer-related Data struct which is used at AHCI mode.
1159 
1160 **/
1161 VOID
1162 EFIAPI
AhciFreeResource(VOID)1163 AhciFreeResource (
1164   VOID
1165   )
1166 {
1167   if (mAhciRegisters.AhciRFis != NULL) {
1168     gBS->FreePages ((EFI_PHYSICAL_ADDRESS)(UINTN)mAhciRegisters.AhciRFis, EFI_SIZE_TO_PAGES (sizeof (EFI_AHCI_RECEIVED_FIS)));
1169   }
1170 
1171   if (mAhciRegisters.AhciCmdList != NULL) {
1172     gBS->FreePages ((EFI_PHYSICAL_ADDRESS)(UINTN)mAhciRegisters.AhciCmdList, EFI_SIZE_TO_PAGES (sizeof (EFI_AHCI_COMMAND_LIST)));
1173   }
1174 
1175   if (mAhciRegisters.AhciCommandTable != NULL) {
1176     gBS->FreePages ((EFI_PHYSICAL_ADDRESS)(UINTN)mAhciRegisters.AhciCommandTable, EFI_SIZE_TO_PAGES (sizeof (EFI_AHCI_COMMAND_TABLE)));
1177   }
1178 }
1179 
1180 /**
1181   Initialize ATA host controller at AHCI mode.
1182 
1183   The function is designed to initialize ATA host controller.
1184 
1185   @param[in]  Port          The port number to do initialization.
1186 
1187 **/
1188 EFI_STATUS
1189 EFIAPI
AhciModeInitialize(UINT8 Port)1190 AhciModeInitialize (
1191   UINT8              Port
1192   )
1193 {
1194   EFI_STATUS         Status;
1195   UINT32             Capability;
1196   UINT32             Offset;
1197   UINT32             Data;
1198   UINT32             PhyDetectDelay;
1199 
1200   Status = AhciReset (ATA_TIMEOUT);
1201   if (EFI_ERROR (Status)) {
1202     return Status;
1203   }
1204 
1205   //
1206   // Collect AHCI controller information
1207   //
1208   Capability = AhciReadReg (EFI_AHCI_CAPABILITY_OFFSET);
1209 
1210   //
1211   // Enable AE before accessing any AHCI registers if Supports AHCI Mode Only is not set
1212   //
1213   if ((Capability & EFI_AHCI_CAP_SAM) == 0) {
1214     AhciOrReg (EFI_AHCI_GHC_OFFSET, EFI_AHCI_GHC_ENABLE);
1215   }
1216 
1217   Offset = EFI_AHCI_PORT_START + Port * EFI_AHCI_PORT_REG_WIDTH + EFI_AHCI_PORT_FB;
1218   AhciWriteReg (Offset, (UINT32)(UINTN)mAhciRegisters.AhciRFis);
1219 
1220   //
1221   // Single task envrionment, we only use one command table for all port
1222   //
1223   Offset = EFI_AHCI_PORT_START + Port * EFI_AHCI_PORT_REG_WIDTH + EFI_AHCI_PORT_CLB;
1224   AhciWriteReg (Offset, (UINT32)(UINTN)mAhciRegisters.AhciCmdList);
1225 
1226   Offset = EFI_AHCI_PORT_START + Port * EFI_AHCI_PORT_REG_WIDTH + EFI_AHCI_PORT_CMD;
1227   Data = AhciReadReg (Offset);
1228   if ((Data & EFI_AHCI_PORT_CMD_CPD) != 0) {
1229     AhciOrReg (Offset, EFI_AHCI_PORT_CMD_POD);
1230   }
1231 
1232   if ((Capability & BIT27) != 0) {
1233     AhciOrReg (Offset, EFI_AHCI_PORT_CMD_SUD);
1234   }
1235 
1236   //
1237   // Disable aggressive power management.
1238   //
1239   Offset = EFI_AHCI_PORT_START + Port * EFI_AHCI_PORT_REG_WIDTH + EFI_AHCI_PORT_SCTL;
1240   AhciOrReg (Offset, EFI_AHCI_PORT_SCTL_IPM_INIT);
1241   //
1242   // Disable the reporting of the corresponding interrupt to system software.
1243   //
1244   Offset = EFI_AHCI_PORT_START + Port * EFI_AHCI_PORT_REG_WIDTH + EFI_AHCI_PORT_IE;
1245   AhciAndReg (Offset, 0);
1246 
1247   Status = AhciEnableFisReceive (
1248              Port,
1249              EFI_TIMER_PERIOD_MILLISECONDS(500)
1250              );
1251   ASSERT_EFI_ERROR (Status);
1252   if (EFI_ERROR (Status)) {
1253     return Status;
1254   }
1255 
1256   //
1257   // According to SATA1.0a spec section 5.2, we need to wait for PxTFD.BSY and PxTFD.DRQ
1258   // and PxTFD.ERR to be zero. The maximum wait time is 16s which is defined at ATA spec.
1259   //
1260   PhyDetectDelay = 16 * 1000;
1261   do {
1262     Offset = EFI_AHCI_PORT_START + Port * EFI_AHCI_PORT_REG_WIDTH + EFI_AHCI_PORT_SERR;
1263     if (AhciReadReg(Offset) != 0) {
1264       AhciWriteReg (Offset, AhciReadReg(Offset));
1265     }
1266     Offset = EFI_AHCI_PORT_START + Port * EFI_AHCI_PORT_REG_WIDTH + EFI_AHCI_PORT_TFD;
1267 
1268     Data = AhciReadReg (Offset) & EFI_AHCI_PORT_TFD_MASK;
1269     if (Data == 0) {
1270       break;
1271     }
1272 
1273     MicroSecondDelay (1000);
1274     PhyDetectDelay--;
1275   } while (PhyDetectDelay > 0);
1276 
1277   if (PhyDetectDelay == 0) {
1278     return EFI_NOT_FOUND;
1279   }
1280 
1281   Offset = EFI_AHCI_PORT_START + Port * EFI_AHCI_PORT_REG_WIDTH + EFI_AHCI_PORT_SIG;
1282   Status = AhciWaitMmioSet (
1283              Offset,
1284              0x0000FFFF,
1285              0x00000101,
1286              EFI_TIMER_PERIOD_SECONDS(16)
1287              );
1288 
1289   if (EFI_ERROR (Status)) {
1290     return Status;
1291   }
1292 
1293   return Status;
1294 }
1295 
1296