]> git.proxmox.com Git - mirror_edk2.git/blobdiff - MdeModulePkg/Bus/Ata/AhciPei/AhciPeiPassThru.c
MdeModulePkg/AhciPei: Add AHCI mode ATA device support in PEI
[mirror_edk2.git] / MdeModulePkg / Bus / Ata / AhciPei / AhciPeiPassThru.c
diff --git a/MdeModulePkg/Bus/Ata/AhciPei/AhciPeiPassThru.c b/MdeModulePkg/Bus/Ata/AhciPei/AhciPeiPassThru.c
new file mode 100644 (file)
index 0000000..394d6d9
--- /dev/null
@@ -0,0 +1,521 @@
+/** @file\r
+  The AhciPei driver is used to manage ATA hard disk device working under AHCI\r
+  mode at PEI phase.\r
+\r
+  Copyright (c) 2019, Intel Corporation. All rights reserved.<BR>\r
+\r
+  This program and the accompanying materials\r
+  are licensed and made available under the terms and conditions\r
+  of the BSD License which accompanies this distribution.  The\r
+  full text of the license may be found at\r
+  http://opensource.org/licenses/bsd-license.php\r
+\r
+  THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
+  WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
+\r
+**/\r
+\r
+#include "AhciPei.h"\r
+\r
+/**\r
+  Traverse the attached ATA devices list to find out the device with given Port\r
+  and PortMultiplierPort.\r
+\r
+  @param[in] Private               A pointer to the PEI_AHCI_CONTROLLER_PRIVATE_DATA\r
+                                   instance.\r
+  @param[in] Port                  The port number of the ATA device.\r
+  @param[in] PortMultiplierPort    The port multiplier port number of the ATA device.\r
+\r
+  @retval    The pointer to the PEI_AHCI_ATA_DEVICE_DATA structure of the device\r
+             info to access.\r
+\r
+**/\r
+PEI_AHCI_ATA_DEVICE_DATA *\r
+SearchDeviceByPort (\r
+  IN PEI_AHCI_CONTROLLER_PRIVATE_DATA    *Private,\r
+  IN UINT16                              Port,\r
+  IN UINT16                              PortMultiplierPort\r
+  )\r
+{\r
+  PEI_AHCI_ATA_DEVICE_DATA    *DeviceData;\r
+  LIST_ENTRY                  *Node;\r
+\r
+  Node = GetFirstNode (&Private->DeviceList);\r
+  while (!IsNull (&Private->DeviceList, Node)) {\r
+    DeviceData = AHCI_PEI_ATA_DEVICE_INFO_FROM_THIS (Node);\r
+\r
+    if ((DeviceData->Port == Port) &&\r
+        (DeviceData->PortMultiplier == PortMultiplierPort)) {\r
+      return DeviceData;\r
+    }\r
+\r
+    Node = GetNextNode (&Private->DeviceList, Node);\r
+  }\r
+\r
+  return NULL;\r
+}\r
+\r
+/**\r
+  Sends an ATA command to an ATA device that is attached to the ATA controller.\r
+\r
+  @param[in]     Private               Pointer to the PEI_AHCI_CONTROLLER_PRIVATE_DATA.\r
+  @param[in]     Port                  The port number of the ATA device.\r
+  @param[in]     PortMultiplierPort    The port multiplier port number of the ATA\r
+                                       device.\r
+  @param[in]     FisIndex              The index of the FIS.\r
+  @param[in,out] Packet                A pointer to the ATA command to send to\r
+                                       the ATA device specified by Port and\r
+                                       PortMultiplierPort.\r
+\r
+  @retval EFI_SUCCESS              The ATA command was sent by the host. For\r
+                                   bi-directional commands, InTransferLength bytes\r
+                                   were transferred from InDataBuffer. For write\r
+                                   and bi-directional commands, OutTransferLength\r
+                                   bytes were transferred by OutDataBuffer.\r
+  @retval EFI_BAD_BUFFER_SIZE      The ATA command was not executed. The number\r
+                                   of bytes that could be transferred is returned\r
+                                   in InTransferLength. For write and bi-directional\r
+                                   commands, OutTransferLength bytes were transferred\r
+                                   by OutDataBuffer.\r
+  @retval EFI_NOT_READY            The ATA command could not be sent because there\r
+                                   are too many ATA commands already queued. The\r
+                                   caller may retry again later.\r
+  @retval EFI_DEVICE_ERROR         A device error occurred while attempting to\r
+                                   send the ATA command.\r
+  @retval EFI_INVALID_PARAMETER    Port, PortMultiplierPort, or the contents of\r
+                                   Acb are invalid. The ATA command was not sent,\r
+                                   so no additional status information is available.\r
+\r
+**/\r
+EFI_STATUS\r
+AhciPassThruExecute (\r
+  IN     PEI_AHCI_CONTROLLER_PRIVATE_DATA    *Private,\r
+  IN     UINT16                              Port,\r
+  IN     UINT16                              PortMultiplierPort,\r
+  IN     UINT8                               FisIndex,\r
+  IN OUT EFI_ATA_PASS_THRU_COMMAND_PACKET    *Packet\r
+  )\r
+{\r
+  EFI_STATUS    Status;\r
+\r
+  switch (Packet->Protocol) {\r
+    case EFI_ATA_PASS_THRU_PROTOCOL_ATA_NON_DATA:\r
+      Status = AhciNonDataTransfer (\r
+                 Private,\r
+                 (UINT8) Port,\r
+                 (UINT8) PortMultiplierPort,\r
+                 FisIndex,\r
+                 Packet->Acb,\r
+                 Packet->Asb,\r
+                 Packet->Timeout\r
+                 );\r
+      break;\r
+    case EFI_ATA_PASS_THRU_PROTOCOL_PIO_DATA_IN:\r
+      Status = AhciPioTransfer (\r
+                 Private,\r
+                 (UINT8) Port,\r
+                 (UINT8) PortMultiplierPort,\r
+                 FisIndex,\r
+                 TRUE,\r
+                 Packet->Acb,\r
+                 Packet->Asb,\r
+                 Packet->InDataBuffer,\r
+                 Packet->InTransferLength,\r
+                 Packet->Timeout\r
+                 );\r
+      break;\r
+    case EFI_ATA_PASS_THRU_PROTOCOL_PIO_DATA_OUT:\r
+      Status = AhciPioTransfer (\r
+                 Private,\r
+                 (UINT8) Port,\r
+                 (UINT8) PortMultiplierPort,\r
+                 FisIndex,\r
+                 FALSE,\r
+                 Packet->Acb,\r
+                 Packet->Asb,\r
+                 Packet->OutDataBuffer,\r
+                 Packet->OutTransferLength,\r
+                 Packet->Timeout\r
+                 );\r
+      break;\r
+    default:\r
+      return EFI_UNSUPPORTED;\r
+  }\r
+\r
+  return Status;\r
+}\r
+\r
+/**\r
+  Sends an ATA command to an ATA device that is attached to the ATA controller.\r
+\r
+  @param[in]     This                  The PPI instance pointer.\r
+  @param[in]     Port                  The port number of the ATA device to send\r
+                                       the command.\r
+  @param[in]     PortMultiplierPort    The port multiplier port number of the ATA\r
+                                       device to send the command.\r
+                                       If there is no port multiplier, then specify\r
+                                       0xFFFF.\r
+  @param[in,out] Packet                A pointer to the ATA command to send to\r
+                                       the ATA device specified by Port and\r
+                                       PortMultiplierPort.\r
+\r
+  @retval EFI_SUCCESS              The ATA command was sent by the host. For\r
+                                   bi-directional commands, InTransferLength bytes\r
+                                   were transferred from InDataBuffer. For write\r
+                                   and bi-directional commands, OutTransferLength\r
+                                   bytes were transferred by OutDataBuffer.\r
+  @retval EFI_NOT_FOUND            The specified ATA device is not found.\r
+  @retval EFI_INVALID_PARAMETER    The contents of Acb are invalid. The ATA command\r
+                                   was not sent, so no additional status information\r
+                                   is available.\r
+  @retval EFI_BAD_BUFFER_SIZE      The ATA command was not executed. The number\r
+                                   of bytes that could be transferred is returned\r
+                                   in InTransferLength. For write and bi-directional\r
+                                   commands, OutTransferLength bytes were transferred\r
+                                   by OutDataBuffer.\r
+  @retval EFI_NOT_READY            The ATA command could not be sent because there\r
+                                   are too many ATA commands already queued. The\r
+                                   caller may retry again later.\r
+  @retval EFI_DEVICE_ERROR         A device error occurred while attempting to\r
+                                   send the ATA command.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+AhciAtaPassThruPassThru (\r
+  IN     EDKII_PEI_ATA_PASS_THRU_PPI         *This,\r
+  IN     UINT16                              Port,\r
+  IN     UINT16                              PortMultiplierPort,\r
+  IN OUT EFI_ATA_PASS_THRU_COMMAND_PACKET    *Packet\r
+  )\r
+{\r
+  UINT32                              IoAlign;\r
+  PEI_AHCI_CONTROLLER_PRIVATE_DATA    *Private;\r
+  PEI_AHCI_ATA_DEVICE_DATA            *DeviceData;\r
+  UINT32                              MaxSectorCount;\r
+  UINT32                              BlockSize;\r
+\r
+  if (This == NULL || Packet == NULL) {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  IoAlign = This->Mode->IoAlign;\r
+  if ((IoAlign > 1) && !IS_ALIGNED (Packet->InDataBuffer, IoAlign)) {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  if ((IoAlign > 1) && !IS_ALIGNED (Packet->OutDataBuffer, IoAlign)) {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  if ((IoAlign > 1) && !IS_ALIGNED (Packet->Asb, IoAlign)) {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  Private = GET_AHCI_PEIM_HC_PRIVATE_DATA_FROM_THIS_PASS_THRU (This);\r
+  DeviceData = SearchDeviceByPort (Private, Port, PortMultiplierPort);\r
+  if (DeviceData == NULL) {\r
+    return EFI_NOT_FOUND;\r
+  }\r
+\r
+  MaxSectorCount = mMaxTransferBlockNumber[DeviceData->Lba48Bit];\r
+  BlockSize      = DeviceData->Media.BlockSize;\r
+\r
+  //\r
+  // Convert the transfer length from sector count to byte.\r
+  //\r
+  if (((Packet->Length & EFI_ATA_PASS_THRU_LENGTH_BYTES) == 0) &&\r
+       (Packet->InTransferLength != 0)) {\r
+    Packet->InTransferLength = Packet->InTransferLength * BlockSize;\r
+  }\r
+\r
+  //\r
+  // Convert the transfer length from sector count to byte.\r
+  //\r
+  if (((Packet->Length & EFI_ATA_PASS_THRU_LENGTH_BYTES) == 0) &&\r
+       (Packet->OutTransferLength != 0)) {\r
+    Packet->OutTransferLength = Packet->OutTransferLength * BlockSize;\r
+  }\r
+\r
+  //\r
+  // If the data buffer described by InDataBuffer/OutDataBuffer and\r
+  // InTransferLength/OutTransferLength is too big to be transferred in a single\r
+  // command, then no data is transferred and EFI_BAD_BUFFER_SIZE is returned.\r
+  //\r
+  if (((Packet->InTransferLength != 0) && (Packet->InTransferLength > MaxSectorCount * BlockSize)) ||\r
+      ((Packet->OutTransferLength != 0) && (Packet->OutTransferLength > MaxSectorCount * BlockSize))) {\r
+    return EFI_BAD_BUFFER_SIZE;\r
+  }\r
+\r
+  return AhciPassThruExecute (\r
+           Private,\r
+           DeviceData->Port,\r
+           DeviceData->PortMultiplier,\r
+           DeviceData->FisIndex,\r
+           Packet\r
+           );\r
+}\r
+\r
+/**\r
+  Used to retrieve the list of legal port numbers for ATA devices on an ATA controller.\r
+  These can either be the list of ports where ATA devices are actually present or the\r
+  list of legal port numbers for the ATA controller. Regardless, the caller of this\r
+  function must probe the port number returned to see if an ATA device is actually\r
+  present at that location on the ATA controller.\r
+\r
+  The GetNextPort() function retrieves the port number on an ATA controller. If on\r
+  input Port is 0xFFFF, then the port number of the first port on the ATA controller\r
+  is returned in Port and EFI_SUCCESS is returned.\r
+\r
+  If Port is a port number that was returned on a previous call to GetNextPort(),\r
+  then the port number of the next port on the ATA controller is returned in Port,\r
+  and EFI_SUCCESS is returned. If Port is not 0xFFFF and Port was not returned on\r
+  a previous call to GetNextPort(), then EFI_INVALID_PARAMETER is returned.\r
+\r
+  If Port is the port number of the last port on the ATA controller, then EFI_NOT_FOUND\r
+  is returned.\r
+\r
+  @param[in]     This    The PPI instance pointer.\r
+  @param[in,out] Port    On input, a pointer to the port number on the ATA controller.\r
+                         On output, a pointer to the next port number on the ATA\r
+                         controller. An input value of 0xFFFF retrieves the first\r
+                         port number on the ATA controller.\r
+\r
+  @retval EFI_SUCCESS              The next port number on the ATA controller was\r
+                                   returned in Port.\r
+  @retval EFI_NOT_FOUND            There are no more ports on this ATA controller.\r
+  @retval EFI_INVALID_PARAMETER    Port is not 0xFFFF and Port was not returned\r
+                                   on a previous call to GetNextPort().\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+AhciAtaPassThruGetNextPort (\r
+  IN     EDKII_PEI_ATA_PASS_THRU_PPI    *This,\r
+  IN OUT UINT16                         *Port\r
+  )\r
+{\r
+  PEI_AHCI_CONTROLLER_PRIVATE_DATA    *Private;\r
+  PEI_AHCI_ATA_DEVICE_DATA            *DeviceData;\r
+  LIST_ENTRY                          *Node;\r
+\r
+  if (This == NULL || Port == NULL) {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  Private = GET_AHCI_PEIM_HC_PRIVATE_DATA_FROM_THIS_PASS_THRU (This);\r
+\r
+  if (*Port == 0xFFFF) {\r
+    //\r
+    // If the Port is all 0xFF's, start to traverse the device list from the\r
+    // beginning.\r
+    //\r
+    Node = GetFirstNode (&Private->DeviceList);\r
+    if (!IsNull (&Private->DeviceList, Node)) {\r
+      DeviceData = AHCI_PEI_ATA_DEVICE_INFO_FROM_THIS (Node);\r
+\r
+      *Port = DeviceData->Port;\r
+      goto Exit;\r
+    }\r
+\r
+    return EFI_NOT_FOUND;\r
+  } else if (*Port == Private->PreviousPort) {\r
+    Node = GetFirstNode (&Private->DeviceList);\r
+\r
+    while (!IsNull (&Private->DeviceList, Node)) {\r
+      DeviceData = AHCI_PEI_ATA_DEVICE_INFO_FROM_THIS (Node);\r
+\r
+      if (DeviceData->Port > *Port){\r
+        *Port = DeviceData->Port;\r
+        goto Exit;\r
+      }\r
+\r
+      Node = GetNextNode (&Private->DeviceList, Node);\r
+    }\r
+\r
+    return EFI_NOT_FOUND;\r
+  } else {\r
+    //\r
+    // Port is not equal to all 0xFF's and not equal to previous return value.\r
+    //\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+Exit:\r
+  //\r
+  // Update the PreviousPort.\r
+  //\r
+  Private->PreviousPort = *Port;\r
+\r
+  return EFI_SUCCESS;\r
+}\r
+\r
+/**\r
+  Used to retrieve the list of legal port multiplier port numbers for ATA devices\r
+  on a port of an ATA controller. These can either be the list of port multiplier\r
+  ports where ATA devices are actually present on port or the list of legal port\r
+  multiplier ports on that port. Regardless, the caller of this function must probe\r
+  the port number and port multiplier port number returned to see if an ATA device\r
+  is actually present.\r
+\r
+  The GetNextDevice() function retrieves the port multiplier port number of an ATA\r
+  device present on a port of an ATA controller.\r
+\r
+  If PortMultiplierPort points to a port multiplier port number value that was\r
+  returned on a previous call to GetNextDevice(), then the port multiplier port\r
+  number of the next ATA device on the port of the ATA controller is returned in\r
+  PortMultiplierPort, and EFI_SUCCESS is returned.\r
+\r
+  If PortMultiplierPort points to 0xFFFF, then the port multiplier port number\r
+  of the first ATA device on port of the ATA controller is returned in PortMultiplierPort\r
+  and EFI_SUCCESS is returned.\r
+\r
+  If PortMultiplierPort is not 0xFFFF and the value pointed to by PortMultiplierPort\r
+  was not returned on a previous call to GetNextDevice(), then EFI_INVALID_PARAMETER\r
+  is returned.\r
+\r
+  If PortMultiplierPort is the port multiplier port number of the last ATA device\r
+  on the port of the ATA controller, then EFI_NOT_FOUND is returned.\r
+\r
+  @param[in]     This                  The PPI instance pointer.\r
+  @param[in]     Port                  The port number present on the ATA controller.\r
+  @param[in,out] PortMultiplierPort    On input, a pointer to the port multiplier\r
+                                       port number of an ATA device present on the\r
+                                       ATA controller. If on input a PortMultiplierPort\r
+                                       of 0xFFFF is specified, then the port multiplier\r
+                                       port number of the first ATA device is returned.\r
+                                       On output, a pointer to the port multiplier port\r
+                                       number of the next ATA device present on an ATA\r
+                                       controller.\r
+\r
+  @retval EFI_SUCCESS              The port multiplier port number of the next ATA\r
+                                   device on the port of the ATA controller was\r
+                                   returned in PortMultiplierPort.\r
+  @retval EFI_NOT_FOUND            There are no more ATA devices on this port of\r
+                                   the ATA controller.\r
+  @retval EFI_INVALID_PARAMETER    PortMultiplierPort is not 0xFFFF, and PortMultiplierPort\r
+                                   was not returned on a previous call to GetNextDevice().\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+AhciAtaPassThruGetNextDevice (\r
+  IN     EDKII_PEI_ATA_PASS_THRU_PPI    *This,\r
+  IN     UINT16                         Port,\r
+  IN OUT UINT16                         *PortMultiplierPort\r
+  )\r
+{\r
+  PEI_AHCI_CONTROLLER_PRIVATE_DATA    *Private;\r
+  PEI_AHCI_ATA_DEVICE_DATA            *DeviceData;\r
+  LIST_ENTRY                          *Node;\r
+\r
+  if (This == NULL || PortMultiplierPort == NULL) {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  Private = GET_AHCI_PEIM_HC_PRIVATE_DATA_FROM_THIS_PASS_THRU (This);\r
+\r
+  if (Private->PreviousPortMultiplier == 0xFFFF) {\r
+    //\r
+    // If a device is directly attached on a port, previous call to this\r
+    // function will return the value 0xFFFF for PortMultiplierPort. In\r
+    // this case, there should be no more device on the port multiplier.\r
+    //\r
+    Private->PreviousPortMultiplier = 0;\r
+    return EFI_NOT_FOUND;\r
+  }\r
+\r
+  if (*PortMultiplierPort == Private->PreviousPortMultiplier) {\r
+    Node = GetFirstNode (&Private->DeviceList);\r
+\r
+    while (!IsNull (&Private->DeviceList, Node)) {\r
+      DeviceData = AHCI_PEI_ATA_DEVICE_INFO_FROM_THIS (Node);\r
+\r
+      if ((DeviceData->Port == Port) &&\r
+          (DeviceData->PortMultiplier > *PortMultiplierPort)){\r
+        *PortMultiplierPort = DeviceData->PortMultiplier;\r
+        goto Exit;\r
+      }\r
+\r
+      Node = GetNextNode (&Private->DeviceList, Node);\r
+    }\r
+\r
+    return EFI_NOT_FOUND;\r
+  } else if (*PortMultiplierPort == 0xFFFF) {\r
+    //\r
+    // If the PortMultiplierPort is all 0xFF's, start to traverse the device list\r
+    // from the beginning.\r
+    //\r
+    Node = GetFirstNode (&Private->DeviceList);\r
+\r
+    while (!IsNull (&Private->DeviceList, Node)) {\r
+      DeviceData = AHCI_PEI_ATA_DEVICE_INFO_FROM_THIS (Node);\r
+\r
+      if (DeviceData->Port == Port){\r
+        *PortMultiplierPort = DeviceData->PortMultiplier;\r
+        goto Exit;\r
+      }\r
+\r
+      Node = GetNextNode (&Private->DeviceList, Node);\r
+    }\r
+\r
+    return EFI_NOT_FOUND;\r
+  } else {\r
+    //\r
+    // PortMultiplierPort is not equal to all 0xFF's and not equal to previous\r
+    // return value.\r
+    //\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+Exit:\r
+  //\r
+  // Update the PreviousPortMultiplier.\r
+  //\r
+  Private->PreviousPortMultiplier = *PortMultiplierPort;\r
+\r
+  return EFI_SUCCESS;\r
+}\r
+\r
+/**\r
+  Gets the device path information of the underlying ATA host controller.\r
+\r
+  @param[in]  This                The PPI instance pointer.\r
+  @param[out] DevicePathLength    The length of the device path in bytes specified\r
+                                  by DevicePath.\r
+  @param[out] DevicePath          The device path of the underlying ATA host controller.\r
+                                  This field re-uses EFI Device Path Protocol as\r
+                                  defined by Section 10.2 EFI Device Path Protocol\r
+                                  of UEFI 2.7 Specification.\r
+\r
+  @retval EFI_SUCCESS              The device path of the ATA host controller has\r
+                                   been successfully returned.\r
+  @retval EFI_INVALID_PARAMETER    DevicePathLength or DevicePath is NULL.\r
+  @retval EFI_OUT_OF_RESOURCES     Not enough resource to return the device path.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+AhciAtaPassThruGetDevicePath (\r
+  IN  EDKII_PEI_ATA_PASS_THRU_PPI    *This,\r
+  OUT UINTN                          *DevicePathLength,\r
+  OUT EFI_DEVICE_PATH_PROTOCOL       **DevicePath\r
+  )\r
+{\r
+  PEI_AHCI_CONTROLLER_PRIVATE_DATA    *Private;\r
+\r
+  if (This == NULL || DevicePathLength == NULL || DevicePath == NULL) {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  Private = GET_AHCI_PEIM_HC_PRIVATE_DATA_FROM_THIS_PASS_THRU (This);\r
+\r
+  *DevicePathLength = Private->DevicePathLength;\r
+  *DevicePath       = AllocateCopyPool (Private->DevicePathLength, Private->DevicePath);\r
+  if (*DevicePath == NULL) {\r
+    *DevicePathLength = 0;\r
+    return EFI_OUT_OF_RESOURCES;\r
+  }\r
+\r
+  return EFI_SUCCESS;\r
+}\r