{"id":2235251,"url":"http://patchwork.ozlabs.org/api/1.2/patches/2235251/?format=json","web_url":"http://patchwork.ozlabs.org/project/uboot/patch/154381187d2e0afe728cb222a71318080e2d6a8e.1778277334.git.aidan@wolfssl.com/","project":{"id":18,"url":"http://patchwork.ozlabs.org/api/1.2/projects/18/?format=json","name":"U-Boot","link_name":"uboot","list_id":"u-boot.lists.denx.de","list_email":"u-boot@lists.denx.de","web_url":null,"scm_url":null,"webscm_url":null,"list_archive_url":"","list_archive_url_format":"","commit_url_format":""},"msgid":"<154381187d2e0afe728cb222a71318080e2d6a8e.1778277334.git.aidan@wolfssl.com>","list_archive_url":null,"date":"2026-05-09T00:04:17","name":"[v3,10/12] test: add wolfTPM C unit tests and Python integration tests","commit_ref":null,"pull_url":null,"state":"new","archived":false,"hash":"de6dc17b8198ce1f28e46b080f7b4e00d4edf639","submitter":{"id":92785,"url":"http://patchwork.ozlabs.org/api/1.2/people/92785/?format=json","name":"Aidan Garske","email":"aidan@wolfssl.com"},"delegate":null,"mbox":"http://patchwork.ozlabs.org/project/uboot/patch/154381187d2e0afe728cb222a71318080e2d6a8e.1778277334.git.aidan@wolfssl.com/mbox/","series":[{"id":503464,"url":"http://patchwork.ozlabs.org/api/1.2/series/503464/?format=json","web_url":"http://patchwork.ozlabs.org/project/uboot/list/?series=503464","date":"2026-05-09T00:04:07","name":"tpm: Add wolfTPM library support for TPM 2.0","version":3,"mbox":"http://patchwork.ozlabs.org/series/503464/mbox/"}],"comments":"http://patchwork.ozlabs.org/api/patches/2235251/comments/","check":"pending","checks":"http://patchwork.ozlabs.org/api/patches/2235251/checks/","tags":{},"related":[],"headers":{"Return-Path":"<u-boot-bounces@lists.denx.de>","X-Original-To":"incoming@patchwork.ozlabs.org","Delivered-To":"patchwork-incoming@legolas.ozlabs.org","Authentication-Results":["legolas.ozlabs.org;\n\tdkim=pass (2048-bit key;\n unprotected) header.d=wolfssl-com.20251104.gappssmtp.com\n header.i=@wolfssl-com.20251104.gappssmtp.com header.a=rsa-sha256\n header.s=20251104 header.b=MRsizRm3;\n\tdkim-atps=neutral","legolas.ozlabs.org;\n spf=pass (sender SPF authorized) smtp.mailfrom=lists.denx.de\n (client-ip=2a01:238:438b:c500:173d:9f52:ddab:ee01; helo=phobos.denx.de;\n envelope-from=u-boot-bounces@lists.denx.de; receiver=patchwork.ozlabs.org)","phobos.denx.de;\n dmarc=fail (p=none dis=none) header.from=wolfssl.com","phobos.denx.de;\n spf=pass smtp.mailfrom=u-boot-bounces@lists.denx.de","phobos.denx.de;\n\tdkim=pass (2048-bit key;\n unprotected) header.d=wolfssl-com.20251104.gappssmtp.com\n header.i=@wolfssl-com.20251104.gappssmtp.com header.b=\"MRsizRm3\";\n\tdkim-atps=neutral","phobos.denx.de;\n dmarc=fail (p=none dis=none) header.from=wolfssl.com","phobos.denx.de;\n spf=pass smtp.mailfrom=aidan@wolfssl.com"],"Received":["from phobos.denx.de (phobos.denx.de\n [IPv6:2a01:238:438b:c500:173d:9f52:ddab:ee01])\n\t(using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits)\n\t key-exchange x25519)\n\t(No client certificate requested)\n\tby legolas.ozlabs.org (Postfix) with ESMTPS id 4gC6cz2b3qz1yCg\n\tfor <incoming@patchwork.ozlabs.org>; Sat, 09 May 2026 10:41:55 +1000 (AEST)","from h2850616.stratoserver.net (localhost [IPv6:::1])\n\tby phobos.denx.de (Postfix) with ESMTP id BF55F84E3F;\n\tSat,  9 May 2026 02:40:16 +0200 (CEST)","by phobos.denx.de (Postfix, from userid 109)\n id 5370A84E0E; Sat,  9 May 2026 02:04:57 +0200 (CEST)","from mail-dl1-x122d.google.com (mail-dl1-x122d.google.com\n [IPv6:2607:f8b0:4864:20::122d])\n (using TLSv1.3 with cipher TLS_AES_128_GCM_SHA256 (128/128 bits))\n (No client certificate requested)\n by phobos.denx.de (Postfix) with ESMTPS id 12AE584E0B\n for <u-boot@lists.denx.de>; Sat,  9 May 2026 02:04:54 +0200 (CEST)","by mail-dl1-x122d.google.com with SMTP id\n a92af1059eb24-12c45281a06so4487499c88.1\n for <u-boot@lists.denx.de>; Fri, 08 May 2026 17:04:53 -0700 (PDT)","from localhost.localdomain ([207.231.76.218])\n by smtp.gmail.com with ESMTPSA id\n a92af1059eb24-132787673ffsm5505030c88.15.2026.05.08.17.04.50\n (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256);\n Fri, 08 May 2026 17:04:51 -0700 (PDT)"],"X-Spam-Checker-Version":"SpamAssassin 3.4.2 (2018-09-13) on phobos.denx.de","X-Spam-Level":"*","X-Spam-Status":"No, score=1.4 required=5.0 tests=BAYES_00,DKIM_SIGNED,\n DKIM_VALID,RCVD_IN_DNSWL_BLOCKED,RCVD_IN_SBL_CSS,SPF_HELO_NONE,\n SPF_PASS autolearn=no autolearn_force=no version=3.4.2","DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/relaxed;\n d=wolfssl-com.20251104.gappssmtp.com; s=20251104; t=1778285092; x=1778889892;\n darn=lists.denx.de;\n h=content-transfer-encoding:mime-version:references:in-reply-to\n :message-id:date:subject:cc:to:from:from:to:cc:subject:date\n :message-id:reply-to;\n bh=vH8gj1g0+MxJ/S3YofSB0NZBfppseTld1w8z6cEOjf0=;\n b=MRsizRm30a4BEN6lTgI3OBzjLCk8zjn0cOrVSl379uoq0AGHKto4ltRmi+Gre5poX/\n J8otlgLoxU77teUOpLG1HEbFplhu06eudvjSWb8AjQNxMobUx47Oi2v+6W9u3oWE1gGQ\n 59RgMcJjGSOha4L4+IGRFgqpl62OhowN+5VTcTQWOd6kgUaCwpD8FqCc8uBgJzu9kXnf\n ihUx/pwr/w1OKfrF++hqC9ifIS7eo1nBrCIE3HIIjAZD2jGAZLGmaNpCDDjiSrS+lZvq\n EO+qV4N3H98GQilQKHGWVzdv8TqJnJckg4GWfXdfxip8tCGn+OKItfV5ScKslwFNjEJv\n lZaA==","X-Google-DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/relaxed;\n d=1e100.net; s=20251104; t=1778285092; x=1778889892;\n h=content-transfer-encoding:mime-version:references:in-reply-to\n :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from\n :to:cc:subject:date:message-id:reply-to;\n bh=vH8gj1g0+MxJ/S3YofSB0NZBfppseTld1w8z6cEOjf0=;\n b=iTJ6XFR8n4jAxmNtulSQI8dCii4EpFPOJfAD91Zuly+/pYZPxY+ELzczSB6Z/Yzr1u\n Z9v5yPk8hZV/pljhaAOD6ZiFeRJpbGP1gxsh9pHbBW1Ir30kpog4qoxYNHg6ZsMMCdT1\n OSZscuk+2TyQ9AW/9rQqHoWOyBvmZWU7c6jqCrYx6KwruhowT7AfHWAsXUCCI2vOx/Ys\n tsbkA8/YCwKcGV4Albslo4UlsutYSvVzEa6LnFiCzFxDvMbCYvd0Od4FrZRKxErHjC9y\n ZHmv3/o3A+DT08j+di5FKjOdS293qg3zwiKNkH2LCWswBYRXSQaLQ6Ack17batgIwpio\n rCjw==","X-Gm-Message-State":"AOJu0YxZvAuo1dUJLIu52DMIlxKeKeqDW9OPGpRxlDzCaFIVXA9LPCtQ\n MKe2QaRKB3n3SvI7cFa+ucd0BUt1ikdiItoel6JJwWiFB3TVMSWmfSytiaWMEbfFZfjlJTEeEIh\n S/z3/","X-Gm-Gg":"AeBDiev1hPdxoFvQotvwC/6AlNB6UgFHd2U0WSV1f2LViwVjet5NPFr1QOSfIIbM8BL\n NrVUTTU3CQNP6z1WrLLrXGo4gVzEJviGHOvz+PABzRJfTYVoPurviwZDBweAPF2Bt1RxaO8B6zJ\n OIZJ6Pv2jU6Kzji2gL7DfI4hTCylDlEUItQZArlQDYZwk/A82PwCyLV43yghL5u98En0gMZ5LAU\n 7jO3BCsbj7nXHdx7Hq2d49a8iy9QzSHnACybntRhmt6/njU7BoVs5PFHdlxS+0V1p9LwUiwJTID\n 1Q32Ft4XXuKw0i1L8VIPg9O0n4+a99We2nPhFpAgLSocuEES9RuU6X98p3hrvF4j4VV8mMsyUOa\n vk6hrywm4U4fEZ1tHQotIke19jKP61/P6mIrnXFsQcxreOnAkzjL+O/oD++2M61v7E2uQdVXpOX\n xvqNpePn/3Gl7iHleaa4I6P/P5+6qEDciBrxduuuOKXE5ZSP2DDT6Lx9auIHblAn6ST9pZ26lpE\n dn5zAnYAu888V2E7tSrUA==","X-Received":"by 2002:a05:7022:4594:b0:11a:fb3c:568b with SMTP id\n a92af1059eb24-131853e5b5amr6432904c88.17.1778285091490;\n Fri, 08 May 2026 17:04:51 -0700 (PDT)","From":"Aidan Garske <aidan@wolfssl.com>","To":"u-boot@lists.denx.de","Cc":"David Garske <david@wolfssl.com>,\n Ilias Apalodimas <ilias.apalodimas@linaro.org>","Subject":"[PATCH v3 10/12] test: add wolfTPM C unit tests and Python\n integration tests","Date":"Fri,  8 May 2026 17:04:17 -0700","Message-ID":"\n <154381187d2e0afe728cb222a71318080e2d6a8e.1778277334.git.aidan@wolfssl.com>","X-Mailer":"git-send-email 2.47.3","In-Reply-To":"<cover.1778277334.git.aidan@wolfssl.com>","References":"<cover.1778277334.git.aidan@wolfssl.com>","MIME-Version":"1.0","Content-Transfer-Encoding":"8bit","X-Mailman-Approved-At":"Sat, 09 May 2026 02:40:11 +0200","X-BeenThere":"u-boot@lists.denx.de","X-Mailman-Version":"2.1.39","Precedence":"list","List-Id":"U-Boot discussion <u-boot.lists.denx.de>","List-Unsubscribe":"<https://lists.denx.de/options/u-boot>,\n <mailto:u-boot-request@lists.denx.de?subject=unsubscribe>","List-Archive":"<https://lists.denx.de/pipermail/u-boot/>","List-Post":"<mailto:u-boot@lists.denx.de>","List-Help":"<mailto:u-boot-request@lists.denx.de?subject=help>","List-Subscribe":"<https://lists.denx.de/listinfo/u-boot>,\n <mailto:u-boot-request@lists.denx.de?subject=subscribe>","Errors-To":"u-boot-bounces@lists.denx.de","Sender":"\"U-Boot\" <u-boot-bounces@lists.denx.de>","X-Virus-Scanned":"clamav-milter 0.103.8 at phobos.denx.de","X-Virus-Status":"Clean"},"content":"From: Aidan <aidan@wolfssl.com>\n\nAdd comprehensive test suites for wolfTPM commands, modeled after\nthe existing TPM2 test infrastructure.\n\ntest/cmd/wolftpm.c (C unit tests):\n  18 tests using U-Boot's unit test framework (CMD_TEST macro):\n  - autostart, init, info, state, device: basic lifecycle\n  - self_test (full and continue): TPM self-test verification\n  - startup_clear, startup_state: TPM2_Startup modes\n  - get_capability: read TPM properties\n  - caps: wolfTPM-enhanced capabilities display\n  - clear: TPM state reset via LOCKOUT hierarchy\n  - pcr_read, pcr_extend, pcr_print: PCR operations\n  - pcr_allocate: PCR bank reconfiguration\n  - dam_reset, dam_parameters: dictionary attack mitigation\n  - change_auth: hierarchy password change (requires wolfCrypt)\n  - cleanup: reset TPM state after tests\n  Run with: ut cmd cmd_test_wolftpm_*\n\ntest/cmd/Makefile:\n  Adds wolftpm.o when CONFIG_TPM_WOLF is enabled.\n\ntest/py/tests/test_wolftpm.py (Python integration tests):\n  21 tests using pytest with the U-Boot test framework:\n  - Requires QEMU + swtpm (not sandbox) because wolfTPM bypasses\n    U-Boot's driver model and communicates directly with TPM\n    hardware via its own SPI/MMIO HAL\n  - Tests mirror the C tests but run end-to-end through the U-Boot\n    console, checking return codes via 'echo $?'\n  - Includes force_init() helper for TPM reinitialization after\n    test failures\n  - Skippable via env__wolftpm_device_test_skip config\n  - Verified: 19 passed, 2 skipped (change_auth requires wolfCrypt,\n    get_capability may skip on some platforms)\n\nSigned-off-by: Aidan Garske <aidan@wolfssl.com>\n---\n test/cmd/Makefile             |   1 +\n test/cmd/wolftpm.c            | 364 +++++++++++++++++++++++++++++++++\n test/py/tests/test_wolftpm.py | 375 ++++++++++++++++++++++++++++++++++\n 3 files changed, 740 insertions(+)\n create mode 100644 test/cmd/wolftpm.c\n create mode 100644 test/py/tests/test_wolftpm.py","diff":"diff --git a/test/cmd/Makefile b/test/cmd/Makefile\nindex 2476068aee6..08fbc31a06a 100644\n--- a/test/cmd/Makefile\n+++ b/test/cmd/Makefile\n@@ -45,3 +45,4 @@ endif\n obj-$(CONFIG_ARM_FFA_TRANSPORT) += armffa.o\n endif\n obj-$(CONFIG_CMD_SPAWN) += spawn.o\n+obj-$(CONFIG_TPM_WOLF) += wolftpm.o\ndiff --git a/test/cmd/wolftpm.c b/test/cmd/wolftpm.c\nnew file mode 100644\nindex 00000000000..b2e6f82a098\n--- /dev/null\n+++ b/test/cmd/wolftpm.c\n@@ -0,0 +1,364 @@\n+// SPDX-License-Identifier: GPL-2.0+\n+/*\n+ * Tests for wolfTPM commands\n+ *\n+ * Copyright (C) 2025 wolfSSL Inc.\n+ * Author: Aidan Garske <aidan@wolfssl.com>\n+ *\n+ * Based on test/py/tests/test_tpm2.py and test/dm/tpm.c\n+ *\n+ * Note: These tests verify command success via return code only.\n+ * Console output is not checked since it varies with debug levels.\n+ * Run with: ut cmd\n+ */\n+\n+#include <command.h>\n+#include <dm.h>\n+#include <dm/test.h>\n+#include <test/cmd.h>\n+#include <test/test.h>\n+#include <test/ut.h>\n+\n+/**\n+ * Test wolfTPM autostart command\n+ *\n+ * This initializes the TPM, performs startup and self-test\n+ */\n+static int cmd_test_wolftpm_autostart(struct unit_test_state *uts)\n+{\n+\t/* Initialize and autostart the TPM */\n+\tut_assertok(run_command(\"tpm2 autostart\", 0));\n+\n+\treturn 0;\n+}\n+CMD_TEST(cmd_test_wolftpm_autostart, 0);\n+\n+/**\n+ * Test wolfTPM init command\n+ */\n+static int cmd_test_wolftpm_init(struct unit_test_state *uts)\n+{\n+\tut_assertok(run_command(\"tpm2 init\", 0));\n+\n+\treturn 0;\n+}\n+CMD_TEST(cmd_test_wolftpm_init, 0);\n+\n+/**\n+ * Test wolfTPM info command\n+ *\n+ * Display TPM device information\n+ */\n+static int cmd_test_wolftpm_info(struct unit_test_state *uts)\n+{\n+\t/* First autostart to ensure TPM is ready */\n+\tut_assertok(run_command(\"tpm2 autostart\", 0));\n+\n+\t/* Get TPM info */\n+\tut_assertok(run_command(\"tpm2 info\", 0));\n+\n+\treturn 0;\n+}\n+CMD_TEST(cmd_test_wolftpm_info, 0);\n+\n+/**\n+ * Test wolfTPM state command\n+ *\n+ * Display TPM internal state\n+ */\n+static int cmd_test_wolftpm_state(struct unit_test_state *uts)\n+{\n+\t/* First autostart to ensure TPM is ready */\n+\tut_assertok(run_command(\"tpm2 autostart\", 0));\n+\n+\t/* Get TPM state */\n+\tut_assertok(run_command(\"tpm2 state\", 0));\n+\n+\treturn 0;\n+}\n+CMD_TEST(cmd_test_wolftpm_state, 0);\n+\n+/**\n+ * Test wolfTPM device command\n+ *\n+ * Show all TPM devices\n+ */\n+static int cmd_test_wolftpm_device(struct unit_test_state *uts)\n+{\n+\t/* Show TPM devices - no autostart needed */\n+\tut_assertok(run_command(\"tpm2 device\", 0));\n+\n+\treturn 0;\n+}\n+CMD_TEST(cmd_test_wolftpm_device, 0);\n+\n+/**\n+ * Test wolfTPM self_test command\n+ */\n+static int cmd_test_wolftpm_self_test(struct unit_test_state *uts)\n+{\n+\t/* First autostart to ensure TPM is ready */\n+\tut_assertok(run_command(\"tpm2 autostart\", 0));\n+\n+\t/* Run full self test */\n+\tut_assertok(run_command(\"tpm2 self_test full\", 0));\n+\n+\treturn 0;\n+}\n+CMD_TEST(cmd_test_wolftpm_self_test, 0);\n+\n+/**\n+ * Test wolfTPM self_test continue command\n+ */\n+static int cmd_test_wolftpm_self_test_continue(struct unit_test_state *uts)\n+{\n+\t/* First autostart to ensure TPM is ready */\n+\tut_assertok(run_command(\"tpm2 autostart\", 0));\n+\n+\t/* Run continue self test */\n+\tut_assertok(run_command(\"tpm2 self_test continue\", 0));\n+\n+\treturn 0;\n+}\n+CMD_TEST(cmd_test_wolftpm_self_test_continue, 0);\n+\n+/**\n+ * Test wolfTPM startup command with TPM2_SU_CLEAR\n+ *\n+ * Issue TPM2_Startup with CLEAR mode (reset state)\n+ */\n+static int cmd_test_wolftpm_startup_clear(struct unit_test_state *uts)\n+{\n+\t/* First init to prepare TPM */\n+\tut_assertok(run_command(\"tpm2 init\", 0));\n+\n+\t/* Issue startup with CLEAR mode */\n+\tut_assertok(run_command(\"tpm2 startup TPM2_SU_CLEAR\", 0));\n+\n+\treturn 0;\n+}\n+CMD_TEST(cmd_test_wolftpm_startup_clear, 0);\n+\n+/**\n+ * Test wolfTPM startup command with TPM2_SU_STATE\n+ *\n+ * Issue TPM2_Startup with STATE mode (preserved state)\n+ */\n+static int cmd_test_wolftpm_startup_state(struct unit_test_state *uts)\n+{\n+\t/* First autostart to ensure TPM has state */\n+\tut_assertok(run_command(\"tpm2 autostart\", 0));\n+\n+\t/* Shutdown first to prepare for STATE startup */\n+\trun_command(\"tpm2 startup TPM2_SU_STATE off\", 0);\n+\n+\t/* Re-init */\n+\tut_assertok(run_command(\"tpm2 init\", 0));\n+\n+\t/* Issue startup with STATE mode - may return already started */\n+\trun_command(\"tpm2 startup TPM2_SU_STATE\", 0);\n+\n+\treturn 0;\n+}\n+CMD_TEST(cmd_test_wolftpm_startup_state, 0);\n+\n+/**\n+ * Test wolfTPM get_capability command\n+ *\n+ * Read TPM capabilities by property\n+ */\n+static int cmd_test_wolftpm_get_capability(struct unit_test_state *uts)\n+{\n+\t/* First autostart to ensure TPM is ready */\n+\tut_assertok(run_command(\"tpm2 autostart\", 0));\n+\n+\t/* Get capability - property 0x6 (TPM_CAP_TPM_PROPERTIES), 0x20e (PT_MANUFACTURER) */\n+\tut_assertok(run_command(\"tpm2 get_capability 0x6 0x20e 0x1000000 1\", 0));\n+\n+\treturn 0;\n+}\n+CMD_TEST(cmd_test_wolftpm_get_capability, 0);\n+\n+/**\n+ * Test wolfTPM caps command (get capabilities)\n+ *\n+ * Display TPM capabilities and vendor info\n+ */\n+static int cmd_test_wolftpm_caps(struct unit_test_state *uts)\n+{\n+\t/* First autostart to ensure TPM is ready */\n+\tut_assertok(run_command(\"tpm2 autostart\", 0));\n+\n+\t/* Get TPM capabilities */\n+\tut_assertok(run_command(\"tpm2 caps\", 0));\n+\n+\treturn 0;\n+}\n+CMD_TEST(cmd_test_wolftpm_caps, 0);\n+\n+/**\n+ * Test wolfTPM clear command\n+ *\n+ * Reset TPM internal state using LOCKOUT hierarchy\n+ */\n+static int cmd_test_wolftpm_clear(struct unit_test_state *uts)\n+{\n+\t/* First autostart to ensure TPM is ready */\n+\tut_assertok(run_command(\"tpm2 autostart\", 0));\n+\n+\t/* Clear using LOCKOUT hierarchy */\n+\tut_assertok(run_command(\"tpm2 clear TPM2_RH_LOCKOUT\", 0));\n+\n+\treturn 0;\n+}\n+CMD_TEST(cmd_test_wolftpm_clear, 0);\n+\n+/**\n+ * Test wolfTPM pcr_read command\n+ *\n+ * Read PCR value from a specific index to a memory address\n+ */\n+static int cmd_test_wolftpm_pcr_read(struct unit_test_state *uts)\n+{\n+\t/* First autostart to ensure TPM is ready */\n+\tut_assertok(run_command(\"tpm2 autostart\", 0));\n+\n+\t/* Read PCR 0 with SHA256 to memory address 0x1000000 */\n+\tut_assertok(run_command(\"tpm2 pcr_read 0 0x1000000 SHA256\", 0));\n+\n+\treturn 0;\n+}\n+CMD_TEST(cmd_test_wolftpm_pcr_read, 0);\n+\n+/**\n+ * Test wolfTPM pcr_extend command\n+ *\n+ * Extend a PCR with a digest value\n+ */\n+static int cmd_test_wolftpm_pcr_extend(struct unit_test_state *uts)\n+{\n+\t/* First autostart to ensure TPM is ready */\n+\tut_assertok(run_command(\"tpm2 autostart\", 0));\n+\n+\t/* Clear to start fresh */\n+\trun_command(\"tpm2 clear TPM2_RH_LOCKOUT\", 0);\n+\n+\t/* Extend PCR 16 (resettable PCR) with digest from memory\n+\t * PCR 16-23 are typically available for debug/testing\n+\t */\n+\tut_assertok(run_command(\"tpm2 pcr_extend 16 0x1000000 SHA256\", 0));\n+\n+\treturn 0;\n+}\n+CMD_TEST(cmd_test_wolftpm_pcr_extend, 0);\n+\n+/**\n+ * Test wolfTPM pcr_print command\n+ *\n+ * Print all PCR values\n+ */\n+static int cmd_test_wolftpm_pcr_print(struct unit_test_state *uts)\n+{\n+\t/* First autostart to ensure TPM is ready */\n+\tut_assertok(run_command(\"tpm2 autostart\", 0));\n+\n+\t/* Print all PCRs */\n+\tut_assertok(run_command(\"tpm2 pcr_print\", 0));\n+\n+\treturn 0;\n+}\n+CMD_TEST(cmd_test_wolftpm_pcr_print, 0);\n+\n+/**\n+ * Test wolfTPM pcr_allocate command\n+ *\n+ * Reconfigure PCR bank algorithm. Note: A TPM restart is required\n+ * for changes to take effect, so we just verify the command succeeds.\n+ */\n+static int cmd_test_wolftpm_pcr_allocate(struct unit_test_state *uts)\n+{\n+\t/* First autostart to ensure TPM is ready */\n+\tut_assertok(run_command(\"tpm2 autostart\", 0));\n+\n+\t/* Allocate SHA256 bank on - this should succeed */\n+\tut_assertok(run_command(\"tpm2 pcr_allocate SHA256 on\", 0));\n+\n+\treturn 0;\n+}\n+CMD_TEST(cmd_test_wolftpm_pcr_allocate, 0);\n+\n+/**\n+ * Test wolfTPM dam_reset command\n+ *\n+ * Reset Dictionary Attack Mitigation counter\n+ */\n+static int cmd_test_wolftpm_dam_reset(struct unit_test_state *uts)\n+{\n+\t/* First autostart to ensure TPM is ready */\n+\tut_assertok(run_command(\"tpm2 autostart\", 0));\n+\n+\t/* Reset DAM counter */\n+\tut_assertok(run_command(\"tpm2 dam_reset\", 0));\n+\n+\treturn 0;\n+}\n+CMD_TEST(cmd_test_wolftpm_dam_reset, 0);\n+\n+/**\n+ * Test wolfTPM dam_parameters command\n+ *\n+ * Set Dictionary Attack Mitigation parameters\n+ */\n+static int cmd_test_wolftpm_dam_parameters(struct unit_test_state *uts)\n+{\n+\t/* First autostart to ensure TPM is ready */\n+\tut_assertok(run_command(\"tpm2 autostart\", 0));\n+\n+\t/* Set DAM parameters:\n+\t * - max_tries: 3\n+\t * - recovery_time: 10 seconds\n+\t * - lockout_recovery: 0 seconds\n+\t */\n+\tut_assertok(run_command(\"tpm2 dam_parameters 3 10 0\", 0));\n+\n+\treturn 0;\n+}\n+CMD_TEST(cmd_test_wolftpm_dam_parameters, 0);\n+\n+/**\n+ * Test wolfTPM change_auth command\n+ *\n+ * Change hierarchy authorization password\n+ * Note: Requires WOLFTPM2_NO_WOLFCRYPT to NOT be defined\n+ */\n+static int cmd_test_wolftpm_change_auth(struct unit_test_state *uts)\n+{\n+\t/* First autostart and clear to ensure clean state */\n+\tut_assertok(run_command(\"tpm2 autostart\", 0));\n+\trun_command(\"tpm2 clear TPM2_RH_LOCKOUT\", 0);\n+\n+\t/* Change LOCKOUT password to \"testpw\"\n+\t * This may fail if WOLFTPM2_NO_WOLFCRYPT is defined\n+\t */\n+\tif (run_command(\"tpm2 change_auth TPM2_RH_LOCKOUT testpw\", 0) == 0) {\n+\t\t/* Clear with new password to verify it worked */\n+\t\tut_assertok(run_command(\"tpm2 clear TPM2_RH_LOCKOUT testpw\", 0));\n+\t}\n+\n+\treturn 0;\n+}\n+CMD_TEST(cmd_test_wolftpm_change_auth, 0);\n+\n+/**\n+ * Cleanup test - ensure TPM is cleared after tests\n+ */\n+static int cmd_test_wolftpm_cleanup(struct unit_test_state *uts)\n+{\n+\t/* Clear TPM to reset any passwords or test state */\n+\trun_command(\"tpm2 autostart\", 0);\n+\trun_command(\"tpm2 clear TPM2_RH_LOCKOUT\", 0);\n+\trun_command(\"tpm2 clear TPM2_RH_PLATFORM\", 0);\n+\n+\treturn 0;\n+}\n+CMD_TEST(cmd_test_wolftpm_cleanup, 0);\ndiff --git a/test/py/tests/test_wolftpm.py b/test/py/tests/test_wolftpm.py\nnew file mode 100644\nindex 00000000000..b862fa06c5b\n--- /dev/null\n+++ b/test/py/tests/test_wolftpm.py\n@@ -0,0 +1,375 @@\n+# SPDX-License-Identifier: GPL-2.0+\n+# Copyright (C) 2025 wolfSSL Inc.\n+# Author: Aidan Garske <aidan@wolfssl.com>\n+#\n+# Based on test_tpm2.py by Miquel Raynal <miquel.raynal@bootlin.com>\n+\n+\"\"\"\n+Test the wolfTPM related commands. These tests require a TPM device\n+(real hardware or software TPM emulator like swtpm).\n+\n+Notes:\n+* These tests will prove the password mechanism. The TPM chip must be cleared of\n+  any password.\n+* Tests are designed to be similar to test_tpm2.py but use wolfTPM wrapper APIs.\n+\n+Configuration:\n+* Set env__wolftpm_device_test_skip to True to skip these tests.\n+\"\"\"\n+\n+import os.path\n+import pytest\n+import utils\n+import re\n+import time\n+\n+\n+def force_init(ubman, force=False):\n+    \"\"\"Initialize wolfTPM before running tests.\n+\n+    When a test fails, U-Boot may be reset. Because TPM stack must be initialized\n+    after each reboot, we must ensure these lines are always executed before\n+    trying any command or they will fail with no reason.\n+    \"\"\"\n+    skip_test = ubman.config.env.get('env__wolftpm_device_test_skip', False)\n+    if skip_test:\n+        pytest.skip('skip wolfTPM device test')\n+    output = ubman.run_command('tpm2 autostart')\n+    if force or 'Error' not in output:\n+        ubman.run_command('echo --- start of init ---')\n+        ubman.run_command('tpm2 clear TPM2_RH_LOCKOUT')\n+        output = ubman.run_command('echo $?')\n+        if not output.endswith('0'):\n+            ubman.run_command('tpm2 clear TPM2_RH_PLATFORM')\n+        ubman.run_command('echo --- end of init ---')\n+\n+\n+@pytest.mark.buildconfigspec('tpm_wolf')\n+def test_wolftpm_autostart(ubman):\n+    \"\"\"Test wolfTPM autostart command.\n+\n+    Initialize the software stack, perform startup and self-test.\n+    \"\"\"\n+    skip_test = ubman.config.env.get('env__wolftpm_device_test_skip', False)\n+    if skip_test:\n+        pytest.skip('skip wolfTPM device test')\n+    ubman.run_command('tpm2 autostart')\n+    output = ubman.run_command('echo $?')\n+    assert output.endswith('0')\n+\n+\n+@pytest.mark.buildconfigspec('tpm_wolf')\n+def test_wolftpm_init(ubman):\n+    \"\"\"Test wolfTPM init command.\n+\n+    Initialize the TPM device for communication.\n+    \"\"\"\n+    skip_test = ubman.config.env.get('env__wolftpm_device_test_skip', False)\n+    if skip_test:\n+        pytest.skip('skip wolfTPM device test')\n+    ubman.run_command('tpm2 init')\n+    output = ubman.run_command('echo $?')\n+    assert output.endswith('0')\n+\n+\n+@pytest.mark.buildconfigspec('tpm_wolf')\n+def test_wolftpm_self_test_full(ubman):\n+    \"\"\"Test wolfTPM full self_test command.\n+\n+    Perform a full TPM self-test to verify all components are operational.\n+    \"\"\"\n+    skip_test = ubman.config.env.get('env__wolftpm_device_test_skip', False)\n+    if skip_test:\n+        pytest.skip('skip wolfTPM device test')\n+    ubman.run_command('tpm2 autostart')\n+    ubman.run_command('tpm2 self_test full')\n+    output = ubman.run_command('echo $?')\n+    assert output.endswith('0')\n+\n+\n+@pytest.mark.buildconfigspec('tpm_wolf')\n+def test_wolftpm_self_test_continue(ubman):\n+    \"\"\"Test wolfTPM continue self_test command.\n+\n+    Ask the TPM to finish any remaining self tests.\n+    \"\"\"\n+    skip_test = ubman.config.env.get('env__wolftpm_device_test_skip', False)\n+    if skip_test:\n+        pytest.skip('skip wolfTPM device test')\n+    ubman.run_command('tpm2 autostart')\n+    ubman.run_command('tpm2 self_test continue')\n+    output = ubman.run_command('echo $?')\n+    assert output.endswith('0')\n+\n+\n+@pytest.mark.buildconfigspec('tpm_wolf')\n+def test_wolftpm_caps(ubman):\n+    \"\"\"Test wolfTPM caps command.\n+\n+    Display TPM capabilities and vendor information.\n+    \"\"\"\n+    force_init(ubman)\n+    ubman.run_command('tpm2 caps')\n+    output = ubman.run_command('echo $?')\n+    assert output.endswith('0')\n+\n+\n+@pytest.mark.buildconfigspec('tpm_wolf')\n+def test_wolftpm_clear(ubman):\n+    \"\"\"Test wolfTPM clear command.\n+\n+    Clear the TPM internal state using LOCKOUT hierarchy.\n+    LOCKOUT/PLATFORM hierarchies must not have a password set.\n+    \"\"\"\n+    skip_test = ubman.config.env.get('env__wolftpm_device_test_skip', False)\n+    if skip_test:\n+        pytest.skip('skip wolfTPM device test')\n+    ubman.run_command('tpm2 autostart')\n+    ubman.run_command('tpm2 clear TPM2_RH_LOCKOUT')\n+    output = ubman.run_command('echo $?')\n+    assert output.endswith('0')\n+\n+    ubman.run_command('tpm2 clear TPM2_RH_PLATFORM')\n+    output = ubman.run_command('echo $?')\n+    assert output.endswith('0')\n+\n+\n+@pytest.mark.buildconfigspec('tpm_wolf')\n+def test_wolftpm_change_auth(ubman):\n+    \"\"\"Test wolfTPM change_auth command.\n+\n+    Change the owner/hierarchy password.\n+    \"\"\"\n+    force_init(ubman)\n+\n+    # Change LOCKOUT password to 'unicorn'\n+    # Note: change_auth requires wolfCrypt (WOLFTPM2_NO_WOLFCRYPT must not be set)\n+    ubman.run_command('tpm2 change_auth TPM2_RH_LOCKOUT unicorn')\n+    output = ubman.run_command('echo $?')\n+    if not output.endswith('0'):\n+        # wolfCrypt not available, skip password test\n+        pytest.skip('change_auth requires wolfCrypt support')\n+\n+    # Clear with new password to verify\n+    ubman.run_command('tpm2 clear TPM2_RH_LOCKOUT unicorn')\n+    output = ubman.run_command('echo $?')\n+    ubman.run_command('tpm2 clear TPM2_RH_PLATFORM')\n+    assert output.endswith('0')\n+\n+\n+@pytest.mark.buildconfigspec('tpm_wolf')\n+def test_wolftpm_dam_parameters(ubman):\n+    \"\"\"Test wolfTPM dam_parameters command.\n+\n+    Change Dictionary Attack Mitigation parameters:\n+    - Max number of failed authentication before lockout: 3\n+    - Time before failure counter is decremented: 10 sec\n+    - Time after lockout failure before retry: 0 sec\n+    \"\"\"\n+    force_init(ubman)\n+\n+    # Set DAM parameters\n+    ubman.run_command('tpm2 dam_parameters 3 10 0')\n+    output = ubman.run_command('echo $?')\n+    assert output.endswith('0')\n+\n+\n+@pytest.mark.buildconfigspec('tpm_wolf')\n+def test_wolftpm_dam_reset(ubman):\n+    \"\"\"Test wolfTPM dam_reset command.\n+\n+    Reset the Dictionary Attack Mitigation counter.\n+    \"\"\"\n+    force_init(ubman)\n+\n+    ubman.run_command('tpm2 dam_reset')\n+    output = ubman.run_command('echo $?')\n+    assert output.endswith('0')\n+\n+\n+@pytest.mark.buildconfigspec('tpm_wolf')\n+def test_wolftpm_pcr_read(ubman):\n+    \"\"\"Test wolfTPM pcr_read command.\n+\n+    Read PCR value from a specific index.\n+    \"\"\"\n+    force_init(ubman)\n+\n+    ram = utils.find_ram_base(ubman)\n+\n+    # Read PCR 0 with SHA256\n+    read_pcr = ubman.run_command('tpm2 pcr_read 0 0x%x SHA256' % ram)\n+    output = ubman.run_command('echo $?')\n+    assert output.endswith('0')\n+\n+\n+@pytest.mark.buildconfigspec('tpm_wolf')\n+def test_wolftpm_pcr_extend(ubman):\n+    \"\"\"Test wolfTPM pcr_extend command.\n+\n+    Extend a PCR with a digest value.\n+    PCR 16-23 are typically available for debug/testing.\n+    \"\"\"\n+    force_init(ubman)\n+    ram = utils.find_ram_base(ubman)\n+\n+    # Read PCR 16 first\n+    read_pcr = ubman.run_command('tpm2 pcr_read 16 0x%x SHA256' % ram)\n+    output = ubman.run_command('echo $?')\n+    assert output.endswith('0')\n+\n+    # Extend PCR 16 with zeroed memory\n+    ubman.run_command('tpm2 pcr_extend 16 0x%x SHA256' % ram)\n+    output = ubman.run_command('echo $?')\n+    assert output.endswith('0')\n+\n+    # Read again to verify it changed\n+    read_pcr_after = ubman.run_command('tpm2 pcr_read 16 0x%x SHA256' % ram)\n+    output = ubman.run_command('echo $?')\n+    assert output.endswith('0')\n+\n+\n+@pytest.mark.buildconfigspec('tpm_wolf')\n+def test_wolftpm_pcr_print(ubman):\n+    \"\"\"Test wolfTPM pcr_print command.\n+\n+    Print all assigned PCRs.\n+    \"\"\"\n+    force_init(ubman)\n+\n+    pcr_output = ubman.run_command('tpm2 pcr_print')\n+    output = ubman.run_command('echo $?')\n+    assert output.endswith('0')\n+\n+    # Should contain PCR info\n+    assert 'PCR' in pcr_output or 'Assigned' in pcr_output\n+\n+\n+@pytest.mark.buildconfigspec('tpm_wolf')\n+def test_wolftpm_info(ubman):\n+    \"\"\"Test wolfTPM info command.\n+\n+    Display TPM device information.\n+    \"\"\"\n+    force_init(ubman)\n+\n+    ubman.run_command('tpm2 info')\n+    output = ubman.run_command('echo $?')\n+    assert output.endswith('0')\n+\n+\n+@pytest.mark.buildconfigspec('tpm_wolf')\n+def test_wolftpm_state(ubman):\n+    \"\"\"Test wolfTPM state command.\n+\n+    Display TPM internal state.\n+    \"\"\"\n+    force_init(ubman)\n+\n+    ubman.run_command('tpm2 state')\n+    output = ubman.run_command('echo $?')\n+    assert output.endswith('0')\n+\n+\n+@pytest.mark.buildconfigspec('tpm_wolf')\n+def test_wolftpm_device(ubman):\n+    \"\"\"Test wolfTPM device command.\n+\n+    Show all TPM devices.\n+    \"\"\"\n+    skip_test = ubman.config.env.get('env__wolftpm_device_test_skip', False)\n+    if skip_test:\n+        pytest.skip('skip wolfTPM device test')\n+    ubman.run_command('tpm2 device')\n+    output = ubman.run_command('echo $?')\n+    assert output.endswith('0')\n+\n+\n+@pytest.mark.buildconfigspec('tpm_wolf')\n+def test_wolftpm_startup_clear(ubman):\n+    \"\"\"Test wolfTPM startup command with TPM2_SU_CLEAR.\n+\n+    Issue TPM2_Startup with CLEAR mode (reset state).\n+    \"\"\"\n+    skip_test = ubman.config.env.get('env__wolftpm_device_test_skip', False)\n+    if skip_test:\n+        pytest.skip('skip wolfTPM device test')\n+    ubman.run_command('tpm2 init')\n+    ubman.run_command('tpm2 startup TPM2_SU_CLEAR')\n+    output = ubman.run_command('echo $?')\n+    assert output.endswith('0')\n+\n+\n+@pytest.mark.buildconfigspec('tpm_wolf')\n+def test_wolftpm_startup_state(ubman):\n+    \"\"\"Test wolfTPM startup command with TPM2_SU_STATE.\n+\n+    Issue TPM2_Startup with STATE mode (preserved state).\n+    \"\"\"\n+    skip_test = ubman.config.env.get('env__wolftpm_device_test_skip', False)\n+    if skip_test:\n+        pytest.skip('skip wolfTPM device test')\n+    # First autostart to have valid state\n+    ubman.run_command('tpm2 autostart')\n+    # Shutdown with STATE\n+    ubman.run_command('tpm2 startup TPM2_SU_STATE off')\n+    # Re-init\n+    ubman.run_command('tpm2 init')\n+    # Startup with STATE - may return already started\n+    ubman.run_command('tpm2 startup TPM2_SU_STATE')\n+    output = ubman.run_command('echo $?')\n+    # May return non-zero if already started, just verify command ran\n+    assert output is not None\n+\n+\n+@pytest.mark.buildconfigspec('tpm_wolf')\n+def test_wolftpm_startup_shutdown(ubman):\n+    \"\"\"Test wolfTPM startup shutdown command.\n+\n+    Issue TPM2_Shutdown.\n+    \"\"\"\n+    skip_test = ubman.config.env.get('env__wolftpm_device_test_skip', False)\n+    if skip_test:\n+        pytest.skip('skip wolfTPM device test')\n+    ubman.run_command('tpm2 autostart')\n+    ubman.run_command('tpm2 startup TPM2_SU_CLEAR off')\n+    output = ubman.run_command('echo $?')\n+    assert output.endswith('0')\n+\n+\n+@pytest.mark.buildconfigspec('tpm_wolf')\n+def test_wolftpm_get_capability(ubman):\n+    \"\"\"Test wolfTPM get_capability command.\n+\n+    Read TPM capabilities by property.\n+    \"\"\"\n+    force_init(ubman)\n+    ram = utils.find_ram_base(ubman)\n+\n+    # Get capability - TPM_CAP_TPM_PROPERTIES (0x6), PT_MANUFACTURER (0x20e)\n+    ubman.run_command('tpm2 get_capability 0x6 0x20e 0x%x 1' % ram)\n+    output = ubman.run_command('echo $?')\n+    # May fail on some platforms if RAM address is not accessible\n+    if not output.endswith('0'):\n+        pytest.skip('get_capability failed (RAM address may not be accessible)')\n+\n+\n+@pytest.mark.buildconfigspec('tpm_wolf')\n+def test_wolftpm_pcr_allocate(ubman):\n+    \"\"\"Test wolfTPM pcr_allocate command.\n+\n+    Reconfigure PCR bank algorithm.\n+    Note: A TPM restart is required for changes to take effect.\n+    \"\"\"\n+    force_init(ubman)\n+\n+    # Allocate SHA256 bank on\n+    ubman.run_command('tpm2 pcr_allocate SHA256 on')\n+    output = ubman.run_command('echo $?')\n+    assert output.endswith('0')\n+\n+\n+@pytest.mark.buildconfigspec('tpm_wolf')\n+def test_wolftpm_cleanup(ubman):\n+    \"\"\"Cleanup test - ensure TPM is cleared after tests.\"\"\"\n+    force_init(ubman, True)\n","prefixes":["v3","10/12"]}