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