+\r
+/**\r
+ Check whether a USB device match the specified USB Class device path. This\r
+ function follows "Load Option Processing" behavior in UEFI specification.\r
+\r
+ @param UsbIo USB I/O protocol associated with the USB device.\r
+ @param UsbClass The USB Class device path to match.\r
+\r
+ @retval TRUE The USB device match the USB Class device path.\r
+ @retval FALSE The USB device does not match the USB Class device path.\r
+\r
+**/\r
+BOOLEAN\r
+BdsMatchUsbClass (\r
+ IN EFI_USB_IO_PROTOCOL *UsbIo,\r
+ IN USB_CLASS_DEVICE_PATH *UsbClass\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+ EFI_USB_DEVICE_DESCRIPTOR DevDesc;\r
+ EFI_USB_INTERFACE_DESCRIPTOR IfDesc;\r
+ UINT8 DeviceClass;\r
+ UINT8 DeviceSubClass;\r
+ UINT8 DeviceProtocol;\r
+\r
+ if ((DevicePathType (UsbClass) != MESSAGING_DEVICE_PATH) ||\r
+ (DevicePathSubType (UsbClass) != MSG_USB_CLASS_DP)){\r
+ return FALSE;\r
+ }\r
+\r
+ //\r
+ // Check Vendor Id and Product Id.\r
+ //\r
+ Status = UsbIo->UsbGetDeviceDescriptor (UsbIo, &DevDesc);\r
+ if (EFI_ERROR (Status)) {\r
+ return FALSE;\r
+ }\r
+\r
+ if ((UsbClass->VendorId != 0xffff) &&\r
+ (UsbClass->VendorId != DevDesc.IdVendor)) {\r
+ return FALSE;\r
+ }\r
+\r
+ if ((UsbClass->ProductId != 0xffff) &&\r
+ (UsbClass->ProductId != DevDesc.IdProduct)) {\r
+ return FALSE;\r
+ }\r
+\r
+ DeviceClass = DevDesc.DeviceClass;\r
+ DeviceSubClass = DevDesc.DeviceSubClass;\r
+ DeviceProtocol = DevDesc.DeviceProtocol;\r
+ if (DeviceClass == 0) {\r
+ //\r
+ // If Class in Device Descriptor is set to 0, use the Class, SubClass and\r
+ // Protocol in Interface Descriptor instead.\r
+ //\r
+ Status = UsbIo->UsbGetInterfaceDescriptor (UsbIo, &IfDesc);\r
+ if (EFI_ERROR (Status)) {\r
+ return FALSE;\r
+ }\r
+\r
+ DeviceClass = IfDesc.InterfaceClass;\r
+ DeviceSubClass = IfDesc.InterfaceSubClass;\r
+ DeviceProtocol = IfDesc.InterfaceProtocol;\r
+ }\r
+\r
+ //\r
+ // Check Class, SubClass and Protocol.\r
+ //\r
+ if ((UsbClass->DeviceClass != 0xff) &&\r
+ (UsbClass->DeviceClass != DeviceClass)) {\r
+ return FALSE;\r
+ }\r
+\r
+ if ((UsbClass->DeviceSubClass != 0xff) &&\r
+ (UsbClass->DeviceSubClass != DeviceSubClass)) {\r
+ return FALSE;\r
+ }\r
+\r
+ if ((UsbClass->DeviceProtocol != 0xff) &&\r
+ (UsbClass->DeviceProtocol != DeviceProtocol)) {\r
+ return FALSE;\r
+ }\r
+\r
+ return TRUE;\r
+}\r
+\r
+/**\r
+ Check whether a USB device match the specified USB WWID device path. This\r
+ function follows "Load Option Processing" behavior in UEFI specification.\r
+\r
+ @param UsbIo USB I/O protocol associated with the USB device.\r
+ @param UsbWwid The USB WWID device path to match.\r
+\r
+ @retval TRUE The USB device match the USB WWID device path.\r
+ @retval FALSE The USB device does not match the USB WWID device path.\r
+\r
+**/\r
+BOOLEAN\r
+BdsMatchUsbWwid (\r
+ IN EFI_USB_IO_PROTOCOL *UsbIo,\r
+ IN USB_WWID_DEVICE_PATH *UsbWwid\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+ EFI_USB_DEVICE_DESCRIPTOR DevDesc;\r
+ EFI_USB_INTERFACE_DESCRIPTOR IfDesc;\r
+ UINT16 *LangIdTable;\r
+ UINT16 TableSize;\r
+ UINT16 Index;\r
+ CHAR16 *CompareStr;\r
+ UINTN CompareLen;\r
+ CHAR16 *SerialNumberStr;\r
+ UINTN Length;\r
+\r
+ if ((DevicePathType (UsbWwid) != MESSAGING_DEVICE_PATH) ||\r
+ (DevicePathSubType (UsbWwid) != MSG_USB_WWID_DP )){\r
+ return FALSE;\r
+ }\r
+\r
+ //\r
+ // Check Vendor Id and Product Id.\r
+ //\r
+ Status = UsbIo->UsbGetDeviceDescriptor (UsbIo, &DevDesc);\r
+ if (EFI_ERROR (Status)) {\r
+ return FALSE;\r
+ }\r
+ if ((DevDesc.IdVendor != UsbWwid->VendorId) ||\r
+ (DevDesc.IdProduct != UsbWwid->ProductId)) {\r
+ return FALSE;\r
+ }\r
+\r
+ //\r
+ // Check Interface Number.\r
+ //\r
+ Status = UsbIo->UsbGetInterfaceDescriptor (UsbIo, &IfDesc);\r
+ if (EFI_ERROR (Status)) {\r
+ return FALSE;\r
+ }\r
+ if (IfDesc.InterfaceNumber != UsbWwid->InterfaceNumber) {\r
+ return FALSE;\r
+ }\r
+\r
+ //\r
+ // Check Serial Number.\r
+ //\r
+ if (DevDesc.StrSerialNumber == 0) {\r
+ return FALSE;\r
+ }\r
+\r
+ //\r
+ // Get all supported languages.\r
+ //\r
+ TableSize = 0;\r
+ LangIdTable = NULL;\r
+ Status = UsbIo->UsbGetSupportedLanguages (UsbIo, &LangIdTable, &TableSize);\r
+ if (EFI_ERROR (Status) || (TableSize == 0) || (LangIdTable == NULL)) {\r
+ return FALSE;\r
+ }\r
+\r
+ //\r
+ // Serial number in USB WWID device path is the last 64-or-less UTF-16 characters.\r
+ //\r
+ CompareStr = (CHAR16 *) (UINTN) (UsbWwid + 1);\r
+ CompareLen = (DevicePathNodeLength (UsbWwid) - sizeof (USB_WWID_DEVICE_PATH)) / sizeof (CHAR16);\r
+ if (CompareStr[CompareLen - 1] == L'\0') {\r
+ CompareLen--;\r
+ }\r
+\r
+ //\r
+ // Compare serial number in each supported language.\r
+ //\r
+ for (Index = 0; Index < TableSize / sizeof (UINT16); Index++) {\r
+ SerialNumberStr = NULL;\r
+ Status = UsbIo->UsbGetStringDescriptor (\r
+ UsbIo,\r
+ LangIdTable[Index],\r
+ DevDesc.StrSerialNumber,\r
+ &SerialNumberStr\r
+ );\r
+ if (EFI_ERROR (Status) || (SerialNumberStr == NULL)) {\r
+ continue;\r
+ }\r
+\r
+ Length = StrLen (SerialNumberStr);\r
+ if ((Length >= CompareLen) &&\r
+ (CompareMem (SerialNumberStr + Length - CompareLen, CompareStr, CompareLen * sizeof (CHAR16)) == 0)) {\r
+ FreePool (SerialNumberStr);\r
+ return TRUE;\r
+ }\r
+\r
+ FreePool (SerialNumberStr);\r
+ }\r
+\r
+ return FALSE;\r
+}\r
+\r
+/**\r
+ Find a USB device path which match the specified short-form device path start\r
+ with USB Class or USB WWID device path and load the boot file then return the \r
+ image handle. If ParentDevicePath is NULL, this function will search in all USB\r
+ devices of the platform. If ParentDevicePath is not NULL,this function will only\r
+ search in its child devices.\r
+\r
+ @param ParentDevicePath The device path of the parent.\r
+ @param ShortFormDevicePath The USB Class or USB WWID device path to match.\r
+\r
+ @return The image Handle if find load file from specified short-form device path\r
+ or NULL if not found.\r
+\r
+**/\r
+EFI_HANDLE *\r
+BdsFindUsbDevice (\r
+ IN EFI_DEVICE_PATH_PROTOCOL *ParentDevicePath,\r
+ IN EFI_DEVICE_PATH_PROTOCOL *ShortFormDevicePath\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+ UINTN UsbIoHandleCount;\r
+ EFI_HANDLE *UsbIoHandleBuffer;\r
+ EFI_DEVICE_PATH_PROTOCOL *UsbIoDevicePath;\r
+ EFI_USB_IO_PROTOCOL *UsbIo;\r
+ UINTN Index;\r
+ UINTN ParentSize;\r
+ UINTN Size;\r
+ EFI_HANDLE ImageHandle;\r
+ EFI_HANDLE Handle;\r
+ EFI_DEVICE_PATH_PROTOCOL *FullDevicePath;\r
+ EFI_DEVICE_PATH_PROTOCOL *NextDevicePath;\r
+\r
+ FullDevicePath = NULL;\r
+ ImageHandle = NULL;\r
+\r
+ //\r
+ // Get all UsbIo Handles.\r
+ //\r
+ UsbIoHandleCount = 0;\r
+ UsbIoHandleBuffer = NULL;\r
+ Status = gBS->LocateHandleBuffer (\r
+ ByProtocol,\r
+ &gEfiUsbIoProtocolGuid,\r
+ NULL,\r
+ &UsbIoHandleCount,\r
+ &UsbIoHandleBuffer\r
+ );\r
+ if (EFI_ERROR (Status) || (UsbIoHandleCount == 0) || (UsbIoHandleBuffer == NULL)) {\r
+ return NULL;\r
+ }\r
+\r
+ ParentSize = (ParentDevicePath == NULL) ? 0 : GetDevicePathSize (ParentDevicePath);\r
+ for (Index = 0; Index < UsbIoHandleCount; Index++) {\r
+ //\r
+ // Get the Usb IO interface.\r
+ //\r
+ Status = gBS->HandleProtocol(\r
+ UsbIoHandleBuffer[Index],\r
+ &gEfiUsbIoProtocolGuid,\r
+ (VOID **) &UsbIo\r
+ );\r
+ if (EFI_ERROR (Status)) {\r
+ continue;\r
+ }\r
+\r
+ UsbIoDevicePath = DevicePathFromHandle (UsbIoHandleBuffer[Index]);\r
+ if (UsbIoDevicePath == NULL) {\r
+ continue;\r
+ }\r
+\r
+ if (ParentDevicePath != NULL) {\r
+ //\r
+ // Compare starting part of UsbIoHandle's device path with ParentDevicePath.\r
+ //\r
+ Size = GetDevicePathSize (UsbIoDevicePath);\r
+ if ((Size < ParentSize) ||\r
+ (CompareMem (UsbIoDevicePath, ParentDevicePath, ParentSize - END_DEVICE_PATH_LENGTH) != 0)) {\r
+ continue;\r
+ }\r
+ }\r
+\r
+ if (BdsMatchUsbClass (UsbIo, (USB_CLASS_DEVICE_PATH *) ShortFormDevicePath) ||\r
+ BdsMatchUsbWwid (UsbIo, (USB_WWID_DEVICE_PATH *) ShortFormDevicePath)) {\r
+ //\r
+ // Try to find if there is the boot file in this DevicePath\r
+ //\r
+ NextDevicePath = NextDevicePathNode (ShortFormDevicePath);\r
+ if (!IsDevicePathEnd (NextDevicePath)) {\r
+ FullDevicePath = AppendDevicePath (UsbIoDevicePath, NextDevicePath);\r
+ //\r
+ // Connect the full device path, so that Simple File System protocol\r
+ // could be installed for this USB device.\r
+ //\r
+ BdsLibConnectDevicePath (FullDevicePath);\r
+ REPORT_STATUS_CODE (EFI_PROGRESS_CODE, PcdGet32 (PcdProgressCodeOsLoaderLoad));\r
+ Status = gBS->LoadImage (\r
+ TRUE,\r
+ gImageHandle,\r
+ FullDevicePath,\r
+ NULL,\r
+ 0,\r
+ &ImageHandle\r
+ );\r
+ FreePool (FullDevicePath);\r
+ } else {\r
+ FullDevicePath = UsbIoDevicePath;\r
+ Status = EFI_NOT_FOUND;\r
+ }\r
+\r
+ //\r
+ // If we didn't find an image directly, we need to try as if it is a removable device boot option\r
+ // and load the image according to the default boot behavior for removable device.\r
+ //\r
+ if (EFI_ERROR (Status)) {\r
+ //\r
+ // check if there is a bootable removable media could be found in this device path ,\r
+ // and get the bootable media handle\r
+ //\r
+ Handle = BdsLibGetBootableHandle(UsbIoDevicePath);\r
+ if (Handle == NULL) {\r
+ continue;\r
+ }\r
+ //\r
+ // Load the default boot file \EFI\BOOT\boot{machinename}.EFI from removable Media\r
+ // machinename is ia32, ia64, x64, ...\r
+ //\r
+ FullDevicePath = FileDevicePath (Handle, EFI_REMOVABLE_MEDIA_FILE_NAME);\r
+ if (FullDevicePath != NULL) {\r
+ REPORT_STATUS_CODE (EFI_PROGRESS_CODE, PcdGet32 (PcdProgressCodeOsLoaderLoad));\r
+ Status = gBS->LoadImage (\r
+ TRUE,\r
+ gImageHandle,\r
+ FullDevicePath,\r
+ NULL,\r
+ 0,\r
+ &ImageHandle\r
+ );\r
+ if (EFI_ERROR (Status)) {\r
+ //\r
+ // The DevicePath failed, and it's not a valid\r
+ // removable media device.\r
+ //\r
+ continue;\r
+ }\r
+ } else {\r
+ continue;\r
+ }\r
+ }\r
+ break;\r
+ }\r
+ }\r
+\r
+ FreePool (UsbIoHandleBuffer);\r
+ return ImageHandle;\r
+}\r
+\r
+/**\r
+ Expand USB Class or USB WWID device path node to be full device path of a USB\r
+ device in platform then load the boot file on this full device path and return the \r
+ image handle.\r
+\r
+ This function support following 4 cases:\r
+ 1) Boot Option device path starts with a USB Class or USB WWID device path,\r
+ and there is no Media FilePath device path in the end.\r
+ In this case, it will follow Removable Media Boot Behavior.\r
+ 2) Boot Option device path starts with a USB Class or USB WWID device path,\r
+ and ended with Media FilePath device path.\r
+ 3) Boot Option device path starts with a full device path to a USB Host Controller,\r
+ contains a USB Class or USB WWID device path node, while not ended with Media\r
+ FilePath device path. In this case, it will follow Removable Media Boot Behavior.\r
+ 4) Boot Option device path starts with a full device path to a USB Host Controller,\r
+ contains a USB Class or USB WWID device path node, and ended with Media\r
+ FilePath device path.\r
+\r
+ @param DevicePath The Boot Option device path.\r
+\r
+ @return The image handle of boot file, or NULL if there is no boot file found in\r
+ the specified USB Class or USB WWID device path.\r
+\r
+**/\r
+EFI_HANDLE *\r
+BdsExpandUsbShortFormDevicePath (\r
+ IN EFI_DEVICE_PATH_PROTOCOL *DevicePath\r
+ )\r
+{\r
+ EFI_HANDLE *ImageHandle;\r
+ EFI_DEVICE_PATH_PROTOCOL *TempDevicePath;\r
+ EFI_DEVICE_PATH_PROTOCOL *ShortFormDevicePath;\r
+\r
+ //\r
+ // Search for USB Class or USB WWID device path node.\r
+ //\r
+ ShortFormDevicePath = NULL;\r
+ ImageHandle = NULL;\r
+ TempDevicePath = DevicePath;\r
+ while (!IsDevicePathEnd (TempDevicePath)) {\r
+ if ((DevicePathType (TempDevicePath) == MESSAGING_DEVICE_PATH) &&\r
+ ((DevicePathSubType (TempDevicePath) == MSG_USB_CLASS_DP) ||\r
+ (DevicePathSubType (TempDevicePath) == MSG_USB_WWID_DP))) {\r
+ ShortFormDevicePath = TempDevicePath;\r
+ break;\r
+ }\r
+ TempDevicePath = NextDevicePathNode (TempDevicePath);\r
+ }\r
+\r
+ if (ShortFormDevicePath == NULL) {\r
+ //\r
+ // No USB Class or USB WWID device path node found, do nothing.\r
+ //\r
+ return NULL;\r
+ }\r
+\r
+ if (ShortFormDevicePath == DevicePath) {\r
+ //\r
+ // Boot Option device path starts with USB Class or USB WWID device path.\r
+ //\r
+ ImageHandle = BdsFindUsbDevice (NULL, ShortFormDevicePath);\r
+ if (ImageHandle == NULL) {\r
+ //\r
+ // Failed to find a match in existing devices, connect the short form USB\r
+ // device path and try again.\r
+ //\r
+ BdsLibConnectUsbDevByShortFormDP (0xff, ShortFormDevicePath);\r
+ ImageHandle = BdsFindUsbDevice (NULL, ShortFormDevicePath);\r
+ }\r
+ } else {\r
+ //\r
+ // Boot Option device path contains USB Class or USB WWID device path node.\r
+ //\r
+\r
+ //\r
+ // Prepare the parent device path for search.\r
+ //\r
+ TempDevicePath = DuplicateDevicePath (DevicePath);\r
+ ASSERT (TempDevicePath != NULL);\r
+ SetDevicePathEndNode (((UINT8 *) TempDevicePath) + ((UINTN) ShortFormDevicePath - (UINTN) DevicePath));\r
+\r
+ //\r
+ // The USB Host Controller device path is already in Boot Option device path\r
+ // and USB Bus driver already support RemainingDevicePath starts with USB\r
+ // Class or USB WWID device path, so just search in existing USB devices and\r
+ // doesn't perform ConnectController here.\r
+ //\r
+ ImageHandle = BdsFindUsbDevice (TempDevicePath, ShortFormDevicePath);\r
+ FreePool (TempDevicePath);\r
+ }\r
+\r
+ return ImageHandle;\r
+}\r
+\r