--- /dev/null
+/** @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