gbmc-release-25.18.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>
(cherry picked from commit 6375cfc064461661601d90fedcc3e2cc41e1395e)
Signed-off-by: Leo Tu <leotu@google.com>
diff --git a/recipes-tpm1/hoth/libhoth/0002-Add-retry-loop-to-htool_libhoth_usb_device-for-LIBUSB.patch b/recipes-tpm1/hoth/libhoth/0002-Add-retry-loop-to-htool_libhoth_usb_device-for-LIBUSB.patch
new file mode 100644
index 0000000..1cecf59
--- /dev/null
+++ b/recipes-tpm1/hoth/libhoth/0002-Add-retry-loop-to-htool_libhoth_usb_device-for-LIBUSB.patch
@@ -0,0 +1,214 @@
+From ff3816a67857ab025ad7aa6d6bd4fd121c4fd657 Mon Sep 17 00:00:00 2001
+From: Leo Tu <leotu@google.com>
+Date: Wed, 11 Jun 2025 22:11:37 +0800
+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 | 7 ++-
+ examples/htool_usb.c | 139 ++++++++++++++++++++++++++++++++++++++++---
+ 2 files changed, 138 insertions(+), 8 deletions(-)
+
+diff --git a/examples/htool.c b/examples/htool.c
+index 0956d91..032d896 100644
+--- a/examples/htool.c
++++ b/examples/htool.c
+@@ -1149,10 +1149,15 @@ static const struct htool_param GLOBAL_FLAGS[] = {
+ "or 'mtd' transports; for example '0x900000'."},
+ {HTOOL_FLAG_VALUE, .name = "dbus_hoth_id", .default_value = "",
+ .desc = "The hoth ID associated with the RoT's hothd service."},
++ {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."},
+ {}};
+
+ int main(int argc, const char* const* argv) {
+ return htool_main(GLOBAL_FLAGS, CMDS, argc - 1, &argv[1]);
+-}
++}
+\ No newline at end of file
+diff --git a/examples/htool_usb.c b/examples/htool_usb.c
+index cedc249..8801471 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"
+@@ -314,6 +318,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) {
+@@ -324,14 +384,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;
+ }
+
+--
+2.50.0.rc0.642.g800a2b2222-goog
+
diff --git a/recipes-tpm1/hoth/libhoth_%.bbappend b/recipes-tpm1/hoth/libhoth_%.bbappend
index e8771c5..0835d64 100644
--- a/recipes-tpm1/hoth/libhoth_%.bbappend
+++ b/recipes-tpm1/hoth/libhoth_%.bbappend
@@ -5,4 +5,5 @@
#TODO(b/411502579): remove this patch once libhoth pin is updated to newer upstream version.
SRC_URI:append:gbmc = " \
file://0001-Expose-spi-headers-in-libhoth.patch \
+ file://0002-Add-retry-loop-to-htool_libhoth_usb_device-for-LIBUSB.patch \
"