[Concept,08/16] tkey: Support loading an app with a user-supplied secret

Message ID 20251115185212.539268-9-sjg@u-boot.org
State New
Headers
Series Continue TKey development |

Commit Message

Simon Glass Nov. 15, 2025, 6:51 p.m. UTC
  From: Simon Glass <simon.glass@canonical.com>

One useful feature a the TKey is the ability to set up its app with an
key modified by a user-supplied secret. Add support for this.

Take this opportunity to make the API more expo-friendly by allowing
loading of the TKey to take place iteratively. The TKey runs fairly
slowly (about 60Kbaud) and loading an app takes 6 seconds or so.

Signed-off-by: Simon Glass <simon.glass@canonical.com>
---

 drivers/misc/tkey-uclass.c | 149 +++++++++++++++++++++----------------
 include/tkey.h             |  49 ++++++++++++
 2 files changed, 132 insertions(+), 66 deletions(-)
  

Patch

diff --git a/drivers/misc/tkey-uclass.c b/drivers/misc/tkey-uclass.c
index 4696f9c8d83..d2c4b8351fd 100644
--- a/drivers/misc/tkey-uclass.c
+++ b/drivers/misc/tkey-uclass.c
@@ -502,27 +502,80 @@  static int tkey_load_app_header(struct udevice *dev, int app_size,
 	return 0;
 }
 
-static int tkey_load_app_data(struct udevice *dev, const void *data, int size)
+int tkey_load_app_with_uss(struct udevice *dev, const void *app_data,
+			   int app_size, const void *uss, int uss_size)
 {
-	struct tkey_frame cmd_frame, rsp_frame;
-	int offset = 0;
+	struct tkey_load_ctx ctx;
 	int ret;
 
-	log_debug("Loading app data, %u bytes\n", size);
+	/* Start loading */
+	ret = tkey_load_start(&ctx, dev, app_data, app_size, uss,
+			      uss_size);
+	if (ret)
+		return ret;
+
+	/* Send all remaining blocks */
+	do {
+		ret = tkey_load_next(&ctx, 0);
+	} while (ret == -EAGAIN);
+
+	return ret;
+}
+
+int tkey_load_app(struct udevice *dev, const void *app_data, int app_size)
+{
+	return tkey_load_app_with_uss(dev, app_data, app_size, NULL, 0);
+}
+
+int tkey_load_start(struct tkey_load_ctx *ctx, struct udevice *dev,
+		    const void *app_data, int app_size,
+		    const void *uss, int uss_size)
+{
+	int ret;
+
+	/* Initialize context */
+	ctx->dev = dev;
+	ctx->app_data = app_data;
+	ctx->app_size = app_size;
+	ctx->offset = 0;
+
+	/* Check if we're in firmware mode first */
+	ret = tkey_in_app_mode(dev);
+	if (ret < 0) {
+		log_debug("Failed to check device mode (error %d)\n", ret);
+		return ret;
+	}
+
+	if (ret) {
+		log_debug("Device must be in firmware mode to load app\n");
+		return -ENOTSUPP;
+	}
+
+	log_debug("Starting iterative app load (%u bytes)...\n", app_size);
+
+	/* Send app header with size and USS (if provided) */
+	ret = tkey_load_app_header(dev, app_size, uss, uss_size);
+	if (ret) {
+		log_debug("Failed to send app header (error %d)\n", ret);
+		return ret;
+	}
+
+	return 0;
+}
+
+int tkey_load_next(struct tkey_load_ctx *ctx, int max_blocks)
+{
+	struct tkey_frame cmd_frame, rsp_frame;
+	int blocks_sent = 0;
+	int ret;
 
-	while (offset < size) {
-		int todo = min(size - offset, TKEY_MAX_DATA_SIZE - 1);
-		u8 len_code;
+	/* If max_blocks is 0, send all remaining blocks */
+	if (max_blocks == 0)
+		max_blocks = INT_MAX;
 
-		/* Determine length code for chunk */
-		if (todo <= 1)
-			len_code = TKEY_LEN_1_BYTE;
-		else if (todo <= 4)
-			len_code = TKEY_LEN_4_BYTES;
-		else if (todo <= 32)
-			len_code = TKEY_LEN_32_BYTES;
-		else
-			len_code = TKEY_LEN_128_BYTES;
+	while (ctx->offset < ctx->app_size && blocks_sent < max_blocks) {
+		int todo = min(ctx->app_size - ctx->offset,
+			       TKEY_MAX_DATA_SIZE - 1);
 
 		/*
 		 * Build LOAD_APP_DATA command (always use 128-byte frames
@@ -533,7 +586,7 @@  static int tkey_load_app_data(struct udevice *dev, const void *data, int size)
 					    TKEY_STATUS_OK,
 					    TKEY_LEN_128_BYTES);
 		cmd_frame.data[0] = TKEY_FW_CMD_LOAD_APP_DATA;
-		memcpy(&cmd_frame.data[1], data + offset, todo);
+		memcpy(&cmd_frame.data[1], ctx->app_data + ctx->offset, todo);
 
 		/* Pad remaining bytes with zeros */
 		if (todo + 1 < 128)
@@ -541,73 +594,37 @@  static int tkey_load_app_data(struct udevice *dev, const void *data, int size)
 			       128 - (todo + 1));
 
 		/* Send chunk (always 128 bytes like Go app) */
-		ret = tkey_send_frame(dev, &cmd_frame, 128);
+		ret = tkey_send_frame(ctx->dev, &cmd_frame, 128);
 		if (ret < 0)
 			return ret;
 
 		/* Receive response */
-		ret = tkey_recv_frame(dev, &rsp_frame, TKEY_LOAD_TIMEOUT_MS);
+		ret = tkey_recv_frame(ctx->dev, &rsp_frame,
+				      TKEY_LOAD_TIMEOUT_MS);
 		if (ret < 0)
 			return ret;
 
 		/* Check response status */
 		if (rsp_frame.header & TKEY_STATUS_ERROR) {
 			log_debug("Load app data failed at offset %u\n",
-				  offset);
+				  ctx->offset);
 			return -EIO;
 		}
 
-		offset += todo;
-		log_debug("Loaded chunk: %u/%u bytes\n", offset, size);
+		ctx->offset += todo;
+		blocks_sent++;
+		log_debug("Loaded chunk: %u/%u bytes (%d blocks sent)\n",
+			  ctx->offset, ctx->app_size, blocks_sent);
 		schedule();
 	}
 
-	log_debug("App data loaded successfully\n");
-
-	return 0;
-}
-
-int tkey_load_app_with_uss(struct udevice *dev, const void *app_data,
-			   int app_size, const void *uss, int uss_size)
-{
-	int ret;
-
-	/* Check if we're in firmware mode first */
-	ret = tkey_in_app_mode(dev);
-	if (ret < 0) {
-		log_debug("Failed to check device mode (error %d)\n", ret);
-		return ret;
+	/* Check if we're done */
+	if (ctx->offset >= ctx->app_size) {
+		log_debug("App data loaded successfully\n");
+		return 0;  /* Done */
 	}
 
-	if (ret) {
-		log_debug("Device must be in firmware mode to load app\n");
-		return -ENOTSUPP;
-	}
-
-	log_debug("Loading app (%u bytes)...\n", app_size);
-
-	/* Send app header with size and USS (if provided) */
-	ret = tkey_load_app_header(dev, app_size, uss, uss_size);
-	if (ret) {
-		log_debug("Failed to send app header (error %d)\n", ret);
-		return ret;
-	}
-
-	/* Send app data */
-	ret = tkey_load_app_data(dev, app_data, app_size);
-	if (ret) {
-		log_debug("Failed to send app data (error %d)\n", ret);
-		return ret;
-	}
-
-	log_debug("App loaded successfully\n");
-
-	return 0;
-}
-
-int tkey_load_app(struct udevice *dev, const void *app_data, int app_size)
-{
-	return tkey_load_app_with_uss(dev, app_data, app_size, NULL, 0);
+	return -EAGAIN;  /* More blocks remain */
 }
 
 int tkey_get_pubkey(struct udevice *dev, void *pubkey)
diff --git a/include/tkey.h b/include/tkey.h
index 14ad3ebc9e9..1c66dab8484 100644
--- a/include/tkey.h
+++ b/include/tkey.h
@@ -13,6 +13,21 @@ 
 struct tkey_frame;
 struct udevice;
 
+/**
+ * struct tkey_load_ctx - Context for iterative app loading
+ *
+ * @dev: TKey device
+ * @app_data: Complete app binary data
+ * @app_size: Size of app data
+ * @offset: Current offset in app data
+ */
+struct tkey_load_ctx {
+	struct udevice *dev;
+	const void *app_data;
+	int app_size;
+	int offset;
+};
+
 /* TKey constants */
 #define TKEY_NAME_SIZE			5
 #define TKEY_CDI_SIZE			32
@@ -182,6 +197,40 @@  int tkey_load_app(struct udevice *dev, const void *app_data, int app_size);
 int tkey_load_app_with_uss(struct udevice *dev, const void *app_data,
 			   int app_size, const void *uss, int uss_size);
 
+/**
+ * tkey_load_start() - Start iterative app loading
+ *
+ * @ctx: Context structure to initialize
+ * @dev: TKey device
+ * @app_data: Complete app binary data
+ * @app_size: Size of app data
+ * @uss: User-Supplied Secret (password/passphrase) - can be NULL
+ * @uss_size: Size of USS data (max 32 bytes)
+ *
+ * This function initializes the context and sends the app header.
+ * Call tkey_load_next() repeatedly to send the data blocks.
+ *
+ * Return: 0 on success, -ve error on failure (-ENOTSUPP if not in
+ *	firmware mode)
+ */
+int tkey_load_start(struct tkey_load_ctx *ctx, struct udevice *dev,
+		    const void *app_data, int app_size,
+		    const void *uss, int uss_size);
+
+/**
+ * tkey_load_next() - Send next block(s) of app data
+ *
+ * @ctx: Context structure from tkey_load_start()
+ * @max_blocks: Maximum number of blocks to send (0 = send all remaining)
+ *
+ * This function sends the next n blocks of app data. Call repeatedly
+ * until it returns 0 (done) instead of -EAGAIN (more blocks remain).
+ *
+ * Return: 0 if loading is complete, -EAGAIN if more blocks remain,
+ *	-ve error on failure
+ */
+int tkey_load_next(struct tkey_load_ctx *ctx, int max_blocks);
+
 /**
  * tkey_get_pubkey() - Get public key from signer app
  *