gbmc-release-25.2.x: meta-security: libhoth: add patch that lets htool retry

Tested:
Manully checked: htool includes the new chagne from the patch.
```
root@wkc27-nfd01:~# htool 2>&1 | grep "usb_"
  --usb_loc (default: "")
  --usb_product (default: "")
  --usb_retry_duration (default: "1000ms")
  --usb_retry_delay (default: "50ms")
root@wkc27-nfd01:~# htool --version
e4827163741e0804f12ac96c81b8e97649be6795-dirty
```

Fusion-Link: fusion2/51a51fa9-2b47-3ba8-856f-d849962b1c92 (platform11)
Fusion-Link: fusion2/64b92888-41c3-39d6-b133-c23425b850bb (platform5)
Fusion-Link: fusion2/ea3d191d-cc33-3a16-9814-03371d53a042 (platform15)
Fusion-Link: fusion2/43899abd-8425-3c62-b092-533da44e8154 (platform17)
Platforms-Affected: All TTF platforms
Google-Bug-Id: 415725820
Change-Id: Icfef421276471a6a97cc53b055233a4aaf5e990e
Signed-off-by: Leo Tu <leotu@google.com>
diff --git a/recipes-tpm1/hoth/libhoth/0001-Add-retry-loop-to-htool_libhoth_usb_device-for-LIBUSB.patch b/recipes-tpm1/hoth/libhoth/0001-Add-retry-loop-to-htool_libhoth_usb_device-for-LIBUSB.patch
new file mode 100644
index 0000000..b34a4f4
--- /dev/null
+++ b/recipes-tpm1/hoth/libhoth/0001-Add-retry-loop-to-htool_libhoth_usb_device-for-LIBUSB.patch
@@ -0,0 +1,210 @@
+From 8c04518e2cea3bbbd77ccc197e0a5dba068f2f58 Mon Sep 17 00:00:00 2001
+From: Chris Evans <cjevans@google.com>
+Date: Mon, 28 Apr 2025 10:09:25 -0700
+Subject: [PATCH] Add retry loop to htool_libhoth_usb_device, for
+ LIBUSB_ERROR_BUSY.
+
+In some cases the USB device may be temporarily busy (ie, held by
+another process), and htool will sometimes return -6 (ie,
+LIBUSB_ERROR_BUSY).  Retrying in this case (for some limited amount of
+time) may be effective.
+
+Cherry-pick of 9095f644b7577659af083a5703be46acdda8cfde.
+---
+ examples/htool.c     |   6 ++
+ examples/htool_usb.c | 139 ++++++++++++++++++++++++++++++++++++++++---
+ 2 files changed, 138 insertions(+), 7 deletions(-)
+
+diff --git a/examples/htool.c b/examples/htool.c
+index beabe9e..c8d1b1f 100644
+--- a/examples/htool.c
++++ b/examples/htool.c
+@@ -877,6 +877,11 @@ static const struct htool_param GLOBAL_FLAGS[] = {
+     {HTOOL_FLAG_VALUE, .name = "mailbox_location", .default_value = "0",
+      .desc = "The location of the mailbox on the RoT, for 'spidev' "
+              "or 'mtd' transports; for example '0x900000'."},
++    {HTOOL_FLAG_VALUE, .name = "usb_retry_duration", .default_value = "1000ms",
++     .desc = "Maximum duration to retry opening a busy USB device (e.g., "
++             "'1s', '1500ms')."},
++    {HTOOL_FLAG_VALUE, .name = "usb_retry_delay", .default_value = "50ms",
++     .desc = "Delay between USB open retries (e.g., '50ms', '10000us')."},
+     {HTOOL_FLAG_BOOL, .name = "version", .default_value = "false",
+      .desc = "Print htool version."},
+     {}};
+@@ -884,3 +889,4 @@ static const struct htool_param GLOBAL_FLAGS[] = {
+ int main(int argc, const char* const* argv) {
+   return htool_main(GLOBAL_FLAGS, CMDS, argc - 1, &argv[1]);
+ }
++
+diff --git a/examples/htool_usb.c b/examples/htool_usb.c
+index 514ca74..88adc5a 100644
+--- a/examples/htool_usb.c
++++ b/examples/htool_usb.c
+@@ -14,6 +14,8 @@
+ 
+ #include "htool_usb.h"
+ 
++#include <ctype.h>
++#include <errno.h>
+ #include <libusb.h>
+ #include <stdbool.h>
+ #include <stddef.h>
+@@ -21,6 +23,8 @@
+ #include <stdio.h>
+ #include <stdlib.h>
+ #include <string.h>
++#include <time.h>
++#include <unistd.h>
+ 
+ #include "../libhoth_usb.h"
+ #include "ec_util.h"
+@@ -312,6 +316,62 @@ libusb_device* htool_libusb_device(void) {
+   return select_device(ctx, filter_allow_all, NULL);
+ }
+ 
++// Helper function to get current monotonic time in milliseconds
++static uint64_t get_monotonic_ms() {
++    struct timespec ts;
++    if (clock_gettime(CLOCK_MONOTONIC, &ts) != 0) {
++        perror("clock_gettime failed");
++        // Return 0 or a value indicating error, relying on caller checks
++        return 0;
++    }
++    return (uint64_t)ts.tv_sec * 1000 + (uint64_t)ts.tv_nsec / 1000000;
++}
++
++// Helper function to parse time string with units (s, ms, us) into microseconds
++// Returns -1 on error.
++static int64_t parse_time_string_us(const char* time_str) {
++    if (!time_str || *time_str == '\0') {
++        return -1; // Invalid input
++    }
++
++    char* endptr;
++    long long val = strtoll(time_str, &endptr, 10);
++
++    if (endptr == time_str || val < 0) {
++        return -1; // No digits found or negative value
++    }
++
++    // Skip whitespace
++    while (*endptr != '\0' && isspace((unsigned char)*endptr)) {
++        endptr++;
++    }
++
++    uint64_t multiplier = 1000000; // Default to seconds if no unit
++
++    if (*endptr != '\0') {
++        // Check for units (case-insensitive)
++        if (tolower((unsigned char)endptr[0]) == 's' && endptr[1] == '\0') {
++            multiplier = 1000000; // seconds
++        } else if (tolower((unsigned char)endptr[0]) == 'm' &&
++                   tolower((unsigned char)endptr[1]) == 's' && endptr[2] == '\0') {
++            multiplier = 1000; // milliseconds
++        } else if (tolower((unsigned char)endptr[0]) == 'u' &&
++                   tolower((unsigned char)endptr[1]) == 's' && endptr[2] == '\0') {
++            multiplier = 1; // microseconds
++        } else {
++            return -1; // Invalid unit or extra characters
++        }
++    }
++
++    // Check for potential overflow before multiplying
++    if (val > INT64_MAX / multiplier) {
++         return -1; // Overflow
++    }
++
++    return (int64_t)val * multiplier;
++}
++
++
+ struct libhoth_device* htool_libhoth_usb_device(void) {
+   static struct libhoth_device* result;
+   if (result) {
+@@ -322,14 +382,79 @@ struct libhoth_device* htool_libhoth_usb_device(void) {
+   if (!ctx || !usb_dev) {
+     return NULL;
+   }
+-  struct libhoth_usb_device_init_options opts = {.usb_device = usb_dev,
+-                                                 .usb_ctx = ctx};
+-  int rv = libhoth_usb_open(&opts, &result);
+-  if (rv) {
+-    // TODO: Convert error-code to a string
+-    fprintf(stderr, "libhoth_usb_open error: %d\n", rv);
+-    return NULL;
++
++  // Get retry parameters from global flags
++  const char* duration_str;
++  const char* delay_str;
++  if (htool_get_param_string(htool_global_flags(), "usb_retry_duration", &duration_str) ||
++      htool_get_param_string(htool_global_flags(), "usb_retry_delay", &delay_str)) {
++      return NULL;
++  }
++
++  int64_t retry_duration_us = parse_time_string_us(duration_str);
++  int64_t retry_delay_us = parse_time_string_us(delay_str);
++
++  if (retry_duration_us < 0) {
++      fprintf(stderr, "Invalid format for --usb_retry_duration: %s\n", duration_str);
++      return NULL;
++  }
++  if (retry_delay_us < 0) {
++      fprintf(stderr, "Invalid format for --usb_retry_delay: %s\n", delay_str);
++      return NULL;
++  }
++  // Convert duration to milliseconds for comparison with monotonic time helper
++  uint64_t retry_duration_ms = (uint64_t)retry_duration_us / 1000;
++
++  struct libhoth_usb_device_init_options opts = {
++      .usb_device = usb_dev, .usb_ctx = ctx};
++
++  int rv = LIBUSB_ERROR_BUSY; // Initialize rv to trigger the loop
++  uint64_t start_time_ms = get_monotonic_ms();
++  if (start_time_ms == 0 && errno != 0) { // Check if get_monotonic_ms failed
++      return NULL;
+   }
++  uint64_t current_time_ms;
++
++  while (rv == LIBUSB_ERROR_BUSY) {
++      rv = libhoth_usb_open(&opts, &result);
++      if (rv == LIBUSB_SUCCESS) {
++          break; // Successfully opened
++      }
++      if (rv != LIBUSB_ERROR_BUSY) {
++          // A different error occurred, report it and exit
++          fprintf(stderr, "libhoth_usb_open error: %d (%s)\n", rv, libusb_strerror(rv));
++          return NULL;
++      }
++
++      // Check elapsed time
++      current_time_ms = get_monotonic_ms();
++       if (current_time_ms == 0 && errno != 0) {
++           return NULL;
++       }
++      // Handle potential timer wrap-around or error from get_monotonic_ms
++      if (current_time_ms < start_time_ms) {
++          fprintf(stderr, "Monotonic clock error detected during retry loop.\n");
++          return NULL;
++      }
++
++      if (current_time_ms - start_time_ms >= retry_duration_ms) {
++          fprintf(stderr, "libhoth_usb_open timed out after %s (error: %d (%s))\n",
++                  duration_str, rv, libusb_strerror(rv));
++          return NULL; // Timeout
++      }
++
++      // Wait before retrying
++      // Ensure delay doesn't exceed reasonable limits for usleep (~10s)
++      useconds_t sleep_us = (retry_delay_us > 10000000) ? 10000000 : (useconds_t)retry_delay_us;
++      usleep(sleep_us);
++  }
++
++  if (rv != LIBUSB_SUCCESS) {
++      fprintf(stderr, "libhoth_usb_open error: %d (%s)\n", rv, libusb_strerror(rv));
++      result = NULL;
++      return NULL;
++  }
++
+   return result;
+ }
+ 
diff --git a/recipes-tpm1/hoth/libhoth_%.bbappend b/recipes-tpm1/hoth/libhoth_%.bbappend
index 92515d5b..f1767ca 100644
--- a/recipes-tpm1/hoth/libhoth_%.bbappend
+++ b/recipes-tpm1/hoth/libhoth_%.bbappend
@@ -1 +1,8 @@
+FILESEXTRAPATHS:prepend:gbmc := "${THISDIR}/${PN}:"
+
 SRCREV:gbmc = "e4827163741e0804f12ac96c81b8e97649be6795"
+
+SRC_URI:append:gbmc = " \
+    file://0001-Add-retry-loop-to-htool_libhoth_usb_device-for-LIBUSB.patch \
+"
+