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