]> git.proxmox.com Git - mirror_edk2.git/blame - MdeModulePkg/Bus/Ata/AhciPei/AhciMode.c
MdeModulePkg/AhciPei: Add AHCI mode ATA device support in PEI
[mirror_edk2.git] / MdeModulePkg / Bus / Ata / AhciPei / AhciMode.c
CommitLineData
87bc3f19
HW
1/** @file\r
2 The AhciPei driver is used to manage ATA hard disk device working under AHCI\r
3 mode at PEI phase.\r
4\r
5 Copyright (c) 2019, Intel Corporation. All rights reserved.<BR>\r
6\r
7 This program and the accompanying materials\r
8 are licensed and made available under the terms and conditions\r
9 of the BSD License which accompanies this distribution. The\r
10 full text of the license may be found at\r
11 http://opensource.org/licenses/bsd-license.php\r
12\r
13 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
14 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
15\r
16**/\r
17\r
18#include "AhciPei.h"\r
19\r
20#define ATA_CMD_TRUST_NON_DATA 0x5B\r
21#define ATA_CMD_TRUST_RECEIVE 0x5C\r
22#define ATA_CMD_TRUST_SEND 0x5E\r
23\r
24//\r
25// Look up table (IsWrite) for EFI_ATA_PASS_THRU_CMD_PROTOCOL\r
26//\r
27EFI_ATA_PASS_THRU_CMD_PROTOCOL mAtaPassThruCmdProtocols[2] = {\r
28 EFI_ATA_PASS_THRU_PROTOCOL_PIO_DATA_IN,\r
29 EFI_ATA_PASS_THRU_PROTOCOL_PIO_DATA_OUT\r
30};\r
31\r
32//\r
33// Look up table (Lba48Bit, IsIsWrite) for ATA_CMD\r
34//\r
35UINT8 mAtaCommands[2][2] = {\r
36 {\r
37 ATA_CMD_READ_SECTORS, // 28-bit LBA; PIO read\r
38 ATA_CMD_WRITE_SECTORS // 28-bit LBA; PIO write\r
39 },\r
40 {\r
41 ATA_CMD_READ_SECTORS_EXT, // 48-bit LBA; PIO read\r
42 ATA_CMD_WRITE_SECTORS_EXT // 48-bit LBA; PIO write\r
43 }\r
44};\r
45\r
46//\r
47// Look up table (IsTrustSend) for ATA_CMD\r
48//\r
49UINT8 mAtaTrustCommands[2] = {\r
50 ATA_CMD_TRUST_RECEIVE, // PIO read\r
51 ATA_CMD_TRUST_SEND // PIO write\r
52};\r
53\r
54//\r
55// Look up table (Lba48Bit) for maximum transfer block number\r
56//\r
57#define MAX_28BIT_TRANSFER_BLOCK_NUM 0x100\r
58#define MAX_48BIT_TRANSFER_BLOCK_NUM 0xFFFF\r
59\r
60UINT32 mMaxTransferBlockNumber[2] = {\r
61 MAX_28BIT_TRANSFER_BLOCK_NUM,\r
62 MAX_48BIT_TRANSFER_BLOCK_NUM\r
63};\r
64\r
65//\r
66// The maximum total sectors count in 28 bit addressing mode\r
67//\r
68#define MAX_28BIT_ADDRESSING_CAPACITY 0xfffffff\r
69\r
70\r
71/**\r
72 Read AHCI Operation register.\r
73\r
74 @param[in] AhciBar AHCI bar address.\r
75 @param[in] Offset The operation register offset.\r
76\r
77 @return The register content read.\r
78\r
79**/\r
80UINT32\r
81AhciReadReg (\r
82 IN UINTN AhciBar,\r
83 IN UINT32 Offset\r
84 )\r
85{\r
86 UINT32 Data;\r
87\r
88 Data = 0;\r
89 Data = MmioRead32 (AhciBar + Offset);\r
90\r
91 return Data;\r
92}\r
93\r
94/**\r
95 Write AHCI Operation register.\r
96\r
97 @param[in] AhciBar AHCI bar address.\r
98 @param[in] Offset The operation register offset.\r
99 @param[in] Data The Data used to write down.\r
100\r
101**/\r
102VOID\r
103AhciWriteReg (\r
104 IN UINTN AhciBar,\r
105 IN UINT32 Offset,\r
106 IN UINT32 Data\r
107 )\r
108{\r
109 MmioWrite32 (AhciBar + Offset, Data);\r
110}\r
111\r
112/**\r
113 Do AND operation with the value of AHCI Operation register.\r
114\r
115 @param[in] AhciBar AHCI bar address.\r
116 @param[in] Offset The operation register offset.\r
117 @param[in] AndData The data used to do AND operation.\r
118\r
119**/\r
120VOID\r
121AhciAndReg (\r
122 IN UINTN AhciBar,\r
123 IN UINT32 Offset,\r
124 IN UINT32 AndData\r
125 )\r
126{\r
127 UINT32 Data;\r
128\r
129 Data = AhciReadReg (AhciBar, Offset);\r
130 Data &= AndData;\r
131\r
132 AhciWriteReg (AhciBar, Offset, Data);\r
133}\r
134\r
135/**\r
136 Do OR operation with the Value of AHCI Operation register.\r
137\r
138 @param[in] AhciBar AHCI bar address.\r
139 @param[in] Offset The operation register offset.\r
140 @param[in] OrData The Data used to do OR operation.\r
141\r
142**/\r
143VOID\r
144AhciOrReg (\r
145 IN UINTN AhciBar,\r
146 IN UINT32 Offset,\r
147 IN UINT32 OrData\r
148 )\r
149{\r
150 UINT32 Data;\r
151\r
152 Data = AhciReadReg (AhciBar, Offset);\r
153 Data |= OrData;\r
154\r
155 AhciWriteReg (AhciBar, Offset, Data);\r
156}\r
157\r
158/**\r
159 Wait for memory set to the test Value.\r
160\r
161 @param[in] AhciBar AHCI bar address.\r
162 @param[in] Offset The memory offset to test.\r
163 @param[in] MaskValue The mask Value of memory.\r
164 @param[in] TestValue The test Value of memory.\r
165 @param[in] Timeout The timeout, in 100ns units, for wait memory set.\r
166\r
167 @retval EFI_DEVICE_ERROR The memory is not set.\r
168 @retval EFI_TIMEOUT The memory setting is time out.\r
169 @retval EFI_SUCCESS The memory is correct set.\r
170\r
171**/\r
172EFI_STATUS\r
173EFIAPI\r
174AhciWaitMmioSet (\r
175 IN UINTN AhciBar,\r
176 IN UINT32 Offset,\r
177 IN UINT32 MaskValue,\r
178 IN UINT32 TestValue,\r
179 IN UINT64 Timeout\r
180 )\r
181{\r
182 UINT32 Value;\r
183 UINT32 Delay;\r
184\r
185 Delay = (UINT32) (DivU64x32(Timeout, 1000) + 1);\r
186\r
187 do {\r
188 Value = AhciReadReg (AhciBar, Offset) & MaskValue;\r
189\r
190 if (Value == TestValue) {\r
191 return EFI_SUCCESS;\r
192 }\r
193\r
194 //\r
195 // Stall for 100 microseconds.\r
196 //\r
197 MicroSecondDelay (100);\r
198\r
199 Delay--;\r
200\r
201 } while (Delay > 0);\r
202\r
203 return EFI_TIMEOUT;\r
204}\r
205\r
206/**\r
207 Check the memory status to the test value.\r
208\r
209 @param[in] Address The memory address to test.\r
210 @param[in] MaskValue The mask value of memory.\r
211 @param[in] TestValue The test value of memory.\r
212\r
213 @retval EFI_NOT_READY The memory is not set.\r
214 @retval EFI_SUCCESS The memory is correct set.\r
215\r
216**/\r
217EFI_STATUS\r
218AhciCheckMemSet (\r
219 IN UINTN Address,\r
220 IN UINT32 MaskValue,\r
221 IN UINT32 TestValue\r
222 )\r
223{\r
224 UINT32 Value;\r
225\r
226 Value = *(volatile UINT32 *) Address;\r
227 Value &= MaskValue;\r
228\r
229 if (Value == TestValue) {\r
230 return EFI_SUCCESS;\r
231 } else {\r
232 return EFI_NOT_READY;\r
233 }\r
234}\r
235\r
236/**\r
237 Wait for the value of the specified system memory set to the test value.\r
238\r
239 @param[in] Address The system memory address to test.\r
240 @param[in] MaskValue The mask value of memory.\r
241 @param[in] TestValue The test value of memory.\r
242 @param[in] Timeout The timeout, in 100ns units, for wait memory set.\r
243\r
244 @retval EFI_TIMEOUT The system memory setting is time out.\r
245 @retval EFI_SUCCESS The system memory is correct set.\r
246\r
247**/\r
248EFI_STATUS\r
249AhciWaitMemSet (\r
250 IN EFI_PHYSICAL_ADDRESS Address,\r
251 IN UINT32 MaskValue,\r
252 IN UINT32 TestValue,\r
253 IN UINT64 Timeout\r
254 )\r
255{\r
256 UINT32 Value;\r
257 UINT64 Delay;\r
258 BOOLEAN InfiniteWait;\r
259\r
260 if (Timeout == 0) {\r
261 InfiniteWait = TRUE;\r
262 } else {\r
263 InfiniteWait = FALSE;\r
264 }\r
265\r
266 Delay = DivU64x32 (Timeout, 1000) + 1;\r
267\r
268 do {\r
269 //\r
270 // Access sytem memory to see if the value is the tested one.\r
271 //\r
272 // The system memory pointed by Address will be updated by the\r
273 // SATA Host Controller, "volatile" is introduced to prevent\r
274 // compiler from optimizing the access to the memory address\r
275 // to only read once.\r
276 //\r
277 Value = *(volatile UINT32 *) (UINTN) Address;\r
278 Value &= MaskValue;\r
279\r
280 if (Value == TestValue) {\r
281 return EFI_SUCCESS;\r
282 }\r
283\r
284 //\r
285 // Stall for 100 microseconds.\r
286 //\r
287 MicroSecondDelay (100);\r
288\r
289 Delay--;\r
290\r
291 } while (InfiniteWait || (Delay > 0));\r
292\r
293 return EFI_TIMEOUT;\r
294}\r
295\r
296/**\r
297\r
298 Clear the port interrupt and error status. It will also clear HBA interrupt\r
299 status.\r
300\r
301 @param[in] AhciBar AHCI bar address.\r
302 @param[in] Port The number of port.\r
303\r
304**/\r
305VOID\r
306AhciClearPortStatus (\r
307 IN UINTN AhciBar,\r
308 IN UINT8 Port\r
309 )\r
310{\r
311 UINT32 Offset;\r
312\r
313 //\r
314 // Clear any error status\r
315 //\r
316 Offset = AHCI_PORT_START + Port * AHCI_PORT_REG_WIDTH + AHCI_PORT_SERR;\r
317 AhciWriteReg (AhciBar, Offset, AhciReadReg (AhciBar, Offset));\r
318\r
319 //\r
320 // Clear any port interrupt status\r
321 //\r
322 Offset = AHCI_PORT_START + Port * AHCI_PORT_REG_WIDTH + AHCI_PORT_IS;\r
323 AhciWriteReg (AhciBar, Offset, AhciReadReg (AhciBar, Offset));\r
324\r
325 //\r
326 // Clear any HBA interrupt status\r
327 //\r
328 AhciWriteReg (AhciBar, AHCI_IS_OFFSET, AhciReadReg (AhciBar, AHCI_IS_OFFSET));\r
329}\r
330\r
331/**\r
332 Enable the FIS running for giving port.\r
333\r
334 @param[in] AhciBar AHCI bar address.\r
335 @param[in] Port The number of port.\r
336 @param[in] Timeout The timeout, in 100ns units, to enabling FIS.\r
337\r
338 @retval EFI_DEVICE_ERROR The FIS enable setting fails.\r
339 @retval EFI_TIMEOUT The FIS enable setting is time out.\r
340 @retval EFI_SUCCESS The FIS enable successfully.\r
341\r
342**/\r
343EFI_STATUS\r
344AhciEnableFisReceive (\r
345 IN UINTN AhciBar,\r
346 IN UINT8 Port,\r
347 IN UINT64 Timeout\r
348 )\r
349{\r
350 UINT32 Offset;\r
351\r
352 Offset = AHCI_PORT_START + Port * AHCI_PORT_REG_WIDTH + AHCI_PORT_CMD;\r
353 AhciOrReg (AhciBar, Offset, AHCI_PORT_CMD_FRE);\r
354\r
355 return EFI_SUCCESS;\r
356}\r
357\r
358/**\r
359 Disable the FIS running for giving port.\r
360\r
361 @param[in] AhciBar AHCI bar address.\r
362 @param[in] Port The number of port.\r
363 @param[in] Timeout The timeout value of disabling FIS, uses 100ns as a unit.\r
364\r
365 @retval EFI_DEVICE_ERROR The FIS disable setting fails.\r
366 @retval EFI_TIMEOUT The FIS disable setting is time out.\r
367 @retval EFI_UNSUPPORTED The port is in running state.\r
368 @retval EFI_SUCCESS The FIS disable successfully.\r
369\r
370**/\r
371EFI_STATUS\r
372AhciDisableFisReceive (\r
373 IN UINTN AhciBar,\r
374 IN UINT8 Port,\r
375 IN UINT64 Timeout\r
376 )\r
377{\r
378 UINT32 Offset;\r
379 UINT32 Data;\r
380\r
381 Offset = AHCI_PORT_START + Port * AHCI_PORT_REG_WIDTH + AHCI_PORT_CMD;\r
382 Data = AhciReadReg (AhciBar, Offset);\r
383\r
384 //\r
385 // Before disabling Fis receive, the DMA engine of the port should NOT be in\r
386 // running status.\r
387 //\r
388 if ((Data & (AHCI_PORT_CMD_ST | AHCI_PORT_CMD_CR)) != 0) {\r
389 return EFI_UNSUPPORTED;\r
390 }\r
391\r
392 //\r
393 // Check if the Fis receive DMA engine for the port is running.\r
394 //\r
395 if ((Data & AHCI_PORT_CMD_FR) != AHCI_PORT_CMD_FR) {\r
396 return EFI_SUCCESS;\r
397 }\r
398\r
399 AhciAndReg (AhciBar, Offset, (UINT32)~(AHCI_PORT_CMD_FRE));\r
400\r
401 return AhciWaitMmioSet (\r
402 AhciBar,\r
403 Offset,\r
404 AHCI_PORT_CMD_FR,\r
405 0,\r
406 Timeout\r
407 );\r
408}\r
409\r
410/**\r
411 Build the command list, command table and prepare the fis receiver.\r
412\r
413 @param[in] Private The pointer to the PEI_AHCI_CONTROLLER_PRIVATE_DATA.\r
414 @param[in] Port The number of port.\r
415 @param[in] PortMultiplier The number of port multiplier.\r
416 @param[in] FisIndex The offset index of the FIS base address.\r
417 @param[in] CommandFis The control fis will be used for the transfer.\r
418 @param[in] CommandList The command list will be used for the transfer.\r
419 @param[in] CommandSlotNumber The command slot will be used for the transfer.\r
420 @param[in,out] DataPhysicalAddr The pointer to the data buffer pci bus master\r
421 address.\r
422 @param[in] DataLength The data count to be transferred.\r
423\r
424**/\r
425VOID\r
426AhciBuildCommand (\r
427 IN PEI_AHCI_CONTROLLER_PRIVATE_DATA *Private,\r
428 IN UINT8 Port,\r
429 IN UINT8 PortMultiplier,\r
430 IN UINT8 FisIndex,\r
431 IN EFI_AHCI_COMMAND_FIS *CommandFis,\r
432 IN EFI_AHCI_COMMAND_LIST *CommandList,\r
433 IN UINT8 CommandSlotNumber,\r
434 IN OUT VOID *DataPhysicalAddr,\r
435 IN UINT32 DataLength\r
436 )\r
437{\r
438 EFI_AHCI_REGISTERS *AhciRegisters;\r
439 UINTN AhciBar;\r
440 UINT64 BaseAddr;\r
441 UINT32 PrdtNumber;\r
442 UINT32 PrdtIndex;\r
443 UINTN RemainedData;\r
444 UINTN MemAddr;\r
445 DATA_64 Data64;\r
446 UINT32 Offset;\r
447\r
448 AhciRegisters = &Private->AhciRegisters;\r
449 AhciBar = Private->MmioBase;\r
450\r
451 //\r
452 // Filling the PRDT\r
453 //\r
454 PrdtNumber = (UINT32)DivU64x32 (\r
455 (UINT64)DataLength + AHCI_MAX_DATA_PER_PRDT - 1,\r
456 AHCI_MAX_DATA_PER_PRDT\r
457 );\r
458\r
459 //\r
460 // According to AHCI 1.3 spec, a PRDT entry can point to a maximum 4MB data block.\r
461 // It also limits that the maximum amount of the PRDT entry in the command table\r
462 // is 65535.\r
463 // Current driver implementation supports up to a maximum of AHCI_MAX_PRDT_NUMBER\r
464 // PRDT entries.\r
465 //\r
466 ASSERT (PrdtNumber <= AHCI_MAX_PRDT_NUMBER);\r
467 if (PrdtNumber > AHCI_MAX_PRDT_NUMBER) {\r
468 return;\r
469 }\r
470\r
471 Data64.Uint64 = (UINTN) (AhciRegisters->AhciRFis) + sizeof (EFI_AHCI_RECEIVED_FIS) * FisIndex;\r
472\r
473 BaseAddr = Data64.Uint64;\r
474\r
475 ZeroMem ((VOID *)((UINTN) BaseAddr), sizeof (EFI_AHCI_RECEIVED_FIS));\r
476\r
477 ZeroMem (AhciRegisters->AhciCmdTable, sizeof (EFI_AHCI_COMMAND_TABLE));\r
478\r
479 CommandFis->AhciCFisPmNum = PortMultiplier;\r
480\r
481 CopyMem (&AhciRegisters->AhciCmdTable->CommandFis, CommandFis, sizeof (EFI_AHCI_COMMAND_FIS));\r
482\r
483 Offset = AHCI_PORT_START + Port * AHCI_PORT_REG_WIDTH + AHCI_PORT_CMD;\r
484 AhciAndReg (AhciBar, Offset, (UINT32)~(AHCI_PORT_CMD_DLAE | AHCI_PORT_CMD_ATAPI));\r
485\r
486 RemainedData = (UINTN) DataLength;\r
487 MemAddr = (UINTN) DataPhysicalAddr;\r
488 CommandList->AhciCmdPrdtl = PrdtNumber;\r
489\r
490 for (PrdtIndex = 0; PrdtIndex < PrdtNumber; PrdtIndex++) {\r
491 if (RemainedData < AHCI_MAX_DATA_PER_PRDT) {\r
492 AhciRegisters->AhciCmdTable->PrdtTable[PrdtIndex].AhciPrdtDbc = (UINT32)RemainedData - 1;\r
493 } else {\r
494 AhciRegisters->AhciCmdTable->PrdtTable[PrdtIndex].AhciPrdtDbc = AHCI_MAX_DATA_PER_PRDT - 1;\r
495 }\r
496\r
497 Data64.Uint64 = (UINT64)MemAddr;\r
498 AhciRegisters->AhciCmdTable->PrdtTable[PrdtIndex].AhciPrdtDba = Data64.Uint32.Lower32;\r
499 AhciRegisters->AhciCmdTable->PrdtTable[PrdtIndex].AhciPrdtDbau = Data64.Uint32.Upper32;\r
500 RemainedData -= AHCI_MAX_DATA_PER_PRDT;\r
501 MemAddr += AHCI_MAX_DATA_PER_PRDT;\r
502 }\r
503\r
504 //\r
505 // Set the last PRDT to Interrupt On Complete\r
506 //\r
507 if (PrdtNumber > 0) {\r
508 AhciRegisters->AhciCmdTable->PrdtTable[PrdtNumber - 1].AhciPrdtIoc = 1;\r
509 }\r
510\r
511 CopyMem (\r
512 (VOID *) ((UINTN) AhciRegisters->AhciCmdList + (UINTN) CommandSlotNumber * sizeof (EFI_AHCI_COMMAND_LIST)),\r
513 CommandList,\r
514 sizeof (EFI_AHCI_COMMAND_LIST)\r
515 );\r
516\r
517 Data64.Uint64 = (UINT64)(UINTN) AhciRegisters->AhciCmdTable;\r
518 AhciRegisters->AhciCmdList[CommandSlotNumber].AhciCmdCtba = Data64.Uint32.Lower32;\r
519 AhciRegisters->AhciCmdList[CommandSlotNumber].AhciCmdCtbau = Data64.Uint32.Upper32;\r
520 AhciRegisters->AhciCmdList[CommandSlotNumber].AhciCmdPmp = PortMultiplier;\r
521}\r
522\r
523/**\r
524 Buid a command FIS.\r
525\r
526 @param[in,out] CmdFis A pointer to the EFI_AHCI_COMMAND_FIS data\r
527 structure.\r
528 @param[in] AtaCommandBlock A pointer to the EFI_ATA_COMMAND_BLOCK data\r
529 structure.\r
530\r
531**/\r
532VOID\r
533AhciBuildCommandFis (\r
534 IN OUT EFI_AHCI_COMMAND_FIS *CmdFis,\r
535 IN EFI_ATA_COMMAND_BLOCK *AtaCommandBlock\r
536 )\r
537{\r
538 ZeroMem (CmdFis, sizeof (EFI_AHCI_COMMAND_FIS));\r
539\r
540 CmdFis->AhciCFisType = AHCI_FIS_REGISTER_H2D;\r
541 //\r
542 // Indicator it's a command\r
543 //\r
544 CmdFis->AhciCFisCmdInd = 0x1;\r
545 CmdFis->AhciCFisCmd = AtaCommandBlock->AtaCommand;\r
546\r
547 CmdFis->AhciCFisFeature = AtaCommandBlock->AtaFeatures;\r
548 CmdFis->AhciCFisFeatureExp = AtaCommandBlock->AtaFeaturesExp;\r
549\r
550 CmdFis->AhciCFisSecNum = AtaCommandBlock->AtaSectorNumber;\r
551 CmdFis->AhciCFisSecNumExp = AtaCommandBlock->AtaSectorNumberExp;\r
552\r
553 CmdFis->AhciCFisClyLow = AtaCommandBlock->AtaCylinderLow;\r
554 CmdFis->AhciCFisClyLowExp = AtaCommandBlock->AtaCylinderLowExp;\r
555\r
556 CmdFis->AhciCFisClyHigh = AtaCommandBlock->AtaCylinderHigh;\r
557 CmdFis->AhciCFisClyHighExp = AtaCommandBlock->AtaCylinderHighExp;\r
558\r
559 CmdFis->AhciCFisSecCount = AtaCommandBlock->AtaSectorCount;\r
560 CmdFis->AhciCFisSecCountExp = AtaCommandBlock->AtaSectorCountExp;\r
561\r
562 CmdFis->AhciCFisDevHead = (UINT8) (AtaCommandBlock->AtaDeviceHead | 0xE0);\r
563}\r
564\r
565/**\r
566 Stop command running for giving port\r
567\r
568 @param[in] AhciBar AHCI bar address.\r
569 @param[in] Port The number of port.\r
570 @param[in] Timeout The timeout value, in 100ns units, to stop.\r
571\r
572 @retval EFI_DEVICE_ERROR The command stop unsuccessfully.\r
573 @retval EFI_TIMEOUT The operation is time out.\r
574 @retval EFI_SUCCESS The command stop successfully.\r
575\r
576**/\r
577EFI_STATUS\r
578AhciStopCommand (\r
579 IN UINTN AhciBar,\r
580 IN UINT8 Port,\r
581 IN UINT64 Timeout\r
582 )\r
583{\r
584 UINT32 Offset;\r
585 UINT32 Data;\r
586\r
587 Offset = AHCI_PORT_START + Port * AHCI_PORT_REG_WIDTH + AHCI_PORT_CMD;\r
588 Data = AhciReadReg (AhciBar, Offset);\r
589\r
590 if ((Data & (AHCI_PORT_CMD_ST | AHCI_PORT_CMD_CR)) == 0) {\r
591 return EFI_SUCCESS;\r
592 }\r
593\r
594 if ((Data & AHCI_PORT_CMD_ST) != 0) {\r
595 AhciAndReg (AhciBar, Offset, (UINT32)~(AHCI_PORT_CMD_ST));\r
596 }\r
597\r
598 return AhciWaitMmioSet (\r
599 AhciBar,\r
600 Offset,\r
601 AHCI_PORT_CMD_CR,\r
602 0,\r
603 Timeout\r
604 );\r
605}\r
606\r
607/**\r
608 Start command for give slot on specific port.\r
609\r
610 @param[in] AhciBar AHCI bar address.\r
611 @param[in] Port The number of port.\r
612 @param[in] CommandSlot The number of Command Slot.\r
613 @param[in] Timeout The timeout value, in 100ns units, to start.\r
614\r
615 @retval EFI_DEVICE_ERROR The command start unsuccessfully.\r
616 @retval EFI_TIMEOUT The operation is time out.\r
617 @retval EFI_SUCCESS The command start successfully.\r
618\r
619**/\r
620EFI_STATUS\r
621AhciStartCommand (\r
622 IN UINTN AhciBar,\r
623 IN UINT8 Port,\r
624 IN UINT8 CommandSlot,\r
625 IN UINT64 Timeout\r
626 )\r
627{\r
628 UINT32 CmdSlotBit;\r
629 EFI_STATUS Status;\r
630 UINT32 PortStatus;\r
631 UINT32 StartCmd;\r
632 UINT32 PortTfd;\r
633 UINT32 Offset;\r
634 UINT32 Capability;\r
635\r
636 //\r
637 // Collect AHCI controller information\r
638 //\r
639 Capability = AhciReadReg (AhciBar, AHCI_CAPABILITY_OFFSET);\r
640\r
641 CmdSlotBit = (UINT32) (1 << CommandSlot);\r
642\r
643 AhciClearPortStatus (\r
644 AhciBar,\r
645 Port\r
646 );\r
647\r
648 Status = AhciEnableFisReceive (\r
649 AhciBar,\r
650 Port,\r
651 Timeout\r
652 );\r
653 if (EFI_ERROR (Status)) {\r
654 return Status;\r
655 }\r
656\r
657 Offset = AHCI_PORT_START + Port * AHCI_PORT_REG_WIDTH + AHCI_PORT_CMD;\r
658 PortStatus = AhciReadReg (AhciBar, Offset);\r
659\r
660 StartCmd = 0;\r
661 if ((PortStatus & AHCI_PORT_CMD_ALPE) != 0) {\r
662 StartCmd = AhciReadReg (AhciBar, Offset);\r
663 StartCmd &= ~AHCI_PORT_CMD_ICC_MASK;\r
664 StartCmd |= AHCI_PORT_CMD_ACTIVE;\r
665 }\r
666\r
667 Offset = AHCI_PORT_START + Port * AHCI_PORT_REG_WIDTH + AHCI_PORT_TFD;\r
668 PortTfd = AhciReadReg (AhciBar, Offset);\r
669\r
670 if ((PortTfd & (AHCI_PORT_TFD_BSY | AHCI_PORT_TFD_DRQ)) != 0) {\r
671 if ((Capability & BIT24) != 0) {\r
672 Offset = AHCI_PORT_START + Port * AHCI_PORT_REG_WIDTH + AHCI_PORT_CMD;\r
673 AhciOrReg (AhciBar, Offset, AHCI_PORT_CMD_CLO);\r
674\r
675 AhciWaitMmioSet (\r
676 AhciBar,\r
677 Offset,\r
678 AHCI_PORT_CMD_CLO,\r
679 0,\r
680 Timeout\r
681 );\r
682 }\r
683 }\r
684\r
685 Offset = AHCI_PORT_START + Port * AHCI_PORT_REG_WIDTH + AHCI_PORT_CMD;\r
686 AhciOrReg (AhciBar, Offset, AHCI_PORT_CMD_ST | StartCmd);\r
687\r
688 //\r
689 // Setting the command\r
690 //\r
691 Offset = AHCI_PORT_START + Port * AHCI_PORT_REG_WIDTH + AHCI_PORT_CI;\r
692 AhciAndReg (AhciBar, Offset, 0);\r
693 AhciOrReg (AhciBar, Offset, CmdSlotBit);\r
694\r
695 return EFI_SUCCESS;\r
696}\r
697\r
698/**\r
699 Start a PIO Data transfer on specific port.\r
700\r
701 @param[in] Private The pointer to the PEI_AHCI_CONTROLLER_PRIVATE_DATA.\r
702 @param[in] Port The number of port.\r
703 @param[in] PortMultiplier The number of port multiplier.\r
704 @param[in] FisIndex The offset index of the FIS base address.\r
705 @param[in] Read The transfer direction.\r
706 @param[in] AtaCommandBlock The EFI_ATA_COMMAND_BLOCK data.\r
707 @param[in,out] AtaStatusBlock The EFI_ATA_STATUS_BLOCK data.\r
708 @param[in,out] MemoryAddr The pointer to the data buffer.\r
709 @param[in] DataCount The data count to be transferred.\r
710 @param[in] Timeout The timeout value of PIO data transfer, uses\r
711 100ns as a unit.\r
712\r
713 @retval EFI_DEVICE_ERROR The PIO data transfer abort with error occurs.\r
714 @retval EFI_TIMEOUT The operation is time out.\r
715 @retval EFI_UNSUPPORTED The device is not ready for transfer.\r
716 @retval EFI_OUT_OF_RESOURCES The operation fails due to lack of resources.\r
717 @retval EFI_SUCCESS The PIO data transfer executes successfully.\r
718\r
719**/\r
720EFI_STATUS\r
721AhciPioTransfer (\r
722 IN PEI_AHCI_CONTROLLER_PRIVATE_DATA *Private,\r
723 IN UINT8 Port,\r
724 IN UINT8 PortMultiplier,\r
725 IN UINT8 FisIndex,\r
726 IN BOOLEAN Read,\r
727 IN EFI_ATA_COMMAND_BLOCK *AtaCommandBlock,\r
728 IN OUT EFI_ATA_STATUS_BLOCK *AtaStatusBlock,\r
729 IN OUT VOID *MemoryAddr,\r
730 IN UINT32 DataCount,\r
731 IN UINT64 Timeout\r
732 )\r
733{\r
734 EFI_STATUS Status;\r
735 EDKII_IOMMU_OPERATION MapOp;\r
736 UINTN MapLength;\r
737 EFI_PHYSICAL_ADDRESS PhyAddr;\r
738 VOID *MapData;\r
739 EFI_AHCI_REGISTERS *AhciRegisters;\r
740 UINTN AhciBar;\r
741 BOOLEAN InfiniteWait;\r
742 UINT32 Offset;\r
743 UINT32 OldRfisLo;\r
744 UINT32 OldRfisHi;\r
745 UINT32 OldCmdListLo;\r
746 UINT32 OldCmdListHi;\r
747 DATA_64 Data64;\r
748 UINT32 FisBaseAddr;\r
749 UINT32 Delay;\r
750 EFI_AHCI_COMMAND_FIS CFis;\r
751 EFI_AHCI_COMMAND_LIST CmdList;\r
752 UINT32 PortTfd;\r
753 UINT32 PrdCount;\r
754 BOOLEAN PioFisReceived;\r
755 BOOLEAN D2hFisReceived;\r
756\r
757 //\r
758 // Current driver implementation supports up to a maximum of AHCI_MAX_PRDT_NUMBER\r
759 // PRDT entries.\r
760 //\r
761 if (DataCount / (UINT32)AHCI_MAX_PRDT_NUMBER > AHCI_MAX_DATA_PER_PRDT) {\r
762 DEBUG ((\r
763 DEBUG_ERROR,\r
764 "%a: Driver only support a maximum of 0x%x PRDT entries, "\r
765 "current number of data byte 0x%x is too large, maximum allowed is 0x%x.\n",\r
766 __FUNCTION__, AHCI_MAX_PRDT_NUMBER, DataCount,\r
767 AHCI_MAX_PRDT_NUMBER * AHCI_MAX_DATA_PER_PRDT\r
768 ));\r
769 return EFI_UNSUPPORTED;\r
770 }\r
771\r
772 MapOp = Read ? EdkiiIoMmuOperationBusMasterWrite :\r
773 EdkiiIoMmuOperationBusMasterRead;\r
774 MapLength = DataCount;\r
775 Status = IoMmuMap (\r
776 MapOp,\r
777 MemoryAddr,\r
778 &MapLength,\r
779 &PhyAddr,\r
780 &MapData\r
781 );\r
782 if (EFI_ERROR (Status) || (MapLength != DataCount)) {\r
783 DEBUG ((DEBUG_ERROR, "%a: Fail to map data buffer.\n", __FUNCTION__));\r
784 return EFI_OUT_OF_RESOURCES;\r
785 }\r
786\r
787 AhciRegisters = &Private->AhciRegisters;\r
788 AhciBar = Private->MmioBase;\r
789 InfiniteWait = (Timeout == 0) ? TRUE : FALSE;\r
790\r
791 //\r
792 // Fill FIS base address register\r
793 //\r
794 Offset = AHCI_PORT_START + Port * AHCI_PORT_REG_WIDTH + AHCI_PORT_FB;\r
795 OldRfisLo = AhciReadReg (AhciBar, Offset);\r
796 Offset = AHCI_PORT_START + Port * AHCI_PORT_REG_WIDTH + AHCI_PORT_FBU;\r
797 OldRfisHi = AhciReadReg (AhciBar, Offset);\r
798 Data64.Uint64 = (UINTN) (AhciRegisters->AhciRFis) + sizeof (EFI_AHCI_RECEIVED_FIS) * FisIndex;\r
799 Offset = AHCI_PORT_START + Port * AHCI_PORT_REG_WIDTH + AHCI_PORT_FB;\r
800 AhciWriteReg (AhciBar, Offset, Data64.Uint32.Lower32);\r
801 Offset = AHCI_PORT_START + Port * AHCI_PORT_REG_WIDTH + AHCI_PORT_FBU;\r
802 AhciWriteReg (AhciBar, Offset, Data64.Uint32.Upper32);\r
803\r
804 //\r
805 // Single task envrionment, we only use one command table for all port\r
806 //\r
807 Offset = AHCI_PORT_START + Port * AHCI_PORT_REG_WIDTH + AHCI_PORT_CLB;\r
808 OldCmdListLo = AhciReadReg (AhciBar, Offset);\r
809 Offset = AHCI_PORT_START + Port * AHCI_PORT_REG_WIDTH + AHCI_PORT_CLBU;\r
810 OldCmdListHi = AhciReadReg (AhciBar, Offset);\r
811 Data64.Uint64 = (UINTN) (AhciRegisters->AhciCmdList);\r
812 Offset = AHCI_PORT_START + Port * AHCI_PORT_REG_WIDTH + AHCI_PORT_CLB;\r
813 AhciWriteReg (AhciBar, Offset, Data64.Uint32.Lower32);\r
814 Offset = AHCI_PORT_START + Port * AHCI_PORT_REG_WIDTH + AHCI_PORT_CLBU;\r
815 AhciWriteReg (AhciBar, Offset, Data64.Uint32.Upper32);\r
816\r
817 //\r
818 // Package read needed\r
819 //\r
820 AhciBuildCommandFis (&CFis, AtaCommandBlock);\r
821\r
822 ZeroMem (&CmdList, sizeof (EFI_AHCI_COMMAND_LIST));\r
823\r
824 CmdList.AhciCmdCfl = AHCI_FIS_REGISTER_H2D_LENGTH / 4;\r
825 CmdList.AhciCmdW = Read ? 0 : 1;\r
826\r
827 AhciBuildCommand (\r
828 Private,\r
829 Port,\r
830 PortMultiplier,\r
831 FisIndex,\r
832 &CFis,\r
833 &CmdList,\r
834 0,\r
835 (VOID *)(UINTN)PhyAddr,\r
836 DataCount\r
837 );\r
838\r
839 Status = AhciStartCommand (\r
840 AhciBar,\r
841 Port,\r
842 0,\r
843 Timeout\r
844 );\r
845 if (EFI_ERROR (Status)) {\r
846 goto Exit;\r
847 }\r
848\r
849 //\r
850 // Checking the status and wait the driver sending Data\r
851 //\r
852 FisBaseAddr = (UINT32)(UINTN)AhciRegisters->AhciRFis + sizeof (EFI_AHCI_RECEIVED_FIS) * FisIndex;\r
853 if (Read) {\r
854 //\r
855 // Wait device sends the PIO setup fis before data transfer\r
856 //\r
857 Status = EFI_TIMEOUT;\r
858 Delay = (UINT32) DivU64x32 (Timeout, 1000) + 1;\r
859 do {\r
860 PioFisReceived = FALSE;\r
861 D2hFisReceived = FALSE;\r
862 Offset = FisBaseAddr + AHCI_PIO_FIS_OFFSET;\r
863 Status = AhciCheckMemSet (Offset, AHCI_FIS_TYPE_MASK, AHCI_FIS_PIO_SETUP);\r
864 if (!EFI_ERROR (Status)) {\r
865 DEBUG ((DEBUG_INFO, "%a: PioFisReceived.\n", __FUNCTION__));\r
866 PioFisReceived = TRUE;\r
867 }\r
868 //\r
869 // According to SATA 2.6 spec section 11.7, D2h FIS means an error encountered.\r
870 // But Qemu and Marvel 9230 sata controller may just receive a D2h FIS from\r
871 // device after the transaction is finished successfully.\r
872 // To get better device compatibilities, we further check if the PxTFD's\r
873 // ERR bit is set. By this way, we can know if there is a real error happened.\r
874 //\r
875 Offset = FisBaseAddr + AHCI_D2H_FIS_OFFSET;\r
876 Status = AhciCheckMemSet (Offset, AHCI_FIS_TYPE_MASK, AHCI_FIS_REGISTER_D2H);\r
877 if (!EFI_ERROR (Status)) {\r
878 DEBUG ((DEBUG_INFO, "%a: D2hFisReceived.\n", __FUNCTION__));\r
879 D2hFisReceived = TRUE;\r
880 }\r
881\r
882 if (PioFisReceived || D2hFisReceived) {\r
883 Offset = AHCI_PORT_START + Port * AHCI_PORT_REG_WIDTH + AHCI_PORT_TFD;\r
884 PortTfd = AhciReadReg (AhciBar, (UINT32) Offset);\r
885 //\r
886 // PxTFD will be updated if there is a D2H or SetupFIS received.\r
887 //\r
888 if ((PortTfd & AHCI_PORT_TFD_ERR) != 0) {\r
889 Status = EFI_DEVICE_ERROR;\r
890 break;\r
891 }\r
892\r
893 PrdCount = *(volatile UINT32 *) (&(AhciRegisters->AhciCmdList[0].AhciCmdPrdbc));\r
894 if (PrdCount == DataCount) {\r
895 Status = EFI_SUCCESS;\r
896 break;\r
897 }\r
898 }\r
899\r
900 //\r
901 // Stall for 100 microseconds.\r
902 //\r
903 MicroSecondDelay(100);\r
904\r
905 Delay--;\r
906 if (Delay == 0) {\r
907 Status = EFI_TIMEOUT;\r
908 }\r
909 } while (InfiniteWait || (Delay > 0));\r
910 } else {\r
911 //\r
912 // Wait for D2H Fis is received\r
913 //\r
914 Offset = FisBaseAddr + AHCI_D2H_FIS_OFFSET;\r
915 Status = AhciWaitMemSet (\r
916 Offset,\r
917 AHCI_FIS_TYPE_MASK,\r
918 AHCI_FIS_REGISTER_D2H,\r
919 Timeout\r
920 );\r
921 if (EFI_ERROR (Status)) {\r
922 DEBUG ((DEBUG_ERROR, "%a: AhciWaitMemSet (%r)\n", __FUNCTION__, Status));\r
923 goto Exit;\r
924 }\r
925\r
926 Offset = AHCI_PORT_START + Port * AHCI_PORT_REG_WIDTH + AHCI_PORT_TFD;\r
927 PortTfd = AhciReadReg (AhciBar, (UINT32) Offset);\r
928 if ((PortTfd & AHCI_PORT_TFD_ERR) != 0) {\r
929 Status = EFI_DEVICE_ERROR;\r
930 }\r
931 }\r
932\r
933Exit:\r
934 AhciStopCommand (\r
935 AhciBar,\r
936 Port,\r
937 Timeout\r
938 );\r
939\r
940 AhciDisableFisReceive (\r
941 AhciBar,\r
942 Port,\r
943 Timeout\r
944 );\r
945\r
946 if (MapData != NULL) {\r
947 IoMmuUnmap (MapData);\r
948 }\r
949\r
950 Offset = AHCI_PORT_START + Port * AHCI_PORT_REG_WIDTH + AHCI_PORT_FB;\r
951 AhciWriteReg (AhciBar, Offset, OldRfisLo);\r
952 Offset = AHCI_PORT_START + Port * AHCI_PORT_REG_WIDTH + AHCI_PORT_FBU;\r
953 AhciWriteReg (AhciBar, Offset, OldRfisHi);\r
954\r
955 Offset = AHCI_PORT_START + Port * AHCI_PORT_REG_WIDTH + AHCI_PORT_CLB;\r
956 AhciWriteReg (AhciBar, Offset, OldCmdListLo);\r
957 Offset = AHCI_PORT_START + Port * AHCI_PORT_REG_WIDTH + AHCI_PORT_CLBU;\r
958 AhciWriteReg (AhciBar, Offset, OldCmdListHi);\r
959\r
960 return Status;\r
961}\r
962\r
963/**\r
964 Start a non data transfer on specific port.\r
965\r
966 @param[in] Private The pointer to the PEI_AHCI_CONTROLLER_PRIVATE_DATA.\r
967 @param[in] Port The number of port.\r
968 @param[in] PortMultiplier The number of port multiplier.\r
969 @param[in] FisIndex The offset index of the FIS base address.\r
970 @param[in] AtaCommandBlock The EFI_ATA_COMMAND_BLOCK data.\r
971 @param[in,out] AtaStatusBlock The EFI_ATA_STATUS_BLOCK data.\r
972 @param[in] Timeout The timeout value of non data transfer, uses\r
973 100ns as a unit.\r
974\r
975 @retval EFI_DEVICE_ERROR The non data transfer abort with error occurs.\r
976 @retval EFI_TIMEOUT The operation is time out.\r
977 @retval EFI_UNSUPPORTED The device is not ready for transfer.\r
978 @retval EFI_SUCCESS The non data transfer executes successfully.\r
979\r
980**/\r
981EFI_STATUS\r
982AhciNonDataTransfer (\r
983 IN PEI_AHCI_CONTROLLER_PRIVATE_DATA *Private,\r
984 IN UINT8 Port,\r
985 IN UINT8 PortMultiplier,\r
986 IN UINT8 FisIndex,\r
987 IN EFI_ATA_COMMAND_BLOCK *AtaCommandBlock,\r
988 IN OUT EFI_ATA_STATUS_BLOCK *AtaStatusBlock,\r
989 IN UINT64 Timeout\r
990 )\r
991{\r
992 EFI_STATUS Status;\r
993 UINTN AhciBar;\r
994 EFI_AHCI_REGISTERS *AhciRegisters;\r
995 UINTN FisBaseAddr;\r
996 UINTN Offset;\r
997 UINT32 PortTfd;\r
998 EFI_AHCI_COMMAND_FIS CFis;\r
999 EFI_AHCI_COMMAND_LIST CmdList;\r
1000\r
1001 AhciBar = Private->MmioBase;\r
1002 AhciRegisters = &Private->AhciRegisters;\r
1003\r
1004 //\r
1005 // Package read needed\r
1006 //\r
1007 AhciBuildCommandFis (&CFis, AtaCommandBlock);\r
1008\r
1009 ZeroMem (&CmdList, sizeof (EFI_AHCI_COMMAND_LIST));\r
1010\r
1011 CmdList.AhciCmdCfl = AHCI_FIS_REGISTER_H2D_LENGTH / 4;\r
1012\r
1013 AhciBuildCommand (\r
1014 Private,\r
1015 Port,\r
1016 PortMultiplier,\r
1017 FisIndex,\r
1018 &CFis,\r
1019 &CmdList,\r
1020 0,\r
1021 NULL,\r
1022 0\r
1023 );\r
1024\r
1025 Status = AhciStartCommand (\r
1026 AhciBar,\r
1027 Port,\r
1028 0,\r
1029 Timeout\r
1030 );\r
1031 if (EFI_ERROR (Status)) {\r
1032 goto Exit;\r
1033 }\r
1034\r
1035 //\r
1036 // Wait device sends the Response Fis\r
1037 //\r
1038 FisBaseAddr = (UINTN)AhciRegisters->AhciRFis + sizeof (EFI_AHCI_RECEIVED_FIS) * FisIndex;\r
1039 Offset = FisBaseAddr + AHCI_D2H_FIS_OFFSET;\r
1040 Status = AhciWaitMemSet (\r
1041 Offset,\r
1042 AHCI_FIS_TYPE_MASK,\r
1043 AHCI_FIS_REGISTER_D2H,\r
1044 Timeout\r
1045 );\r
1046\r
1047 if (EFI_ERROR (Status)) {\r
1048 goto Exit;\r
1049 }\r
1050\r
1051 Offset = AHCI_PORT_START + Port * AHCI_PORT_REG_WIDTH + AHCI_PORT_TFD;\r
1052 PortTfd = AhciReadReg (AhciBar, (UINT32) Offset);\r
1053 if ((PortTfd & AHCI_PORT_TFD_ERR) != 0) {\r
1054 Status = EFI_DEVICE_ERROR;\r
1055 }\r
1056\r
1057Exit:\r
1058 AhciStopCommand (\r
1059 AhciBar,\r
1060 Port,\r
1061 Timeout\r
1062 );\r
1063\r
1064 AhciDisableFisReceive (\r
1065 AhciBar,\r
1066 Port,\r
1067 Timeout\r
1068 );\r
1069\r
1070 return Status;\r
1071}\r
1072\r
1073/**\r
1074 Do AHCI HBA reset.\r
1075\r
1076 @param[in] AhciBar AHCI bar address.\r
1077 @param[in] Timeout The timeout, in 100ns units, to reset.\r
1078\r
1079 @retval EFI_DEVICE_ERROR AHCI controller is failed to complete hardware reset.\r
1080 @retval EFI_TIMEOUT The reset operation is time out.\r
1081 @retval EFI_SUCCESS AHCI controller is reset successfully.\r
1082\r
1083**/\r
1084EFI_STATUS\r
1085AhciReset (\r
1086 IN UINTN AhciBar,\r
1087 IN UINT64 Timeout\r
1088 )\r
1089{\r
1090 UINT32 Delay;\r
1091 UINT32 Value;\r
1092 UINT32 Capability;\r
1093\r
1094 //\r
1095 // Collect AHCI controller information\r
1096 //\r
1097 Capability = AhciReadReg (AhciBar, AHCI_CAPABILITY_OFFSET);\r
1098\r
1099 //\r
1100 // Enable AE before accessing any AHCI registers if Supports AHCI Mode Only is not set\r
1101 //\r
1102 if ((Capability & AHCI_CAP_SAM) == 0) {\r
1103 AhciOrReg (AhciBar, AHCI_GHC_OFFSET, AHCI_GHC_ENABLE);\r
1104 }\r
1105\r
1106 AhciOrReg (AhciBar, AHCI_GHC_OFFSET, AHCI_GHC_RESET);\r
1107\r
1108 Delay = (UINT32) (DivU64x32(Timeout, 1000) + 1);\r
1109\r
1110 do {\r
1111 Value = AhciReadReg(AhciBar, AHCI_GHC_OFFSET);\r
1112 if ((Value & AHCI_GHC_RESET) == 0) {\r
1113 return EFI_SUCCESS;\r
1114 }\r
1115\r
1116 //\r
1117 // Stall for 100 microseconds.\r
1118 //\r
1119 MicroSecondDelay(100);\r
1120\r
1121 Delay--;\r
1122 } while (Delay > 0);\r
1123\r
1124 return EFI_TIMEOUT;\r
1125}\r
1126\r
1127/**\r
1128 Send Identify Drive command to a specific device.\r
1129\r
1130 @param[in] Private The pointer to the PEI_AHCI_CONTROLLER_PRIVATE_DATA.\r
1131 @param[in] Port The number of port.\r
1132 @param[in] PortMultiplier The port multiplier port number.\r
1133 @param[in] FisIndex The offset index of the FIS base address.\r
1134 @param[in] Buffer The data buffer to store IDENTIFY PACKET data.\r
1135\r
1136 @retval EFI_SUCCESS The cmd executes successfully.\r
1137 @retval EFI_INVALID_PARAMETER Buffer is NULL.\r
1138 @retval EFI_DEVICE_ERROR The cmd abort with error occurs.\r
1139 @retval EFI_TIMEOUT The operation is time out.\r
1140 @retval EFI_UNSUPPORTED The device is not ready for executing.\r
1141\r
1142**/\r
1143EFI_STATUS\r
1144AhciIdentify (\r
1145 IN PEI_AHCI_CONTROLLER_PRIVATE_DATA *Private,\r
1146 IN UINT8 Port,\r
1147 IN UINT8 PortMultiplier,\r
1148 IN UINT8 FisIndex,\r
1149 IN ATA_IDENTIFY_DATA *Buffer\r
1150 )\r
1151{\r
1152 EFI_STATUS Status;\r
1153 EFI_ATA_COMMAND_BLOCK Acb;\r
1154 EFI_ATA_STATUS_BLOCK Asb;\r
1155\r
1156 if (Buffer == NULL) {\r
1157 return EFI_INVALID_PARAMETER;\r
1158 }\r
1159\r
1160 ZeroMem (&Acb, sizeof (EFI_ATA_COMMAND_BLOCK));\r
1161 ZeroMem (&Asb, sizeof (EFI_ATA_STATUS_BLOCK));\r
1162\r
1163 Acb.AtaCommand = ATA_CMD_IDENTIFY_DRIVE;\r
1164 Acb.AtaSectorCount = 1;\r
1165\r
1166 Status = AhciPioTransfer (\r
1167 Private,\r
1168 Port,\r
1169 PortMultiplier,\r
1170 FisIndex,\r
1171 TRUE,\r
1172 &Acb,\r
1173 &Asb,\r
1174 Buffer,\r
1175 sizeof (ATA_IDENTIFY_DATA),\r
1176 ATA_TIMEOUT\r
1177 );\r
1178\r
1179 return Status;\r
1180}\r
1181\r
1182\r
1183/**\r
1184 Collect the number of bits set within a port bitmap.\r
1185\r
1186 @param[in] PortBitMap A 32-bit wide bit map of ATA AHCI ports.\r
1187\r
1188 @retval The number of bits set in the bitmap.\r
1189\r
1190**/\r
1191UINT8\r
1192AhciGetNumberOfPortsFromMap (\r
1193 IN UINT32 PortBitMap\r
1194 )\r
1195{\r
1196 UINT8 NumberOfPorts;\r
1197\r
1198 NumberOfPorts = 0;\r
1199\r
1200 while (PortBitMap != 0) {\r
1201 if ((PortBitMap & ((UINT32)BIT0)) != 0) {\r
1202 NumberOfPorts++;\r
1203 }\r
1204 PortBitMap = PortBitMap >> 1;\r
1205 }\r
1206\r
1207 return NumberOfPorts;\r
1208}\r
1209\r
1210/**\r
1211 Get the specified port number from a port bitmap.\r
1212\r
1213 @param[in] PortBitMap A 32-bit wide bit map of ATA AHCI ports.\r
1214 @param[in] PortIndex The specified port index.\r
1215 @param[out] Port The port number of the port specified by PortIndex.\r
1216\r
1217 @retval EFI_SUCCESS The specified port is found and its port number is\r
1218 in Port.\r
1219 @retval EFI_NOT_FOUND Cannot find the specified port within the port bitmap.\r
1220\r
1221**/\r
1222EFI_STATUS\r
1223AhciGetPortFromMap (\r
1224 IN UINT32 PortBitMap,\r
1225 IN UINT8 PortIndex,\r
1226 OUT UINT8 *Port\r
1227 )\r
1228{\r
1229 if (PortIndex == 0) {\r
1230 return EFI_NOT_FOUND;\r
1231 }\r
1232\r
1233 *Port = 0;\r
1234\r
1235 while (PortBitMap != 0) {\r
1236 if ((PortBitMap & ((UINT32)BIT0)) != 0) {\r
1237 PortIndex--;\r
1238\r
1239 //\r
1240 // Found the port specified by PortIndex.\r
1241 //\r
1242 if (PortIndex == 0) {\r
1243 return EFI_SUCCESS;\r
1244 }\r
1245 }\r
1246 PortBitMap = PortBitMap >> 1;\r
1247 *Port = *Port + 1;\r
1248 }\r
1249\r
1250 return EFI_NOT_FOUND;\r
1251}\r
1252\r
1253/**\r
1254 Allocate transfer-related data struct which is used at AHCI mode.\r
1255\r
1256 @param[in,out] Private A pointer to the PEI_AHCI_CONTROLLER_PRIVATE_DATA instance.\r
1257\r
1258 @retval EFI_SUCCESS Data structures are allocated successfully.\r
1259 @retval Others Data structures are not allocated successfully.\r
1260\r
1261**/\r
1262EFI_STATUS\r
1263AhciCreateTransferDescriptor (\r
1264 IN OUT PEI_AHCI_CONTROLLER_PRIVATE_DATA *Private\r
1265 )\r
1266{\r
1267 EFI_STATUS Status;\r
1268 UINTN AhciBar;\r
1269 EFI_AHCI_REGISTERS *AhciRegisters;\r
1270 EFI_PHYSICAL_ADDRESS DeviceAddress;\r
1271 VOID *Base;\r
1272 VOID *Mapping;\r
1273 UINT32 Capability;\r
1274 UINT32 PortImplementBitMap;\r
1275 UINT8 MaxPortNumber;\r
1276 UINT8 MaxCommandSlotNumber;\r
1277 UINTN MaxRFisSize;\r
1278 UINTN MaxCmdListSize;\r
1279 UINTN MaxCmdTableSize;\r
1280\r
1281 AhciBar = Private->MmioBase;\r
1282 AhciRegisters = &Private->AhciRegisters;\r
1283\r
1284 //\r
1285 // Collect AHCI controller information\r
1286 //\r
1287 Capability = AhciReadReg (AhciBar, AHCI_CAPABILITY_OFFSET);\r
1288\r
1289 //\r
1290 // Get the number of command slots per port supported by this HBA.\r
1291 //\r
1292 MaxCommandSlotNumber = (UINT8) (((Capability & 0x1F00) >> 8) + 1);\r
1293 ASSERT (MaxCommandSlotNumber > 0);\r
1294 if (MaxCommandSlotNumber == 0) {\r
1295 return EFI_DEVICE_ERROR;\r
1296 }\r
1297\r
1298 //\r
1299 // Get the highest bit of implemented ports which decides how many bytes are\r
1300 // allocated for recived FIS.\r
1301 //\r
1302 PortImplementBitMap = AhciReadReg (AhciBar, AHCI_PI_OFFSET);\r
1303 MaxPortNumber = (UINT8)(UINTN)(HighBitSet32(PortImplementBitMap) + 1);\r
1304 if (MaxPortNumber == 0) {\r
1305 return EFI_DEVICE_ERROR;\r
1306 }\r
1307 //\r
1308 // Get the number of ports that actually needed to be initialized.\r
1309 //\r
1310 MaxPortNumber = MIN (MaxPortNumber, AhciGetNumberOfPortsFromMap (Private->PortBitMap));\r
1311\r
1312 //\r
1313 // Allocate memory for received FIS.\r
1314 //\r
1315 MaxRFisSize = MaxPortNumber * sizeof (EFI_AHCI_RECEIVED_FIS);\r
1316 Status = IoMmuAllocateBuffer (\r
1317 EFI_SIZE_TO_PAGES (MaxRFisSize),\r
1318 &Base,\r
1319 &DeviceAddress,\r
1320 &Mapping\r
1321 );\r
1322 if (EFI_ERROR (Status)) {\r
1323 return EFI_OUT_OF_RESOURCES;\r
1324 }\r
1325 ASSERT (DeviceAddress == ((EFI_PHYSICAL_ADDRESS) (UINTN) Base));\r
1326 AhciRegisters->AhciRFis = Base;\r
1327 AhciRegisters->AhciRFisMap = Mapping;\r
1328 AhciRegisters->MaxRFisSize = MaxRFisSize;\r
1329 ZeroMem (AhciRegisters->AhciRFis, EFI_PAGE_SIZE * EFI_SIZE_TO_PAGES (MaxRFisSize));\r
1330\r
1331 //\r
1332 // Allocate memory for command list.\r
1333 // Note that the implemenation is a single task model which only use a command\r
1334 // list for each port.\r
1335 //\r
1336 MaxCmdListSize = 1 * sizeof (EFI_AHCI_COMMAND_LIST);\r
1337 Status = IoMmuAllocateBuffer (\r
1338 EFI_SIZE_TO_PAGES (MaxCmdListSize),\r
1339 &Base,\r
1340 &DeviceAddress,\r
1341 &Mapping\r
1342 );\r
1343 if (EFI_ERROR (Status)) {\r
1344 Status = EFI_OUT_OF_RESOURCES;\r
1345 goto ErrorExit;\r
1346 }\r
1347 ASSERT (DeviceAddress == ((EFI_PHYSICAL_ADDRESS) (UINTN) Base));\r
1348 AhciRegisters->AhciCmdList = Base;\r
1349 AhciRegisters->AhciCmdListMap = Mapping;\r
1350 AhciRegisters->MaxCmdListSize = MaxCmdListSize;\r
1351 ZeroMem (AhciRegisters->AhciCmdList, EFI_PAGE_SIZE * EFI_SIZE_TO_PAGES (MaxCmdListSize));\r
1352\r
1353 //\r
1354 // Allocate memory for command table\r
1355 // According to AHCI 1.3 spec, a PRD table can contain maximum 65535 entries.\r
1356 //\r
1357 MaxCmdTableSize = sizeof (EFI_AHCI_COMMAND_TABLE);\r
1358 Status = IoMmuAllocateBuffer (\r
1359 EFI_SIZE_TO_PAGES (MaxCmdTableSize),\r
1360 &Base,\r
1361 &DeviceAddress,\r
1362 &Mapping\r
1363 );\r
1364 if (EFI_ERROR (Status)) {\r
1365 Status = EFI_OUT_OF_RESOURCES;\r
1366 goto ErrorExit;\r
1367 }\r
1368 ASSERT (DeviceAddress == ((EFI_PHYSICAL_ADDRESS) (UINTN) Base));\r
1369 AhciRegisters->AhciCmdTable = Base;\r
1370 AhciRegisters->AhciCmdTableMap = Mapping;\r
1371 AhciRegisters->MaxCmdTableSize = MaxCmdTableSize;\r
1372 ZeroMem (AhciRegisters->AhciCmdTable, EFI_PAGE_SIZE * EFI_SIZE_TO_PAGES (MaxCmdTableSize));\r
1373\r
1374 return EFI_SUCCESS;\r
1375\r
1376ErrorExit:\r
1377 if (AhciRegisters->AhciRFisMap != NULL) {\r
1378 IoMmuFreeBuffer (\r
1379 EFI_SIZE_TO_PAGES (AhciRegisters->MaxRFisSize),\r
1380 AhciRegisters->AhciRFis,\r
1381 AhciRegisters->AhciRFisMap\r
1382 );\r
1383 AhciRegisters->AhciRFis = NULL;\r
1384 }\r
1385\r
1386 if (AhciRegisters->AhciCmdListMap != NULL) {\r
1387 IoMmuFreeBuffer (\r
1388 EFI_SIZE_TO_PAGES (AhciRegisters->MaxCmdListSize),\r
1389 AhciRegisters->AhciCmdList,\r
1390 AhciRegisters->AhciCmdListMap\r
1391 );\r
1392 AhciRegisters->AhciCmdList = NULL;\r
1393 }\r
1394\r
1395 return Status;\r
1396}\r
1397\r
1398/**\r
1399 Gets ATA device Capacity according to ATA 6.\r
1400\r
1401 This function returns the capacity of the ATA device if it follows\r
1402 ATA 6 to support 48 bit addressing.\r
1403\r
1404 @param[in] IdentifyData A pointer to ATA_IDENTIFY_DATA structure.\r
1405\r
1406 @return The capacity of the ATA device or 0 if the device does not support\r
1407 48-bit addressing defined in ATA 6.\r
1408\r
1409**/\r
1410EFI_LBA\r
1411GetAtapi6Capacity (\r
1412 IN ATA_IDENTIFY_DATA *IdentifyData\r
1413 )\r
1414{\r
1415 EFI_LBA Capacity;\r
1416 EFI_LBA TmpLba;\r
1417 UINTN Index;\r
1418\r
1419 if ((IdentifyData->command_set_supported_83 & BIT10) == 0) {\r
1420 //\r
1421 // The device doesn't support 48 bit addressing\r
1422 //\r
1423 return 0;\r
1424 }\r
1425\r
1426 //\r
1427 // 48 bit address feature set is supported, get maximum capacity\r
1428 //\r
1429 Capacity = 0;\r
1430 for (Index = 0; Index < 4; Index++) {\r
1431 //\r
1432 // Lower byte goes first: word[100] is the lowest word, word[103] is highest\r
1433 //\r
1434 TmpLba = IdentifyData->maximum_lba_for_48bit_addressing[Index];\r
1435 Capacity |= LShiftU64 (TmpLba, 16 * Index);\r
1436 }\r
1437\r
1438 return Capacity;\r
1439}\r
1440\r
1441/**\r
1442 Identifies ATA device via the Identify data.\r
1443\r
1444 This function identifies the ATA device and initializes the media information.\r
1445\r
1446 @attention This is boundary function that may receive untrusted input.\r
1447 @attention The input is from peripheral hardware device.\r
1448\r
1449 The Identify Drive command response data from an ATA device is the peripheral\r
1450 hardware input, so this routine will do basic validation for the Identify Drive\r
1451 command response data.\r
1452\r
1453 @param[in,out] DeviceData A pointer to PEI_AHCI_ATA_DEVICE_DATA structure.\r
1454\r
1455 @retval EFI_SUCCESS The device is successfully identified and media\r
1456 information is correctly initialized.\r
1457 @retval EFI_UNSUPPORTED The device is not a valid ATA device (hard disk).\r
1458\r
1459**/\r
1460EFI_STATUS\r
1461IdentifyAtaDevice (\r
1462 IN OUT PEI_AHCI_ATA_DEVICE_DATA *DeviceData\r
1463 )\r
1464{\r
1465 ATA_IDENTIFY_DATA *IdentifyData;\r
1466 EFI_PEI_BLOCK_IO2_MEDIA *Media;\r
1467 EFI_LBA Capacity;\r
1468 UINT32 MaxSectorCount;\r
1469 UINT16 PhyLogicSectorSupport;\r
1470\r
1471 IdentifyData = DeviceData->IdentifyData;\r
1472 Media = &DeviceData->Media;\r
1473\r
1474 if ((IdentifyData->config & BIT15) != 0) {\r
1475 DEBUG ((\r
1476 DEBUG_ERROR, "%a: Not a hard disk device on Port 0x%x PortMultiplierPort 0x%x\n",\r
1477 __FUNCTION__, DeviceData->Port, DeviceData->PortMultiplier\r
1478 ));\r
1479 return EFI_UNSUPPORTED;\r
1480 }\r
1481\r
1482 DEBUG ((\r
1483 DEBUG_INFO, "%a: Identify Device: Port 0x%x PortMultiplierPort 0x%x\n",\r
1484 __FUNCTION__, DeviceData->Port, DeviceData->PortMultiplier\r
1485 ));\r
1486\r
1487 //\r
1488 // Skip checking whether the WORD 88 (supported UltraDMA by drive), since the\r
1489 // driver only support PIO data transfer for now.\r
1490 //\r
1491\r
1492 //\r
1493 // Get the capacity information of the device.\r
1494 //\r
1495 Capacity = GetAtapi6Capacity (IdentifyData);\r
1496 if (Capacity > MAX_28BIT_ADDRESSING_CAPACITY) {\r
1497 //\r
1498 // Capacity exceeds 120GB. 48-bit addressing is really needed\r
1499 //\r
1500 DeviceData->Lba48Bit = TRUE;\r
1501 } else {\r
1502 //\r
1503 // This is a hard disk <= 120GB capacity, treat it as normal hard disk\r
1504 //\r
1505 Capacity = ((UINT32)IdentifyData->user_addressable_sectors_hi << 16) |\r
1506 IdentifyData->user_addressable_sectors_lo;\r
1507 DeviceData->Lba48Bit = FALSE;\r
1508 }\r
1509\r
1510 if (Capacity == 0) {\r
1511 DEBUG ((DEBUG_ERROR, "%a: Invalid Capacity (0) for ATA device.\n", __FUNCTION__));\r
1512 return EFI_UNSUPPORTED;\r
1513 }\r
1514 Media->LastBlock = (EFI_PEI_LBA) (Capacity - 1);\r
1515\r
1516 Media->BlockSize = 0x200;\r
1517 //\r
1518 // Check whether Long Physical Sector Feature is supported\r
1519 //\r
1520 PhyLogicSectorSupport = IdentifyData->phy_logic_sector_support;\r
1521 DEBUG ((\r
1522 DEBUG_INFO, "%a: PhyLogicSectorSupport = 0x%x\n",\r
1523 __FUNCTION__, PhyLogicSectorSupport\r
1524 ));\r
1525 if ((PhyLogicSectorSupport & (BIT14 | BIT15)) == BIT14) {\r
1526 //\r
1527 // Check logical block size\r
1528 //\r
1529 if ((PhyLogicSectorSupport & BIT12) != 0) {\r
1530 Media->BlockSize = (UINT32) (((IdentifyData->logic_sector_size_hi << 16) |\r
1531 IdentifyData->logic_sector_size_lo) * sizeof (UINT16));\r
1532 }\r
1533 }\r
1534\r
1535 //\r
1536 // Check BlockSize validity\r
1537 //\r
1538 MaxSectorCount = mMaxTransferBlockNumber[DeviceData->Lba48Bit];\r
1539 if ((Media->BlockSize == 0) || (Media->BlockSize > MAX_UINT32 / MaxSectorCount)) {\r
1540 DEBUG ((DEBUG_ERROR, "%a: Invalid BlockSize (0x%x).\n", __FUNCTION__, Media->BlockSize));\r
1541 return EFI_UNSUPPORTED;\r
1542 }\r
1543\r
1544 DEBUG ((\r
1545 DEBUG_INFO, "%a: BlockSize = 0x%x, LastBlock = 0x%lx\n",\r
1546 __FUNCTION__, Media->BlockSize, Media->LastBlock\r
1547 ));\r
1548\r
1549 if ((IdentifyData->trusted_computing_support & BIT0) != 0) {\r
1550 DEBUG ((DEBUG_INFO, "%a: Found Trust Computing feature support.\n", __FUNCTION__));\r
1551 DeviceData->TrustComputing = TRUE;\r
1552 }\r
1553\r
1554 Media->InterfaceType = MSG_SATA_DP;\r
1555 Media->RemovableMedia = FALSE;\r
1556 Media->MediaPresent = TRUE;\r
1557 Media->ReadOnly = FALSE;\r
1558\r
1559 return EFI_SUCCESS;\r
1560}\r
1561\r
1562/**\r
1563 Allocate device information data structure to contain device information.\r
1564 And insert the data structure to the tail of device list for tracing.\r
1565\r
1566 @param[in,out] Private A pointer to the PEI_AHCI_CONTROLLER_PRIVATE_DATA\r
1567 instance.\r
1568 @param[in] DeviceIndex The device index.\r
1569 @param[in] Port The port number of the ATA device to send\r
1570 the command.\r
1571 @param[in] PortMultiplierPort The port multiplier port number of the ATA\r
1572 device to send the command.\r
1573 If there is no port multiplier, then specify\r
1574 0xFFFF.\r
1575 @param[in] FisIndex The index of the FIS of the ATA device to\r
1576 send the command.\r
1577 @param[in] IdentifyData The data buffer to store the output of the\r
1578 IDENTIFY command.\r
1579\r
1580 @retval EFI_SUCCESS Successfully insert the ATA device to the\r
1581 tail of device list.\r
1582 @retval EFI_OUT_OF_RESOURCES Not enough resource.\r
1583\r
1584**/\r
1585EFI_STATUS\r
1586CreateNewDevice (\r
1587 IN OUT PEI_AHCI_CONTROLLER_PRIVATE_DATA *Private,\r
1588 IN UINTN DeviceIndex,\r
1589 IN UINT16 Port,\r
1590 IN UINT16 PortMultiplier,\r
1591 IN UINT8 FisIndex,\r
1592 IN ATA_IDENTIFY_DATA *IdentifyData\r
1593 )\r
1594{\r
1595 PEI_AHCI_ATA_DEVICE_DATA *DeviceData;\r
1596 EFI_STATUS Status;\r
1597\r
1598 DeviceData = AllocateZeroPool (sizeof (PEI_AHCI_ATA_DEVICE_DATA));\r
1599 if (DeviceData == NULL) {\r
1600 return EFI_OUT_OF_RESOURCES;\r
1601 }\r
1602\r
1603 if (IdentifyData != NULL) {\r
1604 DeviceData->IdentifyData = AllocateCopyPool (sizeof (ATA_IDENTIFY_DATA), IdentifyData);\r
1605 if (DeviceData->IdentifyData == NULL) {\r
1606 return EFI_OUT_OF_RESOURCES;\r
1607 }\r
1608 }\r
1609\r
1610 DeviceData->Signature = AHCI_PEI_ATA_DEVICE_DATA_SIGNATURE;\r
1611 DeviceData->Port = Port;\r
1612 DeviceData->PortMultiplier = PortMultiplier;\r
1613 DeviceData->FisIndex = FisIndex;\r
1614 DeviceData->DeviceIndex = DeviceIndex;\r
1615 DeviceData->Private = Private;\r
1616\r
1617 Status = IdentifyAtaDevice (DeviceData);\r
1618 if (EFI_ERROR (Status)) {\r
1619 return Status;\r
1620 }\r
1621\r
1622 if (DeviceData->TrustComputing) {\r
1623 Private->TrustComputingDevices++;\r
1624 DeviceData->TrustComputingDeviceIndex = Private->TrustComputingDevices;\r
1625 }\r
1626 Private->ActiveDevices++;\r
1627 InsertTailList (&Private->DeviceList, &DeviceData->Link);\r
1628\r
1629 return EFI_SUCCESS;\r
1630}\r
1631\r
1632/**\r
1633 Initialize ATA host controller at AHCI mode.\r
1634\r
1635 The function is designed to initialize ATA host controller.\r
1636\r
1637 @param[in,out] Private A pointer to the PEI_AHCI_CONTROLLER_PRIVATE_DATA instance.\r
1638\r
1639 @retval EFI_SUCCESS The ATA AHCI controller is initialized successfully.\r
1640 @retval EFI_OUT_OF_RESOURCES Not enough resource to complete while initializing\r
1641 the controller.\r
1642 @retval Others A device error occurred while initializing the\r
1643 controller.\r
1644\r
1645**/\r
1646EFI_STATUS\r
1647AhciModeInitialization (\r
1648 IN OUT PEI_AHCI_CONTROLLER_PRIVATE_DATA *Private\r
1649 )\r
1650{\r
1651 EFI_STATUS Status;\r
1652 UINTN AhciBar;\r
1653 UINT32 Capability;\r
1654 UINT32 Value;\r
1655 UINT8 MaxPortNumber;\r
1656 UINT32 PortImplementBitMap;\r
1657 UINT32 PortInitializeBitMap;\r
1658 EFI_AHCI_REGISTERS *AhciRegisters;\r
1659 UINT8 PortIndex;\r
1660 UINT8 Port;\r
1661 DATA_64 Data64;\r
1662 UINT32 Data;\r
1663 UINT32 Offset;\r
1664 UINT32 PhyDetectDelay;\r
1665 UINTN DeviceIndex;\r
1666 ATA_IDENTIFY_DATA IdentifyData;\r
1667\r
1668 AhciBar = Private->MmioBase;\r
1669\r
1670 Status = AhciReset (AhciBar, AHCI_PEI_RESET_TIMEOUT);\r
1671 if (EFI_ERROR (Status)) {\r
1672 DEBUG ((DEBUG_ERROR, "%a: AHCI HBA reset failed with %r.\n", __FUNCTION__, Status));\r
1673 return EFI_DEVICE_ERROR;\r
1674 }\r
1675\r
1676 //\r
1677 // Collect AHCI controller information\r
1678 //\r
1679 Capability = AhciReadReg (AhciBar, AHCI_CAPABILITY_OFFSET);\r
1680\r
1681 //\r
1682 // Make sure that GHC.AE bit is set before accessing any AHCI registers.\r
1683 //\r
1684 Value = AhciReadReg (AhciBar, AHCI_GHC_OFFSET);\r
1685 if ((Value & AHCI_GHC_ENABLE) == 0) {\r
1686 AhciOrReg (AhciBar, AHCI_GHC_OFFSET, AHCI_GHC_ENABLE);\r
1687 }\r
1688\r
1689 Status = AhciCreateTransferDescriptor (Private);\r
1690 if (EFI_ERROR (Status)) {\r
1691 DEBUG ((\r
1692 DEBUG_ERROR,\r
1693 "%a: Transfer-related data allocation failed with %r.\n",\r
1694 __FUNCTION__, Status\r
1695 ));\r
1696 return EFI_OUT_OF_RESOURCES;\r
1697 }\r
1698\r
1699 //\r
1700 // Get the number of command slots per port supported by this HBA.\r
1701 //\r
1702 MaxPortNumber = (UINT8) ((Capability & 0x1F) + 1);\r
1703\r
1704 //\r
1705 // Get the bit map of those ports exposed by this HBA.\r
1706 // It indicates which ports that the HBA supports are available for software\r
1707 // to use.\r
1708 //\r
1709 PortImplementBitMap = AhciReadReg (AhciBar, AHCI_PI_OFFSET);\r
1710\r
1711 //\r
1712 // Get the number of ports that actually needed to be initialized.\r
1713 //\r
1714 MaxPortNumber = MIN (MaxPortNumber, (UINT8)(UINTN)(HighBitSet32(PortImplementBitMap) + 1));\r
1715 MaxPortNumber = MIN (MaxPortNumber, AhciGetNumberOfPortsFromMap (Private->PortBitMap));\r
1716\r
1717 PortInitializeBitMap = Private->PortBitMap;\r
1718 AhciRegisters = &Private->AhciRegisters;\r
1719 DeviceIndex = 0;\r
1720 //\r
1721 // Enumerate ATA ports\r
1722 //\r
1723 for (PortIndex = 1; PortIndex <= MaxPortNumber; PortIndex ++) {\r
1724 Status = AhciGetPortFromMap (PortInitializeBitMap, PortIndex, &Port);\r
1725 if ((PortImplementBitMap & (BIT0 << Port)) != 0) {\r
1726 //\r
1727 // Initialize FIS Base Address Register and Command List Base Address\r
1728 // Register for use.\r
1729 //\r
1730 Data64.Uint64 = (UINTN) (AhciRegisters->AhciRFis) +\r
1731 sizeof (EFI_AHCI_RECEIVED_FIS) * (PortIndex - 1);\r
1732 Offset = AHCI_PORT_START + Port * AHCI_PORT_REG_WIDTH + AHCI_PORT_FB;\r
1733 AhciWriteReg (AhciBar, Offset, Data64.Uint32.Lower32);\r
1734 Offset = AHCI_PORT_START + Port * AHCI_PORT_REG_WIDTH + AHCI_PORT_FBU;\r
1735 AhciWriteReg (AhciBar, Offset, Data64.Uint32.Upper32);\r
1736\r
1737 Data64.Uint64 = (UINTN) (AhciRegisters->AhciCmdList);\r
1738 Offset = AHCI_PORT_START + Port * AHCI_PORT_REG_WIDTH + AHCI_PORT_CLB;\r
1739 AhciWriteReg (AhciBar, Offset, Data64.Uint32.Lower32);\r
1740 Offset = AHCI_PORT_START + Port * AHCI_PORT_REG_WIDTH + AHCI_PORT_CLBU;\r
1741 AhciWriteReg (AhciBar, Offset, Data64.Uint32.Upper32);\r
1742\r
1743 Offset = AHCI_PORT_START + Port * AHCI_PORT_REG_WIDTH + AHCI_PORT_CMD;\r
1744 Data = AhciReadReg (AhciBar, Offset);\r
1745 if ((Data & AHCI_PORT_CMD_CPD) != 0) {\r
1746 AhciOrReg (AhciBar, Offset, AHCI_PORT_CMD_POD);\r
1747 }\r
1748\r
1749 if ((Capability & AHCI_CAP_SSS) != 0) {\r
1750 AhciOrReg (AhciBar, Offset, AHCI_PORT_CMD_SUD);\r
1751 }\r
1752\r
1753 //\r
1754 // Disable aggressive power management.\r
1755 //\r
1756 Offset = AHCI_PORT_START + Port * AHCI_PORT_REG_WIDTH + AHCI_PORT_SCTL;\r
1757 AhciOrReg (AhciBar, Offset, AHCI_PORT_SCTL_IPM_INIT);\r
1758 //\r
1759 // Disable the reporting of the corresponding interrupt to system software.\r
1760 //\r
1761 Offset = AHCI_PORT_START + Port * AHCI_PORT_REG_WIDTH + AHCI_PORT_IE;\r
1762 AhciAndReg (AhciBar, Offset, 0);\r
1763\r
1764 //\r
1765 // Enable FIS Receive DMA engine for the first D2H FIS.\r
1766 //\r
1767 Offset = AHCI_PORT_START + Port * AHCI_PORT_REG_WIDTH + AHCI_PORT_CMD;\r
1768 AhciOrReg (AhciBar, Offset, AHCI_PORT_CMD_FRE);\r
1769\r
1770 //\r
1771 // Wait no longer than 15 ms to wait the Phy to detect the presence of a device.\r
1772 //\r
1773 PhyDetectDelay = AHCI_BUS_PHY_DETECT_TIMEOUT;\r
1774 Offset = AHCI_PORT_START + Port * AHCI_PORT_REG_WIDTH + AHCI_PORT_SSTS;\r
1775 do {\r
1776 Data = AhciReadReg (AhciBar, Offset) & AHCI_PORT_SSTS_DET_MASK;\r
1777 if ((Data == AHCI_PORT_SSTS_DET_PCE) || (Data == AHCI_PORT_SSTS_DET)) {\r
1778 break;\r
1779 }\r
1780\r
1781 MicroSecondDelay (1000);\r
1782 PhyDetectDelay--;\r
1783 } while (PhyDetectDelay > 0);\r
1784\r
1785 if (PhyDetectDelay == 0) {\r
1786 //\r
1787 // No device detected at this port.\r
1788 // Clear PxCMD.SUD for those ports at which there are no device present.\r
1789 //\r
1790 Offset = AHCI_PORT_START + Port * AHCI_PORT_REG_WIDTH + AHCI_PORT_CMD;\r
1791 AhciAndReg (AhciBar, Offset, (UINT32) ~(AHCI_PORT_CMD_SUD));\r
1792 DEBUG ((DEBUG_ERROR, "%a: No device detected at Port %d.\n", __FUNCTION__, Port));\r
1793 continue;\r
1794 }\r
1795\r
1796 //\r
1797 // According to SATA1.0a spec section 5.2, we need to wait for PxTFD.BSY and PxTFD.DRQ\r
1798 // and PxTFD.ERR to be zero. The maximum wait time is 16s which is defined at ATA spec.\r
1799 //\r
1800 PhyDetectDelay = 16 * 1000;\r
1801 do {\r
1802 Offset = AHCI_PORT_START + Port * AHCI_PORT_REG_WIDTH + AHCI_PORT_SERR;\r
1803 if (AhciReadReg(AhciBar, Offset) != 0) {\r
1804 AhciWriteReg (AhciBar, Offset, AhciReadReg (AhciBar, Offset));\r
1805 }\r
1806 Offset = AHCI_PORT_START + Port * AHCI_PORT_REG_WIDTH + AHCI_PORT_TFD;\r
1807\r
1808 Data = AhciReadReg (AhciBar, Offset) & AHCI_PORT_TFD_MASK;\r
1809 if (Data == 0) {\r
1810 break;\r
1811 }\r
1812\r
1813 MicroSecondDelay (1000);\r
1814 PhyDetectDelay--;\r
1815 } while (PhyDetectDelay > 0);\r
1816\r
1817 if (PhyDetectDelay == 0) {\r
1818 DEBUG ((\r
1819 DEBUG_ERROR,\r
1820 "%a: Port %d device presence detected but phy not ready (TFD=0x%x).\n",\r
1821 __FUNCTION__, Port, Data\r
1822 ));\r
1823 continue;\r
1824 }\r
1825\r
1826 //\r
1827 // When the first D2H register FIS is received, the content of PxSIG register is updated.\r
1828 //\r
1829 Offset = AHCI_PORT_START + Port * AHCI_PORT_REG_WIDTH + AHCI_PORT_SIG;\r
1830 Status = AhciWaitMmioSet (\r
1831 AhciBar,\r
1832 Offset,\r
1833 0x0000FFFF,\r
1834 0x00000101,\r
1835 160000000\r
1836 );\r
1837 if (EFI_ERROR (Status)) {\r
1838 DEBUG ((\r
1839 DEBUG_ERROR,\r
1840 "%a: Error occured when waiting for the first D2H register FIS - %r\n",\r
1841 __FUNCTION__, Status\r
1842 ));\r
1843 continue;\r
1844 }\r
1845\r
1846 Data = AhciReadReg (AhciBar, Offset);\r
1847 if ((Data & AHCI_ATAPI_SIG_MASK) == AHCI_ATA_DEVICE_SIG) {\r
1848 Status = AhciIdentify (Private, Port, 0, PortIndex - 1, &IdentifyData);\r
1849 if (EFI_ERROR (Status)) {\r
1850 DEBUG ((DEBUG_ERROR, "%a: AhciIdentify() failed with %r\n", __FUNCTION__, Status));\r
1851 continue;\r
1852 }\r
1853 DEBUG ((DEBUG_INFO, "%a: ATA hard disk found on Port %d.\n", __FUNCTION__, Port));\r
1854 } else {\r
1855 continue;\r
1856 }\r
1857\r
1858 //\r
1859 // Found an ATA hard disk device, add it into the device list.\r
1860 //\r
1861 DeviceIndex++;\r
1862 CreateNewDevice (\r
1863 Private,\r
1864 DeviceIndex,\r
1865 Port,\r
1866 0xFFFF,\r
1867 PortIndex - 1,\r
1868 &IdentifyData\r
1869 );\r
1870 }\r
1871 }\r
1872\r
1873 return EFI_SUCCESS;\r
1874}\r
1875\r
1876/**\r
1877 Trust transfer data from/to ATA device.\r
1878\r
1879 This function performs one ATA pass through transaction to do a trust transfer\r
1880 from/to ATA device. It chooses the appropriate ATA command and protocol to invoke\r
1881 PassThru interface of ATA pass through.\r
1882\r
1883 @param[in] DeviceData Pointer to PEI_AHCI_ATA_DEVICE_DATA structure.\r
1884 @param[in,out] Buffer The pointer to the current transaction buffer.\r
1885 @param[in] SecurityProtocolId\r
1886 The value of the "Security Protocol" parameter\r
1887 of the security protocol command to be sent.\r
1888 @param[in] SecurityProtocolSpecificData\r
1889 The value of the "Security Protocol Specific"\r
1890 parameter of the security protocol command to\r
1891 be sent.\r
1892 @param[in] TransferLength The block number or sector count of the transfer.\r
1893 @param[in] IsTrustSend Indicates whether it is a trust send operation\r
1894 or not.\r
1895 @param[in] Timeout The timeout, in 100ns units, to use for the execution\r
1896 of the security protocol command. A Timeout value\r
1897 of 0 means that this function will wait indefinitely\r
1898 for the security protocol command to execute. If\r
1899 Timeout is greater than zero, then this function\r
1900 will return EFI_TIMEOUT if the time required to\r
1901 execute the receive data command is greater than\r
1902 Timeout.\r
1903 @param[out] TransferLengthOut\r
1904 A pointer to a buffer to store the size in bytes\r
1905 of the data written to the buffer. Ignore it when\r
1906 IsTrustSend is TRUE.\r
1907\r
1908 @retval EFI_SUCCESS The data transfer is complete successfully.\r
1909 @return others Some error occurs when transferring data.\r
1910\r
1911**/\r
1912EFI_STATUS\r
1913TrustTransferAtaDevice (\r
1914 IN PEI_AHCI_ATA_DEVICE_DATA *DeviceData,\r
1915 IN OUT VOID *Buffer,\r
1916 IN UINT8 SecurityProtocolId,\r
1917 IN UINT16 SecurityProtocolSpecificData,\r
1918 IN UINTN TransferLength,\r
1919 IN BOOLEAN IsTrustSend,\r
1920 IN UINT64 Timeout,\r
1921 OUT UINTN *TransferLengthOut\r
1922 )\r
1923{\r
1924 PEI_AHCI_CONTROLLER_PRIVATE_DATA *Private;\r
1925 EDKII_PEI_ATA_PASS_THRU_PPI *AtaPassThru;\r
1926 EFI_ATA_COMMAND_BLOCK Acb;\r
1927 EFI_ATA_PASS_THRU_COMMAND_PACKET Packet;\r
1928 EFI_STATUS Status;\r
1929 VOID *NewBuffer;\r
1930\r
1931 Private = DeviceData->Private;\r
1932 AtaPassThru = &Private->AtaPassThruPpi;\r
1933\r
1934 //\r
1935 // Ensure IsTrustSend are valid boolean values\r
1936 //\r
1937 ASSERT ((UINTN) IsTrustSend < 2);\r
1938 if ((UINTN) IsTrustSend >= 2) {\r
1939 return EFI_INVALID_PARAMETER;\r
1940 }\r
1941\r
1942 //\r
1943 // Prepare for ATA command block.\r
1944 //\r
1945 ZeroMem (&Acb, sizeof (EFI_ATA_COMMAND_BLOCK));\r
1946 if (TransferLength == 0) {\r
1947 Acb.AtaCommand = ATA_CMD_TRUST_NON_DATA;\r
1948 } else {\r
1949 Acb.AtaCommand = mAtaTrustCommands[IsTrustSend];\r
1950 }\r
1951 Acb.AtaFeatures = SecurityProtocolId;\r
1952 Acb.AtaSectorCount = (UINT8) (TransferLength / 512);\r
1953 Acb.AtaSectorNumber = (UINT8) ((TransferLength / 512) >> 8);\r
1954 //\r
1955 // NOTE: ATA Spec has no explicitly definition for Security Protocol Specific layout.\r
1956 // Here use big endian for Cylinder register.\r
1957 //\r
1958 Acb.AtaCylinderHigh = (UINT8) SecurityProtocolSpecificData;\r
1959 Acb.AtaCylinderLow = (UINT8) (SecurityProtocolSpecificData >> 8);\r
1960 Acb.AtaDeviceHead = (UINT8) (BIT7 | BIT6 | BIT5 |\r
1961 (DeviceData->PortMultiplier == 0xFFFF ?\r
1962 0 : (DeviceData->PortMultiplier << 4)));\r
1963\r
1964 //\r
1965 // Prepare for ATA pass through packet.\r
1966 //\r
1967 ZeroMem (&Packet, sizeof (EFI_ATA_PASS_THRU_COMMAND_PACKET));\r
1968 if (TransferLength == 0) {\r
1969 Packet.InTransferLength = 0;\r
1970 Packet.OutTransferLength = 0;\r
1971 Packet.Protocol = EFI_ATA_PASS_THRU_PROTOCOL_ATA_NON_DATA;\r
1972 } else if (IsTrustSend) {\r
1973 //\r
1974 // Check the alignment of the incoming buffer prior to invoking underlying\r
1975 // ATA PassThru PPI.\r
1976 //\r
1977 if ((AtaPassThru->Mode->IoAlign > 1) &&\r
1978 !IS_ALIGNED (Buffer, AtaPassThru->Mode->IoAlign)) {\r
1979 NewBuffer = AllocateAlignedPages (\r
1980 EFI_SIZE_TO_PAGES (TransferLength),\r
1981 AtaPassThru->Mode->IoAlign\r
1982 );\r
1983 if (NewBuffer == NULL) {\r
1984 return EFI_OUT_OF_RESOURCES;\r
1985 }\r
1986\r
1987 CopyMem (NewBuffer, Buffer, TransferLength);\r
1988 Buffer = NewBuffer;\r
1989 }\r
1990 Packet.OutDataBuffer = Buffer;\r
1991 Packet.OutTransferLength = (UINT32) TransferLength;\r
1992 Packet.Protocol = mAtaPassThruCmdProtocols[IsTrustSend];\r
1993 } else {\r
1994 Packet.InDataBuffer = Buffer;\r
1995 Packet.InTransferLength = (UINT32) TransferLength;\r
1996 Packet.Protocol = mAtaPassThruCmdProtocols[IsTrustSend];\r
1997 }\r
1998 Packet.Asb = NULL;\r
1999 Packet.Acb = &Acb;\r
2000 Packet.Timeout = Timeout;\r
2001 Packet.Length = EFI_ATA_PASS_THRU_LENGTH_BYTES;\r
2002\r
2003 Status = AtaPassThru->PassThru (\r
2004 AtaPassThru,\r
2005 DeviceData->Port,\r
2006 DeviceData->PortMultiplier,\r
2007 &Packet\r
2008 );\r
2009 if (TransferLengthOut != NULL) {\r
2010 if (!IsTrustSend) {\r
2011 *TransferLengthOut = Packet.InTransferLength;\r
2012 }\r
2013 }\r
2014 return Status;\r
2015}\r