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