]> git.proxmox.com Git - mirror_edk2.git/blobdiff - MdeModulePkg/Bus/Ata/AhciPei/DevicePath.c
MdeModulePkg/AhciPei: Add AHCI mode ATA device support in PEI
[mirror_edk2.git] / MdeModulePkg / Bus / Ata / AhciPei / DevicePath.c
diff --git a/MdeModulePkg/Bus/Ata/AhciPei/DevicePath.c b/MdeModulePkg/Bus/Ata/AhciPei/DevicePath.c
new file mode 100644 (file)
index 0000000..02cf8d1
--- /dev/null
@@ -0,0 +1,284 @@
+/** @file\r
+  The device path help function.\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
+// Template for a SATA Device Path node\r
+//\r
+SATA_DEVICE_PATH  mAhciSataDevicePathNodeTemplate = {\r
+  {        // Header\r
+    MESSAGING_DEVICE_PATH,\r
+    MSG_SATA_DP,\r
+    {\r
+      (UINT8) (sizeof (SATA_DEVICE_PATH)),\r
+      (UINT8) ((sizeof (SATA_DEVICE_PATH)) >> 8)\r
+    }\r
+  },\r
+  0x0,     // HBAPortNumber\r
+  0xFFFF,  // PortMultiplierPortNumber\r
+  0x0      // Lun\r
+};\r
+\r
+//\r
+// Template for an End of entire Device Path node\r
+//\r
+EFI_DEVICE_PATH_PROTOCOL  mAhciEndDevicePathNodeTemplate = {\r
+  END_DEVICE_PATH_TYPE,\r
+  END_ENTIRE_DEVICE_PATH_SUBTYPE,\r
+  {\r
+    (UINT8) (sizeof (EFI_DEVICE_PATH_PROTOCOL)),\r
+    (UINT8) ((sizeof (EFI_DEVICE_PATH_PROTOCOL)) >> 8)\r
+  }\r
+};\r
+\r
+/**\r
+  Returns the 16-bit Length field of a device path node.\r
+\r
+  Returns the 16-bit Length field of the device path node specified by Node.\r
+  Node is not required to be aligned on a 16-bit boundary, so it is recommended\r
+  that a function such as ReadUnaligned16() be used to extract the contents of\r
+  the Length field.\r
+\r
+  If Node is NULL, then ASSERT().\r
+\r
+  @param  Node      A pointer to a device path node data structure.\r
+\r
+  @return The 16-bit Length field of the device path node specified by Node.\r
+\r
+**/\r
+UINTN\r
+DevicePathNodeLength (\r
+  IN CONST VOID  *Node\r
+  )\r
+{\r
+  ASSERT (Node != NULL);\r
+  return ReadUnaligned16 ((UINT16 *)&((EFI_DEVICE_PATH_PROTOCOL *)(Node))->Length[0]);\r
+}\r
+\r
+/**\r
+  Returns a pointer to the next node in a device path.\r
+\r
+  If Node is NULL, then ASSERT().\r
+\r
+  @param  Node    A pointer to a device path node data structure.\r
+\r
+  @return a pointer to the device path node that follows the device path node\r
+  specified by Node.\r
+\r
+**/\r
+EFI_DEVICE_PATH_PROTOCOL *\r
+NextDevicePathNode (\r
+  IN CONST VOID  *Node\r
+  )\r
+{\r
+  ASSERT (Node != NULL);\r
+  return (EFI_DEVICE_PATH_PROTOCOL *)((UINT8 *)(Node) + DevicePathNodeLength(Node));\r
+}\r
+\r
+/**\r
+  Get the size of the current device path instance.\r
+\r
+  @param[in]  DevicePath             A pointer to the EFI_DEVICE_PATH_PROTOCOL\r
+                                     structure.\r
+  @param[out] InstanceSize           The size of the current device path instance.\r
+  @param[out] EntireDevicePathEnd    Indicate whether the instance is the last\r
+                                     one in the device path strucure.\r
+\r
+  @retval EFI_SUCCESS    The size of the current device path instance is fetched.\r
+  @retval Others         Fails to get the size of the current device path instance.\r
+\r
+**/\r
+EFI_STATUS\r
+GetDevicePathInstanceSize (\r
+  IN  EFI_DEVICE_PATH_PROTOCOL    *DevicePath,\r
+  OUT UINTN                       *InstanceSize,\r
+  OUT BOOLEAN                     *EntireDevicePathEnd\r
+  )\r
+{\r
+  EFI_DEVICE_PATH_PROTOCOL    *Walker;\r
+\r
+  if (DevicePath == NULL || InstanceSize == NULL || EntireDevicePathEnd == NULL) {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  //\r
+  // Find the end of the device path instance\r
+  //\r
+  Walker = DevicePath;\r
+  while (Walker->Type != END_DEVICE_PATH_TYPE) {\r
+    Walker = NextDevicePathNode (Walker);\r
+  }\r
+\r
+  //\r
+  // Check if 'Walker' points to the end of an entire device path\r
+  //\r
+  if (Walker->SubType == END_ENTIRE_DEVICE_PATH_SUBTYPE) {\r
+    *EntireDevicePathEnd = TRUE;\r
+  } else if (Walker->SubType == END_INSTANCE_DEVICE_PATH_SUBTYPE) {\r
+    *EntireDevicePathEnd = FALSE;\r
+  } else {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  //\r
+  // Compute the size of the device path instance\r
+  //\r
+  *InstanceSize = ((UINTN) Walker - (UINTN) (DevicePath)) + sizeof (EFI_DEVICE_PATH_PROTOCOL);\r
+\r
+  return EFI_SUCCESS;\r
+}\r
+\r
+/**\r
+  Check the validity of the device path of a ATA AHCI host controller.\r
+\r
+  @param[in] DevicePath          A pointer to the EFI_DEVICE_PATH_PROTOCOL\r
+                                 structure.\r
+  @param[in] DevicePathLength    The length of the device path.\r
+\r
+  @retval EFI_SUCCESS              The device path is valid.\r
+  @retval EFI_INVALID_PARAMETER    The device path is invalid.\r
+\r
+**/\r
+EFI_STATUS\r
+AhciIsHcDevicePathValid (\r
+  IN EFI_DEVICE_PATH_PROTOCOL    *DevicePath,\r
+  IN UINTN                       DevicePathLength\r
+  )\r
+{\r
+  EFI_DEVICE_PATH_PROTOCOL    *Start;\r
+  UINTN                       Size;\r
+\r
+  if (DevicePath == NULL) {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  //\r
+  // Validate the DevicePathLength is big enough to touch the first node.\r
+  //\r
+  if (DevicePathLength < sizeof (EFI_DEVICE_PATH_PROTOCOL)) {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  Start = DevicePath;\r
+  while (!(DevicePath->Type == END_DEVICE_PATH_TYPE &&\r
+           DevicePath->SubType == END_ENTIRE_DEVICE_PATH_SUBTYPE)) {\r
+    DevicePath = NextDevicePathNode (DevicePath);\r
+\r
+    //\r
+    // Prevent overflow and invalid zero in the 'Length' field of a device path\r
+    // node.\r
+    //\r
+    if ((UINTN) DevicePath <= (UINTN) Start) {\r
+      return EFI_INVALID_PARAMETER;\r
+    }\r
+\r
+    //\r
+    // Prevent touching memory beyond given DevicePathLength.\r
+    //\r
+    if ((UINTN) DevicePath - (UINTN) Start >\r
+        DevicePathLength - sizeof (EFI_DEVICE_PATH_PROTOCOL)) {\r
+      return EFI_INVALID_PARAMETER;\r
+    }\r
+  }\r
+\r
+  //\r
+  // Check if the device path and its size match each other.\r
+  //\r
+  Size = ((UINTN) DevicePath - (UINTN) Start) + sizeof (EFI_DEVICE_PATH_PROTOCOL);\r
+  if (Size != DevicePathLength) {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  return EFI_SUCCESS;\r
+}\r
+\r
+/**\r
+  Build the device path for an ATA device with given port and port multiplier number.\r
+\r
+  @param[in]  Private               A pointer to the PEI_AHCI_CONTROLLER_PRIVATE_DATA\r
+                                    data structure.\r
+  @param[in]  Port                  The given port number.\r
+  @param[in]  PortMultiplierPort    The given port multiplier number.\r
+  @param[out] DevicePathLength      The length of the device path in bytes specified\r
+                                    by DevicePath.\r
+  @param[out] DevicePath            The device path of ATA device.\r
+\r
+  @retval EFI_SUCCESS               The operation succeeds.\r
+  @retval EFI_INVALID_PARAMETER     The parameters are invalid.\r
+  @retval EFI_OUT_OF_RESOURCES      The operation fails due to lack of resources.\r
+\r
+**/\r
+EFI_STATUS\r
+AhciBuildDevicePath (\r
+  IN  PEI_AHCI_CONTROLLER_PRIVATE_DATA    *Private,\r
+  IN  UINT16                              Port,\r
+  IN  UINT16                              PortMultiplierPort,\r
+  OUT UINTN                               *DevicePathLength,\r
+  OUT EFI_DEVICE_PATH_PROTOCOL            **DevicePath\r
+  )\r
+{\r
+  EFI_DEVICE_PATH_PROTOCOL    *DevicePathWalker;\r
+  SATA_DEVICE_PATH            *SataDeviceNode;\r
+\r
+  if (DevicePathLength == NULL || DevicePath == NULL) {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  *DevicePathLength = Private->DevicePathLength + sizeof (SATA_DEVICE_PATH);\r
+  *DevicePath       = AllocatePool (*DevicePathLength);\r
+  if (*DevicePath == NULL) {\r
+    *DevicePathLength = 0;\r
+    return EFI_OUT_OF_RESOURCES;\r
+  }\r
+\r
+  //\r
+  // Construct the host controller part device nodes\r
+  //\r
+  DevicePathWalker = *DevicePath;\r
+  CopyMem (\r
+    DevicePathWalker,\r
+    Private->DevicePath,\r
+    Private->DevicePathLength - sizeof (EFI_DEVICE_PATH_PROTOCOL)\r
+    );\r
+\r
+  //\r
+  // Construct the SATA device node\r
+  //\r
+  DevicePathWalker = (EFI_DEVICE_PATH_PROTOCOL *) ((UINT8 *)DevicePathWalker +\r
+                     (Private->DevicePathLength - sizeof (EFI_DEVICE_PATH_PROTOCOL)));\r
+  CopyMem (\r
+    DevicePathWalker,\r
+    &mAhciSataDevicePathNodeTemplate,\r
+    sizeof (mAhciSataDevicePathNodeTemplate)\r
+    );\r
+  SataDeviceNode                           = (SATA_DEVICE_PATH *)DevicePathWalker;\r
+  SataDeviceNode->HBAPortNumber            = Port;\r
+  SataDeviceNode->PortMultiplierPortNumber = PortMultiplierPort;\r
+\r
+  //\r
+  // Construct the end device node\r
+  //\r
+  DevicePathWalker = (EFI_DEVICE_PATH_PROTOCOL *) ((UINT8 *)DevicePathWalker +\r
+                     sizeof (SATA_DEVICE_PATH));\r
+  CopyMem (\r
+    DevicePathWalker,\r
+    &mAhciEndDevicePathNodeTemplate,\r
+    sizeof (mAhciEndDevicePathNodeTemplate)\r
+    );\r
+\r
+  return EFI_SUCCESS;\r
+}\r