From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from phobos.denx.de (phobos.denx.de [85.214.62.61]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.lore.kernel.org (Postfix) with ESMTPS id 8A946D46BE5 for ; Wed, 28 Jan 2026 17:49:11 +0000 (UTC) Received: from h2850616.stratoserver.net (localhost [IPv6:::1]) by phobos.denx.de (Postfix) with ESMTP id F313F83DE0; Wed, 28 Jan 2026 18:49:09 +0100 (CET) Authentication-Results: phobos.denx.de; dmarc=fail (p=none dis=none) header.from=wolfssl.com Authentication-Results: phobos.denx.de; spf=pass smtp.mailfrom=u-boot-bounces@lists.denx.de Authentication-Results: phobos.denx.de; dkim=pass (2048-bit key; unprotected) header.d=wolfssl-com.20230601.gappssmtp.com header.i=@wolfssl-com.20230601.gappssmtp.com header.b="kwduLqFa"; dkim-atps=neutral Received: by phobos.denx.de (Postfix, from userid 109) id 5D9AE84099; Wed, 28 Jan 2026 18:11:27 +0100 (CET) Received: from mail-dl1-x122e.google.com (mail-dl1-x122e.google.com [IPv6:2607:f8b0:4864:20::122e]) (using TLSv1.3 with cipher TLS_AES_128_GCM_SHA256 (128/128 bits)) (No client certificate requested) by phobos.denx.de (Postfix) with ESMTPS id 700C18401A for ; Wed, 28 Jan 2026 18:11:20 +0100 (CET) Authentication-Results: phobos.denx.de; dmarc=fail (p=none dis=none) header.from=wolfssl.com Authentication-Results: phobos.denx.de; spf=pass smtp.mailfrom=david@wolfssl.com Received: by mail-dl1-x122e.google.com with SMTP id a92af1059eb24-12336c0a8b6so3065670c88.1 for ; Wed, 28 Jan 2026 09:11:20 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=wolfssl-com.20230601.gappssmtp.com; s=20230601; t=1769620278; x=1770225078; darn=lists.denx.de; h=content-transfer-encoding:mime-version:message-id:date:subject:cc :to:from:from:to:cc:subject:date:message-id:reply-to; bh=jITWFHOQkJCHcCVlb2QVuam5o7ibKa1HfjpH8CHGQO8=; b=kwduLqFaR0UGveN5JjtSOe6bObt5f4G91QFzcrr3FfVc1ZneEVgkpGJXKzJRKdGIRt bhTGvWMrLrfXhQx+L82LZKCxF7LbmNDOgHHZMy/VCguWaMgDqP27WxAsEYfjsaMrljiy 36gBwuTifGZoGx91MPHNhDvFAv14JqQT05+0k90axbzipf6Mu8E54qYj7vQUM1QvOr4B czvkOoIAxpSMZUpQmNB1nD1pThW5LSCm5ZYD1vzqeLdpeG0l65+i7qvEcYYWRgwSedzD /whHTxq2uaNbYpWux+AtWX4CoJ5aqt0ZFe/xF9bliPCvRaRwnoFv0Uvh3CbjpzkJDFy3 UpHA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1769620278; x=1770225078; h=content-transfer-encoding:mime-version:message-id:date:subject:cc :to:from:x-gm-gg:x-gm-message-state:from:to:cc:subject:date :message-id:reply-to; bh=jITWFHOQkJCHcCVlb2QVuam5o7ibKa1HfjpH8CHGQO8=; b=GWnsjk111OLJfBcjBy0ag/JQnVqv39niLAFIw2iEo1ZP4sfc/YI9Q0N1TExtdw2KMA lI60OJpEPuoi8HL9ZNBJsJbDMGp99Pp2jH1YR1PnEv4issnm1DzL7r7gh2xHosXnksNp ZDak1n9/1aR8CAcKujT+krShNlnrqrr9KdnVfUi5Fgnjd6ATlofLjbk1dymQVRDa+ooM GQp5Vlg47uNpJdg2/TFyDTOTQ0OUwProy91+bCtUvamlQb9Ip0fXBHm7c9QfjVEbFKS3 Ya0GL+xYii01Ni0MNimOYLv+R9IVWjve5m2RJZv0GEkGc9TKf5LR5aUUIRpBDKQ1Lncd nysQ== X-Gm-Message-State: AOJu0YyiFjgTSIY+XfJ/dlVW0vd+JS/oshP3ZrHarrEcuPOA18sRaiRb rIcnOLfHaBiYTQ0DcxUVD2u5d2EySttHr7TF0U/1A9tOo/vlOC30xh46a5d2VRqVJfCHrAKZY6v 2/Lur X-Gm-Gg: AZuq6aLKtNLraOL5R96eah8KFKl2kf6HrsDa+yjtAmiBc6mZhBbxwuwpJXI661RqjPF JcrGqIDEBu5jZ7UDBsDCHEUt6fdz7AIA0dLtOVJ6GY7ipBbljGlKPbX3VGWeqySCQ4IgInwJfoV GJIHsVq3OFwP5fSUcAszEhPRXeFNRAjJB4t9DOn1EcEczZxj6r76WJ0O+6unn5QU31Ag2AWuHDi f0n8IEG7SBbHItmesLZ+DEJ3XbaflEgxPxQR2wNOekFoDzf6S5xbskukT63puCNfKZk+Lyoieip eIHU1xe1BE6XQHYp57JZyR+4DfuyexOilMi0Zur6+p0dUpXNpFt4OPwhbI+oOfY7danQCQiEZHx 2bgQ0Mf0qv3h+mv3PgZUSArILBJcrtTUYUq6GSanfkw14goYTLj9hNUpO/AsWsFi1fiAaU7S5SH nLrVGe53v3 X-Received: by 2002:a05:7022:4398:b0:11e:3e9:3e8a with SMTP id a92af1059eb24-124a00e4c48mr4538779c88.49.1769620276457; Wed, 28 Jan 2026 09:11:16 -0800 (PST) Received: from localhost ([2605:59c0:2082:bc08:ab40:208e:38fb:2546]) by smtp.gmail.com with ESMTPSA id a92af1059eb24-124a9df97d0sm3104656c88.13.2026.01.28.09.11.15 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Wed, 28 Jan 2026 09:11:15 -0800 (PST) From: David Garske To: u-boot@lists.denx.de Cc: Aidan Garske Subject: [PATCH] tpm: Add wolfTPM library support for TPM 2.0 Date: Wed, 28 Jan 2026 09:11:11 -0800 Message-ID: <20260128171112.1818705-1-david@wolfssl.com> X-Mailer: git-send-email 2.43.0 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit X-Mailman-Approved-At: Wed, 28 Jan 2026 18:49:09 +0100 X-BeenThere: u-boot@lists.denx.de X-Mailman-Version: 2.1.39 Precedence: list List-Id: U-Boot discussion List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: u-boot-bounces@lists.denx.de Sender: "U-Boot" X-Virus-Scanned: clamav-milter 0.103.8 at phobos.denx.de X-Virus-Status: Clean From: Aidan Garske Add wolfTPM library integration to U-Boot, providing an alternative TPM 2.0 implementation with native API access and enhanced features. wolfTPM is a portable, open-source TPM 2.0 library that provides: - Native TPM 2.0 API with direct access to all TPM 2.0 commands - Wrapper API for simplified common TPM operations - Direct SPI communication via internal TIS layer - Firmware update support for Infineon SLB9672/SLB9673 TPMs The standard tpm2 commands (init, startup, self_test, pcr_read, pcr_extend, clear, etc.) work with any TPM 2.0 compliant module. Firmware update functionality is specific to Infineon SLB9672/SLB9673 TPMs only. Key components added: - cmd/wolftpm.c: TPM2 command implementation using wolfTPM APIs - lib/wolftpm: wolfTPM library as git submodule - drivers/spi/bcm2835_spi.c: BCM2835 SPI driver for Raspberry Pi 4 - drivers/tpm/tpm_spi_sandbox.c: TPM SPI sandbox driver for testing - drivers/tpm/wolftpm_common.c: Common wolfTPM driver functions - include/configs/user_settings.h: wolfTPM build configuration Configuration options: - CONFIG_TPM_WOLF: Enable wolfTPM support (mutually exclusive with CONFIG_CMD_TPM_V2) - CONFIG_WOLFTPM_SLB9672: Enable Infineon SLB9672/SLB9673 firmware update support - CONFIG_BCM2835_SPI: Enable BCM2835 SPI driver Device tree updates for Raspberry Pi 4, QEMU arm64, and sandbox environments to support TPM over SPI. Tested on Raspberry Pi 4 Model B with Infineon SLB9670 (LetsTrust HAT) and SLB9672 TPMs. Signed-off-by: Aidan Garske --- .gitmodules | 3 + README | 3 + README.wolftpm.md | 149 +++ arch/arm/dts/bcm2711-rpi-4-b.dts | 20 + arch/arm/dts/qemu-arm64.dts | 4 + arch/sandbox/dts/sandbox.dtsi | 11 + cmd/Kconfig | 16 + cmd/Makefile | 11 +- cmd/tpm-common.c | 4 +- cmd/wolftpm.c | 1443 +++++++++++++++++++++++++++++ configs/rpi_4_defconfig | 29 +- doc/usage/cmd/wolftpm.rst | 694 ++++++++++++++ drivers/mtd/spi/sandbox.c | 30 +- drivers/spi/Kconfig | 9 + drivers/spi/Makefile | 1 + drivers/spi/bcm2835_spi.c | 429 +++++++++ drivers/tpm/Kconfig | 36 + drivers/tpm/Makefile | 9 + drivers/tpm/tpm_spi_sandbox.c | 410 ++++++++ drivers/tpm/wolftpm_common.c | 135 +++ include/configs/user_settings.h | 118 +++ include/hash.h | 17 + include/linux/byteorder/generic.h | 31 +- include/tpm-common.h | 22 + include/wolftpm.h | 33 + lib/Kconfig | 13 + lib/Makefile | 18 + lib/wolftpm | 1 + lib/wolftpm.c | 56 ++ scripts/check-local-export | 9 + test/cmd/Makefile | 1 + test/cmd/wolftpm.c | 364 ++++++++ test/py/tests/test_wolftpm.py | 373 ++++++++ 33 files changed, 4483 insertions(+), 19 deletions(-) create mode 100644 .gitmodules create mode 100644 README.wolftpm.md create mode 100644 cmd/wolftpm.c create mode 100644 doc/usage/cmd/wolftpm.rst create mode 100644 drivers/spi/bcm2835_spi.c create mode 100644 drivers/tpm/tpm_spi_sandbox.c create mode 100644 drivers/tpm/wolftpm_common.c create mode 100644 include/configs/user_settings.h create mode 100644 include/wolftpm.h create mode 160000 lib/wolftpm create mode 100644 lib/wolftpm.c create mode 100644 test/cmd/wolftpm.c create mode 100644 test/py/tests/test_wolftpm.py diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 00000000000..3f95a7c3eb9 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "lib/wolftpm"] + path = lib/wolftpm + url = https://github.com/wolfssl/wolfTPM.git diff --git a/README b/README index 20a73bab802..b9bfe14a1c7 100644 --- a/README +++ b/README @@ -361,6 +361,9 @@ The following options need to be configured: CONFIG_TPM Support TPM devices. + CONFIG_TPM_WOLF + Enables support for wolfTPM library. + CONFIG_TPM_TIS_INFINEON Support for Infineon i2c bus TPM devices. Only one device per system is supported at this time. diff --git a/README.wolftpm.md b/README.wolftpm.md new file mode 100644 index 00000000000..08d1fdf9776 --- /dev/null +++ b/README.wolftpm.md @@ -0,0 +1,149 @@ +# wolfTPM Support for U-Boot + +This fork adds [wolfTPM](https://github.com/wolfSSL/wolfTPM) support to U-Boot, providing full TPM 2.0 command support using wolfTPM APIs and the wolfTPM library. + +## Overview + +wolfTPM is a portable, open-source TPM 2.0 library that provides: + +- **Native TPM 2.0 API** - Direct access to all TPM 2.0 commands +- **Wrapper API** - Simplified interface for common TPM operations +- **Hardware SPI Support** - Direct communication with TPM hardware via SPI +- **Firmware Update** - Support for Infineon SLB9672/SLB9673 firmware updates +- **No External Dependencies** - Standalone implementation without kernel TPM stack + +## Why wolfTPM vs Standard U-Boot TPM? + +| Feature | Standard U-Boot TPM | wolfTPM | +|---------|---------------------|---------| +| API | Basic TPM commands | Full TPM 2.0 + Wrapper API | +| PCR Operations | Basic read/extend | Full PCR management | +| Firmware Update | Not supported | Infineon SLB9672/9673 | +| Capabilities Query | Limited | Comprehensive `caps` command | +| SPI Communication | Via kernel driver | Native wolfTPM TIS layer | +| Library Integration | N/A | wolfSSL ecosystem compatible | + +## Command: `tpm2` + +When wolfTPM is enabled, the `tpm2` command uses wolfTPM APIs instead of the standard implementation. The command interface is compatible with the standard `tpm2` command, with additional wolfTPM-specific features. + +### Basic Commands + +```bash +tpm2 autostart # Initialize TPM, startup, and self-test +tpm2 init # Initialize TPM software stack +tpm2 info # Show TPM device information +tpm2 caps # Show TPM capabilities (wolfTPM enhanced) +tpm2 self_test full # Run full self-test +``` + +### PCR Operations + +```bash +tpm2 pcr_read [algo] # Read PCR value +tpm2 pcr_extend [algo] # Extend PCR with digest +tpm2 pcr_print # Print all PCR values +tpm2 pcr_allocate # Configure PCR banks +``` + +### Security Management + +```bash +tpm2 clear # Clear TPM +tpm2 change_auth [old_pw] # Change password +tpm2 dam_reset [password] # Reset DAM counter +tpm2 dam_parameters # Set DAM params +``` + +### Firmware Update (Infineon Only) + +```bash +tpm2 firmware_update +tpm2 firmware_cancel +``` + +## Building + +### For Raspberry Pi 4 + +```bash +git clone https://github.com/aidangarske/u-boot.git +cd u-boot +git checkout rpi4-wolftpm-uboot +git submodule update --init lib/wolftpm + +export CROSS_COMPILE=aarch64-elf- +make rpi_4_defconfig +make -j$(nproc) +``` + +### Configuration Options + +Enable in `menuconfig` or defconfig: + +``` +# Core TPM support +CONFIG_TPM=y +CONFIG_TPM_V2=y + +# wolfTPM (replaces standard tpm2 command) +CONFIG_TPM_WOLF=y +CONFIG_CMD_TPM=y + +# For Infineon hardware +CONFIG_WOLFTPM_SLB9672=y + +# For QEMU/swtpm testing +# CONFIG_WOLFTPM_LINUX_DEV=y +``` + +**Note:** `CONFIG_TPM_WOLF` and standard `CONFIG_CMD_TPM_V2` are mutually exclusive. When wolfTPM is enabled, it provides the `tpm2` command implementation. + +## Hardware Support + +### Tested Hardware + +- Raspberry Pi 4 Model B +- Infineon SLB9670 TPM (LetsTrust HAT) +- Infineon SLB9672 TPM (with firmware update support) + +### SPI Configuration + +The TPM is configured on SPI0 CE1 (GPIO7), matching the standard Raspberry Pi `tpm-slb9670` overlay: + +``` +SPI0 Pins: +- SCLK: GPIO11 (pin 23) +- MOSI: GPIO10 (pin 19) +- MISO: GPIO9 (pin 21) +- CE1: GPIO7 (pin 26) +``` + +## Documentation + +- **Full Guide**: [rpi4-wolftpm-uboot](https://github.com/aidangarske/rpi4-wolftpm-uboot) +- **Firmware Update**: See `doc/usage/cmd/wolftpm.rst` +- **wolfTPM Library**: [github.com/wolfSSL/wolfTPM](https://github.com/wolfSSL/wolfTPM) + +## Files Modified/Added + +``` +cmd/wolftpm.c # TPM2 command implementation using wolfTPM +lib/wolftpm/ # wolfTPM library (submodule) +include/configs/user_settings.h # wolfTPM configuration +arch/arm/dts/bcm2711-rpi-4-b.dts # Device tree with SPI/TPM config +configs/rpi_4_defconfig # RPi4 build configuration +drivers/spi/bcm2835_spi.c # BCM2835 SPI driver +doc/usage/cmd/wolftpm.rst # Command documentation +``` + +## License + +- U-Boot: GPL-2.0 +- wolfTPM: GPL-2.0 +- This integration: GPL-2.0 + +## Author + +Aidan Garske +wolfSSL Inc. diff --git a/arch/arm/dts/bcm2711-rpi-4-b.dts b/arch/arm/dts/bcm2711-rpi-4-b.dts index 72ce80fbf26..a09276dc279 100644 --- a/arch/arm/dts/bcm2711-rpi-4-b.dts +++ b/arch/arm/dts/bcm2711-rpi-4-b.dts @@ -8,6 +8,11 @@ compatible = "raspberrypi,4-model-b", "brcm,bcm2711"; model = "Raspberry Pi 4 Model B"; + /* Alias hardware SPI as spi0 so wolfTPM finds it at bus 0 */ + aliases { + spi0 = &spi; + }; + chosen { /* 8250 auxiliary UART instead of pl011 */ stdout-path = "serial1:115200n8"; @@ -54,6 +59,21 @@ enable-active-high; gpio = <&expgpio 6 GPIO_ACTIVE_HIGH>; }; + +}; + +/* Hardware SPI with TPM - matches Linux tpm-slb9670 overlay */ +&spi { + status = "okay"; + pinctrl-names = "default"; + pinctrl-0 = <&spi0_gpio7>; + + /* Infineon SLB9670/9672 TPM 2.0 on CE1 (GPIO7) */ + tpm@1 { + compatible = "infineon,slb9670", "tcg,tpm_tis-spi"; + reg = <1>; /* CE1 */ + spi-max-frequency = <32000000>; + }; }; &ddc0 { diff --git a/arch/arm/dts/qemu-arm64.dts b/arch/arm/dts/qemu-arm64.dts index 95fcf53ed74..e74d036a532 100644 --- a/arch/arm/dts/qemu-arm64.dts +++ b/arch/arm/dts/qemu-arm64.dts @@ -12,4 +12,8 @@ #endif / { + tpm@0c000000 { + compatible = "tcg,tpm-tis-mmio"; + reg = <0x0 0x0c000000 0x0 0x5000>; + }; }; diff --git a/arch/sandbox/dts/sandbox.dtsi b/arch/sandbox/dts/sandbox.dtsi index 02b03894eaf..2fdd7f0e942 100644 --- a/arch/sandbox/dts/sandbox.dtsi +++ b/arch/sandbox/dts/sandbox.dtsi @@ -286,6 +286,17 @@ spi-max-frequency = <40000000>; sandbox,filename = "spi.bin"; }; + + tpm_spi: tpm@1 { + reg = <1>; + compatible = "sandbox,tpm-spi"; + spi-max-frequency = <10000000>; + sandbox,emul = <&tpm_spi_emul>; + }; + }; + + tpm_spi_emul: tpm-spi-emul { + compatible = "sandbox,tpm-spi-emul"; }; spl-test { diff --git a/cmd/Kconfig b/cmd/Kconfig index cc4dd5a3163..3a4be20268e 100644 --- a/cmd/Kconfig +++ b/cmd/Kconfig @@ -2814,6 +2814,9 @@ config CMD_TPM command requires a suitable TPM on your board and the correct driver must be enabled. + Note: For TPM2, do not enable both CMD_TPM and CMD_WOLFTPM as they + both provide the tpm2 command. + if CMD_TPM config CMD_TPM_TEST @@ -3194,4 +3197,17 @@ config CMD_SPAWN_NUM_JOBS When a jobs exits, its identifier is available to be re-used by the next spawn command. +config CMD_WOLFTPM + bool "Enable wolfTPM tpm2 command (replaces standard tpm2)" + depends on TPM && TPM_V2 + select TPM_WOLF + help + This provides the 'tpm2' command using the wolfTPM library instead + of the standard U-Boot TPM2 implementation. wolfTPM offers additional + features including firmware update support for Infineon TPMs and + enhanced capabilities reporting. + + Note: Do not enable both CMD_TPM and CMD_WOLFTPM as they both + provide the tpm2 command. + endif diff --git a/cmd/Makefile b/cmd/Makefile index 4cd13d4fa6e..9c622e3d717 100644 --- a/cmd/Makefile +++ b/cmd/Makefile @@ -191,9 +191,18 @@ obj-$(CONFIG_CMD_TIMER) += timer.o obj-$(CONFIG_CMD_TRACE) += trace.o obj-$(CONFIG_HUSH_PARSER) += test.o obj-$(CONFIG_CMD_TPM) += tpm-common.o -obj-$(CONFIG_CMD_TPM_V1) += tpm-v1.o obj-$(CONFIG_CMD_TPM_TEST) += tpm_test.o +ifeq ($(CONFIG_TPM_WOLF),y) +ifeq ($(CONFIG_TPM_V2),y) +ccflags-y += -Ilib/wolftpm \ + -Iinclude/configs \ + -DWOLFTPM_USER_SETTINGS +obj-$(CONFIG_TPM_WOLF) += wolftpm.o +endif +else +obj-$(CONFIG_CMD_TPM_V1) += tpm-v1.o obj-$(CONFIG_CMD_TPM_V2) += tpm-v2.o +endif obj-$(CONFIG_CMD_CROS_EC) += cros_ec.o obj-$(CONFIG_CMD_UBI) += ubi.o obj-$(CONFIG_CMD_UBIFS) += ubifs.o diff --git a/cmd/tpm-common.c b/cmd/tpm-common.c index 1cd57f901b6..30e99e10758 100644 --- a/cmd/tpm-common.c +++ b/cmd/tpm-common.c @@ -234,7 +234,7 @@ int type_string_write_vars(const char *type_str, u8 *data, return 0; } -static int tpm_show_device(void) +int tpm_show_device(void) { struct udevice *dev; char buf[80]; @@ -253,7 +253,7 @@ static int tpm_show_device(void) return 0; } -static int tpm_set_device(unsigned long num) +int tpm_set_device(unsigned long num) { struct udevice *dev; unsigned long n = 0; diff --git a/cmd/wolftpm.c b/cmd/wolftpm.c new file mode 100644 index 00000000000..cafd790cfaa --- /dev/null +++ b/cmd/wolftpm.c @@ -0,0 +1,1443 @@ +/* wolftpm.c +* +* SPDX-License-Identifier: GPL-2.0+ +* +* (C) Copyright 2025 +* Aidan Garske +*/ + +#define LOG_CATEGORY UCLASS_BOOTSTD + +#ifdef HAVE_CONFIG_H + #include +#endif + +#include +#include +#include +#include + +#include +#include +#ifndef WOLFTPM2_NO_WRAPPER + +#include +#include + +/* U-boot specific includes */ +#include +#include +#include +#include +#include +#include +#include + + +/* Firmware update info structure for Infineon TPM */ +#ifdef WOLFTPM_FIRMWARE_UPGRADE +#if defined(WOLFTPM_SLB9672) || defined(WOLFTPM_SLB9673) +typedef struct { + byte* manifest_buf; + byte* firmware_buf; + size_t manifest_bufSz; + size_t firmware_bufSz; +} fw_info_t; +#endif +#endif + +/******************************************************************************/ +/* --- BEGIN Common Commands -- */ +/******************************************************************************/ + +#ifdef WOLFTPM_LINUX_DEV +/* Use U-Boot's TPM device model for device/info/state when in Linux dev mode */ +static int do_tpm2_device(struct cmd_tbl *cmdtp, int flag, + int argc, char *const argv[]) +{ + int rc; + unsigned long num; + + /* Expected 1-2 arg: command + [num device] */ + if (argc < 1 || argc > 2) { + return CMD_RET_USAGE; + } + + if (argc == 2) { + num = dectoul(argv[1], NULL); + rc = tpm_set_device(num); + if (rc) + log_debug("Couldn't set TPM %lu (rc = %d)\n", num, rc); + } else { + rc = tpm_show_device(); + } + + log_debug("tpm device: rc = %d (%s)\n", rc, TPM2_GetRCString(rc)); + + return rc; +} + +static int do_tpm2_info(struct cmd_tbl *cmdtp, int flag, + int argc, char *const argv[]) +{ + int rc; + char buf[80]; + struct udevice *dev; + + if (argc != 1) { + return CMD_RET_USAGE; + } + + /* Init the TPM2 device */ + rc = get_tpm(&dev); + if (rc == 0) { + /* Get the current TPM's description + * tpm_get_desc returns the number of bytes written on success */ + rc = tpm_get_desc(dev, buf, sizeof(buf)); + if (rc < 0) { + log_debug("Couldn't get TPM info (%d)\n", rc); + rc = CMD_RET_FAILURE; + } + else { + log_debug("%s\n", buf); + rc = 0; /* Success - return 0, not byte count */ + } + } + + log_debug("tpm2 info: rc = %d (%s)\n", rc, TPM2_GetRCString(rc)); + + return rc; +} + +static int do_tpm2_state(struct cmd_tbl *cmdtp, int flag, + int argc, char *const argv[]) +{ + int rc; + char buf[80]; + struct udevice *dev; + + if (argc != 1) { + return CMD_RET_USAGE; + } + + /* Init the TPM2 device */ + rc = get_tpm(&dev); + if (rc == 0) { + /* Get the current TPM's state + * tpm_report_state may return -ENOSYS if not implemented by driver */ + rc = tpm_report_state(dev, buf, sizeof(buf)); + if (rc == -ENOSYS) { + log_debug("TPM state reporting not supported by driver\n"); + rc = 0; /* Not an error, just not supported */ + } + else if (rc < 0) { + log_debug("Couldn't get TPM state (%d)\n", rc); + rc = CMD_RET_FAILURE; + } + else { + log_debug("%s\n", buf); + rc = 0; /* Success - return 0, not byte count */ + } + } + + log_debug("tpm2 state: rc = %d (%s)\n", rc, TPM2_GetRCString(rc)); + + return rc; +} + +#else /* !WOLFTPM_LINUX_DEV - Native SPI mode implementations */ + +static int do_tpm2_device(struct cmd_tbl *cmdtp, int flag, + int argc, char *const argv[]) +{ + WOLFTPM2_DEV dev; + WOLFTPM2_CAPS caps; + int rc; + + /* Expected 1 arg only in native SPI mode (no device switching) */ + if (argc != 1) { + return CMD_RET_USAGE; + } + + /* Try to initialize and get device info */ + rc = wolfTPM2_Init(&dev, TPM2_IoCb, NULL); + if (rc == 0) { + rc = wolfTPM2_GetCapabilities(&dev, &caps); + if (rc == 0) { + printf("TPM Device 0: %s (%s) FW=%d.%d\n", + caps.mfgStr, caps.vendorStr, + caps.fwVerMajor, caps.fwVerMinor); + } + wolfTPM2_Cleanup(&dev); + } + + if (rc != 0) { + printf("No TPM device found (rc=%d: %s)\n", rc, TPM2_GetRCString(rc)); + return CMD_RET_FAILURE; + } + + return 0; +} + +static int do_tpm2_info(struct cmd_tbl *cmdtp, int flag, + int argc, char *const argv[]) +{ + WOLFTPM2_DEV dev; + WOLFTPM2_CAPS caps; + int rc; + + if (argc != 1) { + return CMD_RET_USAGE; + } + + /* Init the TPM2 device */ + rc = wolfTPM2_Init(&dev, TPM2_IoCb, NULL); + if (rc == 0) { + rc = wolfTPM2_GetCapabilities(&dev, &caps); + if (rc == 0) { + printf("TPM 2.0: %s (%s)\n", caps.mfgStr, caps.vendorStr); + printf(" Firmware: %d.%d (0x%08X)\n", + caps.fwVerMajor, caps.fwVerMinor, caps.fwVerVendor); + printf(" Type: 0x%08X\n", caps.tpmType); + } + wolfTPM2_Cleanup(&dev); + } + + if (rc != 0) { + printf("Couldn't get TPM info (rc=%d: %s)\n", rc, TPM2_GetRCString(rc)); + return CMD_RET_FAILURE; + } + + log_debug("tpm2 info: rc = %d (%s)\n", rc, TPM2_GetRCString(rc)); + return 0; +} + +static int do_tpm2_state(struct cmd_tbl *cmdtp, int flag, + int argc, char *const argv[]) +{ + WOLFTPM2_DEV dev; + WOLFTPM2_CAPS caps; + int rc; + + if (argc != 1) { + return CMD_RET_USAGE; + } + + /* Init the TPM2 device */ + rc = wolfTPM2_Init(&dev, TPM2_IoCb, NULL); + if (rc == 0) { + rc = wolfTPM2_GetCapabilities(&dev, &caps); + if (rc == 0) { + printf("TPM State:\n"); + printf(" Manufacturer: %s\n", caps.mfgStr); + printf(" Vendor: %s\n", caps.vendorStr); + printf(" Firmware: %d.%d\n", caps.fwVerMajor, caps.fwVerMinor); +#if defined(WOLFTPM_SLB9672) || defined(WOLFTPM_SLB9673) + printf(" Mode: Infineon SLB967x (Native SPI)\n"); + printf(" OpMode: %d\n", caps.opMode); +#else + printf(" Mode: Native wolfTPM SPI\n"); +#endif + } + wolfTPM2_Cleanup(&dev); + } + + if (rc != 0) { + printf("Couldn't get TPM state (rc=%d: %s)\n", rc, TPM2_GetRCString(rc)); + return CMD_RET_FAILURE; + } + + log_debug("tpm2 state: rc = %d (%s)\n", rc, TPM2_GetRCString(rc)); + return 0; +} + +#endif /* WOLFTPM_LINUX_DEV */ + +static int do_tpm2_init(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + WOLFTPM2_DEV dev; + + if (argc != 1) { + return CMD_RET_USAGE; + } + + /* Init the TPM2 device */ + return TPM2_Init_Device(&dev, NULL); +} + + +static int do_tpm2_autostart(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + int rc; + WOLFTPM2_DEV dev; + + if (argc != 1) { + return CMD_RET_USAGE; + } + + /* Init the TPM2 device */ + rc = TPM2_Init_Device(&dev, NULL); + if (rc == TPM_RC_SUCCESS) { + /* Perform a startup clear + * doStartup=1: Just starts up the TPM */ + rc = wolfTPM2_Reset(&dev, 0, 1); + /* TPM_RC_INITIALIZE means already started - treat as success */ + if (rc == TPM_RC_INITIALIZE) { + rc = TPM_RC_SUCCESS; + } else if (rc != TPM_RC_SUCCESS) { + log_debug("wolfTPM2_Reset failed 0x%x: %s\n", rc, + TPM2_GetRCString(rc)); + } + } + if (rc == TPM_RC_SUCCESS) { + /* Perform a full self test */ + rc = wolfTPM2_SelfTest(&dev); + if (rc != TPM_RC_SUCCESS) { + log_debug("wolfTPM2_SelfTest failed 0x%x: %s\n", rc, + TPM2_GetRCString(rc)); + } + } + + log_debug("tpm2 autostart: rc = %d (%s)\n", rc, TPM2_GetRCString(rc)); + + return rc; +} + +/******************************************************************************/ +/* --- END Common Commands -- */ +/******************************************************************************/ + + +/******************************************************************************/ +/* --- START TPM 2.0 Commands -- */ +/******************************************************************************/ + +static int do_tpm2_wrapper_getcapsargs(struct cmd_tbl *cmdtp, int flag, + int argc, char *const argv[]) +{ + GetCapability_In in; + GetCapability_Out out; + u32 capability, property, rc; + u8 *data; + size_t count; + int i, j; + + if (argc != 5) + return CMD_RET_USAGE; + + capability = simple_strtoul(argv[1], NULL, 0); + property = simple_strtoul(argv[2], NULL, 0); + data = map_sysmem(simple_strtoul(argv[3], NULL, 0), 0); + count = simple_strtoul(argv[4], NULL, 0); + + memset(&in, 0, sizeof(in)); + memset(&out, 0, sizeof(out)); + in.capability = capability; + in.property = property; + in.propertyCount = count; + rc = TPM2_GetCapability(&in, &out); + if (rc == 0) { + XMEMCPY(data, &out.capabilityData.data, sizeof(out.capabilityData.data)); + + printf("Capabilities read from TPM:\n"); + for (i = 0; i < count; i++) { + printf("Property 0x"); + for (j = 0; j < 4; j++) + printf("%02x", data[(i * 8) + j + sizeof(u32)]); + printf(": 0x"); + for (j = 4; j < 8; j++) + printf("%02x", data[(i * 8) + j + sizeof(u32)]); + printf("\n"); + } + } + + unmap_sysmem(data); + + log_debug("tpm2 get_capability: rc = %d (%s)\n", rc, TPM2_GetRCString(rc)); + + return rc; +} + +static int do_tpm2_wrapper_capsargs(struct cmd_tbl *cmdtp, int flag, + int argc, char *const argv[]) +{ + int rc; + WOLFTPM2_DEV dev; + WOLFTPM2_CAPS caps; + + if (argc != 1) { + return CMD_RET_USAGE; + } + + /* Init the TPM2 device */ + rc = TPM2_Init_Device(&dev, NULL); + if (rc == TPM_RC_SUCCESS) { + rc = wolfTPM2_GetCapabilities(&dev, &caps); + } + if (rc == TPM_RC_SUCCESS) { + log_debug("Mfg %s (%d), Vendor %s, Fw %u.%u (0x%x), " + "FIPS 140-2 %d, CC-EAL4 %d\n", + caps.mfgStr, caps.mfg, caps.vendorStr, caps.fwVerMajor, + caps.fwVerMinor, caps.fwVerVendor, caps.fips140_2, caps.cc_eal4); +#if defined(WOLFTPM_SLB9672) || defined(WOLFTPM_SLB9673) + log_debug("Operational mode: %s (0x%x)\n", + TPM2_IFX_GetOpModeStr(caps.opMode), caps.opMode); + log_debug("KeyGroupId 0x%x, FwCounter %d (%d same)\n", + caps.keyGroupId, caps.fwCounter, caps.fwCounterSame); +#endif + } + + /* List the active persistent handles */ + if (rc == TPM_RC_SUCCESS) { + rc = wolfTPM2_GetHandles(PERSISTENT_FIRST, NULL); + if (rc >= TPM_RC_SUCCESS) { + log_debug("Found %d persistent handles\n", rc); + } + } + + /* Print the available PCR's */ + if (rc == TPM_RC_SUCCESS) { + rc = TPM2_PCRs_Print(); + } + + /* Only doShutdown=1: Just shut down the TPM */ + wolfTPM2_Reset(&dev, 1, 0); + + wolfTPM2_Cleanup(&dev); + + log_debug("tpm2 caps: rc = %d (%s)\n", rc, TPM2_GetRCString(rc)); + + return rc; +} + +#ifdef WOLFTPM_FIRMWARE_UPGRADE +#if defined(WOLFTPM_SLB9672) || defined(WOLFTPM_SLB9673) +static int do_tpm2_firmware_update(struct cmd_tbl *cmdtp, int flag, + int argc, char *const argv[]) +{ + int rc; + WOLFTPM2_DEV dev; + WOLFTPM2_CAPS caps; + fw_info_t fwinfo; + ulong manifest_addr, firmware_addr; + size_t manifest_sz, firmware_sz; + uint8_t manifest_hash[TPM_SHA384_DIGEST_SIZE]; + int recovery = 0; + + memset(&fwinfo, 0, sizeof(fwinfo)); + + /* Need 5 args: command + 4 arguments */ + if (argc != 5) { + log_debug("Error: Expected 5 arguments but got %d\n", argc); + return CMD_RET_USAGE; + } + printf("TPM2 Firmware Update\n"); + + /* Convert all arguments from strings to numbers */ + manifest_addr = simple_strtoul(argv[1], NULL, 0); + manifest_sz = simple_strtoul(argv[2], NULL, 0); + firmware_addr = simple_strtoul(argv[3], NULL, 0); + firmware_sz = simple_strtoul(argv[4], NULL, 0); + + /* Map the memory addresses */ + fwinfo.manifest_buf = map_sysmem(manifest_addr, manifest_sz); + fwinfo.firmware_buf = map_sysmem(firmware_addr, firmware_sz); + fwinfo.manifest_bufSz = manifest_sz; + fwinfo.firmware_bufSz = firmware_sz; + + if (fwinfo.manifest_buf == NULL || fwinfo.firmware_buf == NULL) { + log_debug("Error: Invalid memory addresses\n"); + return CMD_RET_FAILURE; + } + + printf("Infineon Firmware Update Tool\n"); + printf("\tManifest Address: 0x%lx (size: %zu)\n", + manifest_addr, manifest_sz); + printf("\tFirmware Address: 0x%lx (size: %zu)\n", + firmware_addr, firmware_sz); + + rc = TPM2_Init_Device(&dev, NULL); + if (rc == TPM_RC_SUCCESS) { + rc = wolfTPM2_GetCapabilities(&dev, &caps); + } + + if (rc == TPM_RC_SUCCESS) { + TPM2_IFX_PrintInfo(&caps); + if (caps.keyGroupId == 0) { + log_debug("Error getting key group id from TPM!\n"); + } + if (caps.opMode == 0x02 || (caps.opMode & 0x80)) { + /* if opmode == 2 or 0x8x then we need to use recovery mode */ + recovery = 1; + } + } + + if (rc == TPM_RC_SUCCESS) { + if (recovery) { + printf("Firmware Update (recovery mode):\n"); + rc = wolfTPM2_FirmwareUpgradeRecover(&dev, + fwinfo.manifest_buf, (uint32_t)fwinfo.manifest_bufSz, + TPM2_IFX_FwData_Cb, &fwinfo); + } + else { + /* Normal mode - hash with wc_Sha384Hash */ + printf("Firmware Update (normal mode):\n"); + rc = wc_Sha384Hash(fwinfo.manifest_buf, + (uint32_t)fwinfo.manifest_bufSz, manifest_hash); + if (rc == TPM_RC_SUCCESS) { + rc = wolfTPM2_FirmwareUpgradeHash(&dev, TPM_ALG_SHA384, + manifest_hash, (uint32_t)sizeof(manifest_hash), + fwinfo.manifest_buf, (uint32_t)fwinfo.manifest_bufSz, + TPM2_IFX_FwData_Cb, &fwinfo); + } + } + } + if (rc == TPM_RC_SUCCESS) { + TPM2_IFX_PrintInfo(&caps); + } + + if (fwinfo.manifest_buf) + unmap_sysmem(fwinfo.manifest_buf); + if (fwinfo.firmware_buf) + unmap_sysmem(fwinfo.firmware_buf); + + if (rc != TPM_RC_SUCCESS) { + log_debug("Infineon firmware update failed 0x%x: %s\n", + rc, TPM2_GetRCString(rc)); + } + + wolfTPM2_Cleanup(&dev); + + log_debug("tpm2 firmware_update: rc=%d (%s)\n", rc, TPM2_GetRCString(rc)); + + return rc; +} + +static int do_tpm2_firmware_cancel(struct cmd_tbl *cmdtp, int flag, + int argc, char *const argv[]) +{ + int rc; + WOLFTPM2_DEV dev; + uint8_t cmd[TPM2_HEADER_SIZE + 2]; + uint16_t val16; + + if (argc != 1) { + return CMD_RET_USAGE; + } + + /* Init the TPM2 device */ + rc = TPM2_Init_Device(&dev, NULL); + if (rc == TPM_RC_SUCCESS) { + /* Setup command size in header */ + val16 = TPM2_HEADER_SIZE + 2; + XMEMCPY(cmd, &val16, sizeof(val16)); + val16 = 0; + XMEMCPY(&cmd[TPM2_HEADER_SIZE], &val16, sizeof(val16)); + + rc = TPM2_IFX_FieldUpgradeCommand(TPM_CC_FieldUpgradeAbandonVendor, + cmd, sizeof(cmd)); + if (rc != TPM_RC_SUCCESS) { + log_debug("Firmware abandon failed 0x%x: %s\n", + rc, TPM2_GetRCString(rc)); + } + } + + wolfTPM2_Cleanup(&dev); + + log_debug("tpm2 firmware_cancel: rc=%d (%s)\n", rc, TPM2_GetRCString(rc)); + + return rc; +} +#endif /* WOLFTPM_SLB9672 || WOLFTPM_SLB9673 */ +#endif /* WOLFTPM_FIRMWARE_UPGRADE */ + +static int do_tpm2_startup(struct cmd_tbl *cmdtp, int flag, + int argc, char *const argv[]) +{ + int rc; + WOLFTPM2_DEV dev; + Startup_In startupIn; + Shutdown_In shutdownIn; + int doStartup = YES; + + /* startup TPM2_SU_CLEAR|TPM2_SU_STATE [off] */ + if (argc < 2 || argc > 3) + return CMD_RET_USAGE; + /* Check if shutdown requested */ + if (argc == 3) { + if (strcmp(argv[2], "off") != 0) + return CMD_RET_USAGE; + doStartup = NO; /* shutdown */ + } + printf("TPM2 Startup\n"); + + memset(&startupIn, 0, sizeof(startupIn)); + memset(&shutdownIn, 0, sizeof(shutdownIn)); + + /* Init the TPM2 device */ + rc = TPM2_Init_Device(&dev, NULL); + if (rc != TPM_RC_SUCCESS) return rc; + + if (!strcmp(argv[1], "TPM2_SU_CLEAR")) { + if (doStartup == YES) { + startupIn.startupType = TPM_SU_CLEAR; + } else { + shutdownIn.shutdownType = TPM_SU_CLEAR; + } + } else if (!strcmp(argv[1], "TPM2_SU_STATE")) { + if (doStartup == YES) { + startupIn.startupType = TPM_SU_STATE; + } else { + shutdownIn.shutdownType = TPM_SU_STATE; + } + } else { + log_debug("Couldn't recognize mode string: %s\n", argv[1]); + wolfTPM2_Cleanup(&dev); + return CMD_RET_FAILURE; + } + + /* startup */ + if (doStartup == YES) { + rc = TPM2_Startup(&startupIn); + /* TPM_RC_INITIALIZE = Already started */ + if (rc != TPM_RC_SUCCESS && rc != TPM_RC_INITIALIZE) { + log_debug("TPM2 Startup: Result = 0x%x (%s)\n", rc, + TPM2_GetRCString(rc)); + } + /* shutdown */ + } else { + rc = TPM2_Shutdown(&shutdownIn); + if (rc != TPM_RC_SUCCESS) { + log_debug("TPM2 Shutdown: Result = 0x%x (%s)\n", rc, + TPM2_GetRCString(rc)); + } + } + + wolfTPM2_Cleanup(&dev); + + if (rc >= 0) + rc = 0; + + log_debug("tpm2 startup (%s): rc = %d (%s)\n", + doStartup ? "startup" : "shutdown", rc, TPM2_GetRCString(rc)); + + return rc; +} + +static int do_tpm2_selftest(struct cmd_tbl *cmdtp, int flag, + int argc, char *const argv[]) +{ + int rc; + WOLFTPM2_DEV dev; + TPMI_YES_NO fullTest = YES; + + /* Need 2 arg: command + type */ + if (argc != 2) { + return CMD_RET_USAGE; + } + + /* Init the TPM2 device */ + rc = TPM2_Init_Device(&dev, NULL); + if (rc == TPM_RC_SUCCESS) { + if (!strcmp(argv[1], "full")) { + fullTest = YES; + } else if (!strcmp(argv[1], "continue")) { + fullTest = NO; + } else { + log_debug("Couldn't recognize test mode: %s\n", argv[1]); + wolfTPM2_Cleanup(&dev); + return CMD_RET_FAILURE; + } + + /* full test */ + if (fullTest == YES) { + rc = wolfTPM2_SelfTest(&dev); + if (rc != TPM_RC_SUCCESS) { + log_debug("TPM2 Self Test: Result = 0x%x (%s)\n", rc, + TPM2_GetRCString(rc)); + } + /* continue test */ + } else { + rc = wolfTPM2_SelfTest(&dev); + if (rc != TPM_RC_SUCCESS) { + log_debug("TPM2 Self Test: Result = 0x%x (%s)\n", rc, + TPM2_GetRCString(rc)); + } + } + } + + wolfTPM2_Cleanup(&dev); + + log_debug("tpm2 selftest (%s): rc = %d (%s)\n", + fullTest ? "full" : "continue", rc, TPM2_GetRCString(rc)); + + return rc; +} + +static int do_tpm2_clear(struct cmd_tbl *cmdtp, int flag, + int argc, char *const argv[]) +{ + int rc; + WOLFTPM2_DEV dev; + Clear_In clearIn; + TPMI_RH_CLEAR handle; + + /* Need 2 arg: command + type */ + if (argc != 2) { + return CMD_RET_USAGE; + } + + if (!strcasecmp("TPM2_RH_LOCKOUT", argv[1])) { + handle = TPM_RH_LOCKOUT; + } else if (!strcasecmp("TPM2_RH_PLATFORM", argv[1])) { + handle = TPM_RH_PLATFORM; + } else { + return CMD_RET_USAGE; + } + + /* Init the TPM2 device */ + rc = TPM2_Init_Device(&dev, NULL); + if (rc == TPM_RC_SUCCESS) { + /* Set up clear */ + memset(&clearIn, 0, sizeof(clearIn)); + clearIn.authHandle = handle; + + rc = TPM2_Clear(&clearIn); + if (rc != TPM_RC_SUCCESS) { + log_debug("TPM2 Clear: Result = 0x%x (%s)\n", rc, + TPM2_GetRCString(rc)); + } + } + + wolfTPM2_Cleanup(&dev); + + log_debug("tpm2 clear (%s): rc = %d (%s)\n", + handle == TPM_RH_LOCKOUT ? "TPM2_RH_LOCKOUT" : "TPM2_RH_PLATFORM", + rc, TPM2_GetRCString(rc)); + + return rc; +} + +static int do_tpm2_pcr_extend(struct cmd_tbl *cmdtp, int flag, + int argc, char *const argv[]) +{ + int rc; + WOLFTPM2_DEV dev; + uint32_t pcrIndex; + int algo = TPM_ALG_SHA256; + int digestLen; + void *digest; + ulong digest_addr; + + /* Need 3-4 args: command + pcr + digest_addr + [algo] */ + if (argc < 3 || argc > 4) { + return CMD_RET_USAGE; + } + printf("TPM2 PCR Extend\n"); + + pcrIndex = simple_strtoul(argv[1], NULL, 0); + digest_addr = simple_strtoul(argv[2], NULL, 0); + + /* Optional algorithm */ + if (argc == 4) { + algo = TPM2_GetAlgId(argv[3]); + if (algo < 0) { + log_debug("Couldn't recognize algorithm: %s\n", argv[3]); + return CMD_RET_FAILURE; + } + log_debug("Using algorithm: %s\n", TPM2_GetAlgName(algo)); + } + + /* Get digest length based on algorithm */ + digestLen = TPM2_GetHashDigestSize(algo); + if (digestLen <= 0) { + log_debug("Invalid algorithm digest length\n"); + return CMD_RET_FAILURE; + } + + /* Map digest from memory address */ + digest = map_sysmem(digest_addr, digestLen); + if (digest == NULL) { + log_debug("Error: Invalid digest memory address\n"); + return CMD_RET_FAILURE; + } + + log_debug("TPM2 PCR Extend: PCR %u with %s digest\n", + (unsigned int)pcrIndex, TPM2_GetAlgName(algo)); + + /* Init the TPM2 device */ + rc = TPM2_Init_Device(&dev, NULL); + if (rc != TPM_RC_SUCCESS) { + unmap_sysmem(digest); + return rc; + } + + /* Extend the PCR */ + rc = wolfTPM2_ExtendPCR(&dev, pcrIndex, algo, digest, digestLen); + if (rc != TPM_RC_SUCCESS) { + log_debug("TPM2_PCR_Extend failed 0x%x: %s\n", rc, + TPM2_GetRCString(rc)); + } + + unmap_sysmem(digest); + wolfTPM2_Cleanup(&dev); + + log_debug("tpm2 pcr_extend: rc = %d (%s)\n", rc, TPM2_GetRCString(rc)); + + return rc; +} + +static int do_tpm2_pcr_read(struct cmd_tbl *cmdtp, int flag, + int argc, char *const argv[]) +{ + int rc; + WOLFTPM2_DEV dev; + uint32_t pcrIndex; + int algo = TPM_ALG_SHA256; + void *digest; + ulong digest_addr; + int digestLen; + + /* Need 3-4 args: command + pcr + digest_addr + [algo] */ + if (argc < 3 || argc > 4) { + return CMD_RET_USAGE; + } + + pcrIndex = simple_strtoul(argv[1], NULL, 0); + digest_addr = simple_strtoul(argv[2], NULL, 0); + + /* Optional algorithm */ + if (argc == 4) { + algo = TPM2_GetAlgId(argv[3]); + if (algo < 0) { + log_debug("Couldn't recognize algorithm: %s\n", argv[3]); + return CMD_RET_FAILURE; + } + log_debug("Using algorithm: %s\n", TPM2_GetAlgName(algo)); + } + + /* Get digest length based on algorithm */ + digestLen = TPM2_GetHashDigestSize(algo); + if (digestLen <= 0) { + log_debug("Invalid algorithm digest length\n"); + return CMD_RET_FAILURE; + } + + /* Map digest from memory address */ + digest = map_sysmem(digest_addr, digestLen); + if (digest == NULL) { + log_debug("Error: Invalid digest memory address\n"); + return CMD_RET_FAILURE; + } + + log_debug("TPM2 PCR Read: PCR %u to %s digest\n", + (unsigned int)pcrIndex, TPM2_GetAlgName(algo)); + + /* Init the TPM2 device */ + rc = TPM2_Init_Device(&dev, NULL); + if (rc != TPM_RC_SUCCESS) { + unmap_sysmem(digest); + return rc; + } + + /* Read the PCR */ + rc = wolfTPM2_ReadPCR(&dev, pcrIndex, algo, digest, &digestLen); + if (rc != TPM_RC_SUCCESS) { + log_debug("TPM2_PCR_Read failed 0x%x: %s\n", rc, + TPM2_GetRCString(rc)); + } + + unmap_sysmem(digest); + wolfTPM2_Cleanup(&dev); + + log_debug("tpm2 pcr_read: rc = %d (%s)\n", rc, TPM2_GetRCString(rc)); + + return rc; +} + +static int do_tpm2_pcr_allocate(struct cmd_tbl *cmdtp, int flag, + int argc, char *const argv[]) +{ + int rc; + WOLFTPM2_DEV dev; + PCR_Allocate_In in; + PCR_Allocate_Out out; + TPM2B_AUTH auth; + + /* Need 3-4 args: command + algorithm + on/off + [password] */ + if (argc < 3 || argc > 4) { + return CMD_RET_USAGE; + } + + /* Init the TPM2 device */ + rc = TPM2_Init_Device(&dev, NULL); + if (rc != TPM_RC_SUCCESS) return rc; + + /* Setup PCR Allocation command */ + memset(&in, 0, sizeof(in)); + in.authHandle = TPM_RH_PLATFORM; + + /* Single PCR bank allocation */ + in.pcrAllocation.count = 1; /* Change only one bank */ + in.pcrAllocation.pcrSelections[0].hash = TPM2_GetAlgId(argv[1]); + in.pcrAllocation.pcrSelections[0].sizeofSelect = PCR_SELECT_MAX; + + /* Set all PCRs for this algorithm */ + if (!strcmp(argv[2], "on")) { + memset(in.pcrAllocation.pcrSelections[0].pcrSelect, 0xFF, + PCR_SELECT_MAX); + } + /* Clear all PCRs for this algorithm */ + else if (!strcmp(argv[2], "off")) { + memset(in.pcrAllocation.pcrSelections[0].pcrSelect, 0x00, + PCR_SELECT_MAX); + } + else { + log_debug("Couldn't recognize allocate mode: %s\n", argv[2]); + wolfTPM2_Cleanup(&dev); + return CMD_RET_USAGE; + } + log_debug("Attempting to set %s bank to %s\n", + TPM2_GetAlgName(in.pcrAllocation.pcrSelections[0].hash), + argv[2]); + + /* Set auth password if provided */ + if (argc == 4) { + memset(&auth, 0, sizeof(auth)); + auth.size = strlen(argv[3]); + XMEMCPY(auth.buffer, argv[3], auth.size); + rc = wolfTPM2_SetAuth(&dev, 0, TPM_RH_PLATFORM, &auth, 0, NULL); + if (rc != TPM_RC_SUCCESS) { + log_debug("wolfTPM2_SetAuth failed 0x%x: %s\n", rc, + TPM2_GetRCString(rc)); + wolfTPM2_Cleanup(&dev); + return rc; + } + } + + /* Allocate the PCR */ + rc = TPM2_PCR_Allocate(&in, &out); + if (rc != TPM_RC_SUCCESS) { + log_debug("TPM2_PCR_Allocate failed 0x%x: %s\n", rc, + TPM2_GetRCString(rc)); + } + + /* Print current PCR state */ + printf("\n\tNOTE: A TPM restart is required for changes to take effect\n"); + printf("\nCurrent PCR state:\n"); + TPM2_PCRs_Print(); + + wolfTPM2_Cleanup(&dev); + + printf("Allocation Success: %s\n", + out.allocationSuccess ? "YES" : "NO"); + log_debug("tpm2 pcr_allocate %s (%s): rc = %d (%s)\n", + TPM2_GetAlgName(in.pcrAllocation.pcrSelections[0].hash), + argv[2], rc, TPM2_GetRCString(rc)); + + return rc; +} + +/* We dont have parameter encryption enabled when WOLFTPM2_NO_WOLFCRYPT +* is defined. If the session isn't used then the new password is not +* encrypted in transit over the bus: "a session is required to protect +* the new platform auth" */ +#ifndef WOLFTPM2_NO_WOLFCRYPT +static int TPM2_PCR_SetAuth(int argc, char *const argv[], + int isPolicy) +{ + int rc; + WOLFTPM2_DEV dev; + WOLFTPM2_SESSION session; + TPM2B_AUTH auth; + const char *pw = (argc < 4) ? NULL : argv[3]; + const char *key = argv[2]; + const ssize_t key_sz = strlen(key); + u32 pcrIndex = simple_strtoul(argv[1], NULL, 0); + + /* Need 3-4 args: command + pcr + auth + [platform_auth] */ + if (argc < 3 || argc > 4) { + return CMD_RET_USAGE; + } + + /* Init the TPM2 device for value/policy */ + rc = TPM2_Init_Device(&dev, NULL); + if (rc != TPM_RC_SUCCESS) return rc; + + /* Start the session */ + rc = wolfTPM2_StartSession(&dev, &session, NULL, NULL, + isPolicy ? TPM_SE_POLICY : TPM_SE_HMAC, TPM_ALG_NULL); + if (rc != TPM_RC_SUCCESS) { + log_debug("wolfTPM2_StartSession failed 0x%x: %s\n", rc, + TPM2_GetRCString(rc)); + wolfTPM2_Cleanup(&dev); + return rc; + } + + /* Set the platform auth if provided */ + if (pw) { + TPM2B_AUTH platformAuth; + memset(&platformAuth, 0, sizeof(platformAuth)); + platformAuth.size = strlen(pw); + XMEMCPY(platformAuth.buffer, pw, platformAuth.size); + rc = wolfTPM2_SetAuth(&dev, 0, TPM_RH_PLATFORM, + &platformAuth, 0, NULL); + if (rc != TPM_RC_SUCCESS) { + log_debug("wolfTPM2_SetAuth failed 0x%x: %s\n", rc, + TPM2_GetRCString(rc)); + wolfTPM2_UnloadHandle(&dev, &session.handle); + wolfTPM2_Cleanup(&dev); + return rc; + } + } + + printf("Setting %s auth for PCR %u\n", + isPolicy ? "policy" : "value", pcrIndex); + + /* Set up the auth value/policy */ + memset(&auth, 0, sizeof(auth)); + auth.size = key_sz; + XMEMCPY(auth.buffer, key, key_sz); + + if (isPolicy) { + /* Use TPM2_PCR_SetAuthPolicy command */ + PCR_SetAuthPolicy_In in; + memset(&in, 0, sizeof(in)); + in.authHandle = TPM_RH_PLATFORM; + in.authPolicy = auth; + in.hashAlg = TPM_ALG_SHA256; /* Default to SHA256 */ + in.pcrNum = pcrIndex; + rc = TPM2_PCR_SetAuthPolicy(&in); + } else { + /* Use TPM2_PCR_SetAuthValue command */ + PCR_SetAuthValue_In in; + memset(&in, 0, sizeof(in)); + in.pcrHandle = pcrIndex; + in.auth = auth; + rc = TPM2_PCR_SetAuthValue(&in); + } + + if (rc != TPM_RC_SUCCESS) { + log_debug("TPM2_PCR_SetAuth%s failed 0x%x: %s\n", + isPolicy ? "Policy" : "Value", + rc, TPM2_GetRCString(rc)); + } + + wolfTPM2_UnloadHandle(&dev, &session.handle); + wolfTPM2_Cleanup(&dev); + + log_debug("tpm2 set_auth %s: rc = %d (%s)\n", + isPolicy ? "Policy" : "Value", rc, TPM2_GetRCString(rc)); + + return rc; +} + +static int do_tpm2_pcr_setauthpolicy(struct cmd_tbl *cmdtp, int flag, + int argc, char *const argv[]) +{ + return TPM2_PCR_SetAuth(argc, argv, YES); +} + +static int do_tpm2_pcr_setauthvalue(struct cmd_tbl *cmdtp, int flag, + int argc, char *const argv[]) +{ + return TPM2_PCR_SetAuth(argc, argv, NO); +} + +static int do_tpm2_change_auth(struct cmd_tbl *cmdtp, int flag, + int argc, char *const argv[]) +{ + int rc; + WOLFTPM2_DEV dev; + WOLFTPM2_SESSION session; + const char *newpw = argv[2]; + const char *oldpw = (argc == 4) ? argv[3] : NULL; + const ssize_t newpw_sz = strlen(newpw); + const ssize_t oldpw_sz = oldpw ? strlen(oldpw) : 0; + HierarchyChangeAuth_In in; + TPM2B_AUTH newAuth; + + /* Need 3-4 args: command + hierarchy + new_pw + [old_pw] */ + if (argc < 3 || argc > 4) { + return CMD_RET_USAGE; + } + + /* Init the TPM2 device */ + rc = TPM2_Init_Device(&dev, NULL); + if (rc != TPM_RC_SUCCESS) return rc; + + memset(&in, 0, sizeof(in)); + + /* Set the handle */ + if (!strcmp(argv[1], "TPM2_RH_LOCKOUT")) + in.authHandle = TPM_RH_LOCKOUT; + else if (!strcmp(argv[1], "TPM2_RH_ENDORSEMENT")) + in.authHandle = TPM_RH_ENDORSEMENT; + else if (!strcmp(argv[1], "TPM2_RH_OWNER")) + in.authHandle = TPM_RH_OWNER; + else if (!strcmp(argv[1], "TPM2_RH_PLATFORM")) + in.authHandle = TPM_RH_PLATFORM; + else { + wolfTPM2_Cleanup(&dev); + return CMD_RET_USAGE; + } + + /* Validate password length if provided */ + if (newpw_sz > TPM_SHA256_DIGEST_SIZE || + oldpw_sz > TPM_SHA256_DIGEST_SIZE) { + wolfTPM2_Cleanup(&dev); + return -EINVAL; + } + + /* Start auth session */ + rc = wolfTPM2_StartSession(&dev, &session, NULL, NULL, + TPM_SE_HMAC, TPM_ALG_CFB); + if (rc != TPM_RC_SUCCESS) { + log_debug("wolfTPM2_StartSession failed 0x%x: %s\n", rc, + TPM2_GetRCString(rc)); + wolfTPM2_Cleanup(&dev); + return rc; + } + + /* If old password exists then set it as the current auth */ + if (oldpw) { + TPM2B_AUTH oldAuth; + memset(&oldAuth, 0, sizeof(oldAuth)); + oldAuth.size = oldpw_sz; + XMEMCPY(oldAuth.buffer, oldpw, oldpw_sz); + rc = wolfTPM2_SetAuthPassword(&dev, 0, &oldAuth); + if (rc != TPM_RC_SUCCESS) { + log_debug("wolfTPM2_SetAuthPassword failed 0x%x: %s\n", rc, + TPM2_GetRCString(rc)); + wolfTPM2_UnloadHandle(&dev, &session.handle); + wolfTPM2_Cleanup(&dev); + return rc; + } + } + + memset(&newAuth, 0, sizeof(newAuth)); + newAuth.size = newpw_sz; + XMEMCPY(newAuth.buffer, newpw, newpw_sz); + in.newAuth = newAuth; + + /* Change the auth based on the hierarchy */ + rc = wolfTPM2_ChangeHierarchyAuth(&dev, &session, in.authHandle); + if (rc != TPM_RC_SUCCESS) { + log_debug("wolfTPM2_ChangeHierarchyAuth failed 0x%x: %s\n", rc, + TPM2_GetRCString(rc)); + } else { + log_debug("Successfully changed auth for %s\n", argv[1]); + } + + wolfTPM2_UnloadHandle(&dev, &session.handle); + wolfTPM2_Cleanup(&dev); + + log_debug("tpm2 change_auth: rc = %d (%s)\n", rc, TPM2_GetRCString(rc)); + + return rc; +} +#endif /* !WOLFTPM2_NO_WOLFCRYPT */ + +static int do_tpm2_pcr_print(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[]) +{ + int rc; + WOLFTPM2_DEV dev; + + /* Need 1 arg: command */ + if (argc != 1) { + return CMD_RET_USAGE; + } + + /* Init the TPM2 device */ + rc = TPM2_Init_Device(&dev, NULL); + if (rc == TPM_RC_SUCCESS) { + /* Print the current PCR state */ + TPM2_PCRs_Print(); + } + wolfTPM2_Cleanup(&dev); + + log_debug("tpm2 pcr_print: rc = %d (%s)\n", rc, TPM2_GetRCString(rc)); + + return rc; +} + +static int do_tpm2_dam_reset(struct cmd_tbl *cmdtp, int flag, + int argc, char *const argv[]) +{ + int rc; + WOLFTPM2_DEV dev; + const char *pw = (argc < 2) ? NULL : argv[1]; + const ssize_t pw_sz = pw ? strlen(pw) : 0; + DictionaryAttackLockReset_In in; + TPM2_AUTH_SESSION session[MAX_SESSION_NUM]; + + /* Need 1-2 args: command + [password] */ + if (argc > 2) { + return CMD_RET_USAGE; + } + + /* Validate password length if provided */ + if (pw && pw_sz > TPM_SHA256_DIGEST_SIZE) { + log_debug("Error: Password too long\n"); + return -EINVAL; + } + + /* Init the TPM2 device */ + rc = TPM2_Init_Device(&dev, NULL); + if (rc == TPM_RC_SUCCESS) { + /* set lock handle */ + memset(&in, 0, sizeof(in)); + in.lockHandle = TPM_RH_LOCKOUT; + + /* Setup auth session only if password provided */ + memset(session, 0, sizeof(session)); + session[0].sessionHandle = TPM_RS_PW; + if (pw) { + session[0].auth.size = pw_sz; + XMEMCPY(session[0].auth.buffer, pw, pw_sz); + } + TPM2_SetSessionAuth(session); + + rc = TPM2_DictionaryAttackLockReset(&in); + log_debug("TPM2_Dam_Reset: Result = 0x%x (%s)\n", rc, + TPM2_GetRCString(rc)); + } + wolfTPM2_Cleanup(&dev); + + log_debug("tpm2 dam_reset: rc = %d (%s)\n", rc, TPM2_GetRCString(rc)); + + return rc; +} + +static int do_tpm2_dam_parameters(struct cmd_tbl *cmdtp, int flag, + int argc, char *const argv[]) +{ + int rc; + WOLFTPM2_DEV dev; + const char *pw = (argc < 5) ? NULL : argv[4]; + const ssize_t pw_sz = pw ? strlen(pw) : 0; + DictionaryAttackParameters_In in; + TPM2_AUTH_SESSION session[MAX_SESSION_NUM]; + + /* Need 4-5 args: command + max_tries + recovery_time + + * lockout_recovery + [password] */ + if (argc < 4 || argc > 5) { + return CMD_RET_USAGE; + } + + /* Validate password length if provided */ + if (pw && pw_sz > TPM_SHA256_DIGEST_SIZE) { + log_debug("Error: Password too long\n"); + return -EINVAL; + } + + /* Init the TPM2 device */ + rc = TPM2_Init_Device(&dev, NULL); + if (rc == TPM_RC_SUCCESS) { + /* Set parameters */ + memset(&in, 0, sizeof(in)); + in.newMaxTries = simple_strtoul(argv[1], NULL, 0); + in.newRecoveryTime = simple_strtoul(argv[2], NULL, 0); + in.lockoutRecovery = simple_strtoul(argv[3], NULL, 0); + + /* set lock handle */ + in.lockHandle = TPM_RH_LOCKOUT; + + /* Setup auth session only if password provided */ + memset(session, 0, sizeof(session)); + session[0].sessionHandle = TPM_RS_PW; + if (pw) { + session[0].auth.size = pw_sz; + XMEMCPY(session[0].auth.buffer, pw, pw_sz); + } + TPM2_SetSessionAuth(session); + + /* Set DAM parameters */ + rc = TPM2_DictionaryAttackParameters(&in); + if (rc != TPM_RC_SUCCESS) { + log_debug("TPM2_DictionaryAttackParameters failed 0x%x: %s\n", rc, + TPM2_GetRCString(rc)); + } + + printf("Changing dictionary attack parameters:\n"); + printf(" maxTries: %u\n", in.newMaxTries); + printf(" recoveryTime: %u\n", in.newRecoveryTime); + printf(" lockoutRecovery: %u\n", in.lockoutRecovery); + } + wolfTPM2_Cleanup(&dev); + + log_debug("tpm2 dam_parameters: rc = %d (%s)\n", rc, TPM2_GetRCString(rc)); + + return rc; +} + +static struct cmd_tbl wolftpm_cmds[] = { + U_BOOT_CMD_MKENT(device, 2, 1, do_tpm2_device, "", ""), + U_BOOT_CMD_MKENT(info, 1, 1, do_tpm2_info, "", ""), + U_BOOT_CMD_MKENT(state, 1, 1, do_tpm2_state, "", ""), + U_BOOT_CMD_MKENT(init, 1, 1, do_tpm2_init, "", ""), + U_BOOT_CMD_MKENT(autostart, 1, 1, do_tpm2_autostart, "", ""), + U_BOOT_CMD_MKENT(startup, 3, 1, do_tpm2_startup, "", ""), + U_BOOT_CMD_MKENT(self_test, 2, 1, do_tpm2_selftest, "", ""), + U_BOOT_CMD_MKENT(clear, 2, 1, do_tpm2_clear, "", ""), + U_BOOT_CMD_MKENT(pcr_extend, 4, 1, do_tpm2_pcr_extend, "", ""), + U_BOOT_CMD_MKENT(pcr_read, 4, 1, do_tpm2_pcr_read, "", ""), + U_BOOT_CMD_MKENT(pcr_allocate, 4, 1, do_tpm2_pcr_allocate, "", ""), + U_BOOT_CMD_MKENT(pcr_print, 1, 1, do_tpm2_pcr_print, "", ""), +#ifndef WOLFTPM2_NO_WOLFCRYPT + U_BOOT_CMD_MKENT(change_auth, 4, 1, do_tpm2_change_auth, "", ""), + U_BOOT_CMD_MKENT(pcr_setauthpolicy, 4, 1, do_tpm2_pcr_setauthpolicy, "", ""), + U_BOOT_CMD_MKENT(pcr_setauthvalue, 4, 1, do_tpm2_pcr_setauthvalue, "", ""), +#endif + U_BOOT_CMD_MKENT(get_capability, 5, 1, do_tpm2_wrapper_getcapsargs, "", ""), + U_BOOT_CMD_MKENT(dam_reset, 2, 1, do_tpm2_dam_reset, "", ""), + U_BOOT_CMD_MKENT(dam_parameters, 5, 1, do_tpm2_dam_parameters, "", ""), + U_BOOT_CMD_MKENT(caps, 1, 1, do_tpm2_wrapper_capsargs, "", ""), +#ifdef WOLFTPM_FIRMWARE_UPGRADE +#if defined(WOLFTPM_SLB9672) || defined(WOLFTPM_SLB9673) + U_BOOT_CMD_MKENT(firmware_update, 5, 1, do_tpm2_firmware_update, "", ""), + U_BOOT_CMD_MKENT(firmware_cancel, 1, 1, do_tpm2_firmware_cancel, "", ""), +#endif +#endif +}; + +struct cmd_tbl *get_wolftpm_commands(unsigned int *size) +{ + *size = ARRAY_SIZE(wolftpm_cmds); + + return wolftpm_cmds; +} + +static int do_wolftpm(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + struct cmd_tbl *cmd; + + if (argc < 2) + return CMD_RET_USAGE; + + cmd = find_cmd_tbl(argv[1], wolftpm_cmds, ARRAY_SIZE(wolftpm_cmds)); + if (!cmd) + return CMD_RET_USAGE; + + return cmd->cmd(cmdtp, flag, argc - 1, argv + 1); +} + +U_BOOT_CMD( + tpm2, /* name of cmd - replaces standard tpm2 when wolfTPM enabled */ + CONFIG_SYS_MAXARGS, /* max args */ + 1, /* repeatable */ + do_wolftpm, /* function */ + "Issue a TPMv2.x command (wolfTPM)", + " []\n" + "\n" + "Commands:\n" + "help\n" + " Show this help text\n" + "device [num device]\n" + " Show all devices or set the specified device\n" + "info\n" + " Show information about the TPM.\n" + "state\n" + " Show internal state from the TPM (if available)\n" + "autostart\n" + " Initalize the tpm, perform a Startup(clear) and run a full selftest\n" + " sequence\n" + "init\n" + " Initialize the software stack. Always the first command to issue.\n" + " 'tpm startup' is the only acceptable command after a 'tpm init' has been\n" + " issued\n" + "startup []\n" + " Issue a TPM2_Startup command.\n" + " is one of:\n" + " * TPM2_SU_CLEAR (reset state)\n" + " * TPM2_SU_STATE (preserved state)\n" + " []: optional shutdown\n" + " * off - To shutdown the TPM\n" + "self_test \n" + " Test the TPM capabilities.\n" + " is one of:\n" + " * full (perform all tests)\n" + " * continue (only check untested tests)\n" + "clear \n" + " Issue a TPM2_Clear command.\n" + " is one of:\n" + " * TPM2_RH_LOCKOUT\n" + " * TPM2_RH_PLATFORM\n" + "pcr_extend []\n" + " Extend PCR # with digest at with digest_algo.\n" + " : index of the PCR\n" + " : address of digest of digest_algo type (defaults to SHA256)\n" + " []: algorithm to use for digest\n" + "pcr_read []\n" + " Read PCR # to memory address with .\n" + " : index of the PCR\n" + " : address of digest of digest_algo type (defaults to SHA256)\n" + " []: algorithm to use for digest\n" + "pcr_print\n" + " Prints the current PCR state\n" + "caps\n" + " Show TPM capabilities and info\n" + "get_capability \n" + " Read and display entries indexed by /.\n" + " Values are 4 bytes long and are written at .\n" + " : capability\n" + " : property\n" + " : address to store entries of 4 bytes\n" + " : number of entries to retrieve\n" + "dam_reset []\n" + " If the TPM is not in a LOCKOUT state, reset the internal error counter.\n" + " []: optional password\n" + "dam_parameters []\n" + " If the TPM is not in a LOCKOUT state, sets the DAM parameters\n" + " : maximum number of failures before lockout,\n" + " 0 means always locking\n" + " : time before decrement of the error counter,\n" + " 0 means no lockout\n" + " : time of a lockout (before the next try),\n" + " 0 means a reboot is needed\n" + " []: optional password of the LOCKOUT hierarchy\n" + "change_auth []\n" + " : the hierarchy\n" + " * TPM2_RH_LOCKOUT\n" + " * TPM2_RH_ENDORSEMENT\n" + " * TPM2_RH_OWNER\n" + " * TPM2_RH_PLATFORM\n" + " : new password for \n" + " []: optional previous password of \n" + "pcr_setauthpolicy | pcr_setauthvalue []\n" + " Change the to access PCR #.\n" + " : index of the PCR\n" + " : secret to protect the access of PCR #\n" + " []: optional password of the PLATFORM hierarchy\n" + "pcr_allocate []\n" + " Issue a TPM2_PCR_Allocate Command to reconfig PCR bank algorithm.\n" + " is one of:\n" + " * SHA1\n" + " * SHA256\n" + " * SHA384\n" + " * SHA512\n" + " is one of:\n" + " * on - Select all available PCRs associated with the specified\n" + " algorithm (bank)\n" + " * off - Clear all available PCRs associated with the specified\n" + " algorithm (bank)\n" + " []: optional password\n" + +#ifdef WOLFTPM_FIRMWARE_UPGRADE +#if defined(WOLFTPM_SLB9672) || defined(WOLFTPM_SLB9673) + "firmware_update \n" + " Update TPM firmware\n" + "firmware_cancel\n" + " Cancel TPM firmware update\n" +#endif +#endif +); + +#endif /* !WOLFTPM2_NO_WRAPPER */ + +/******************************************************************************/ +/* --- END TPM 2.0 Commands -- */ +/******************************************************************************/ diff --git a/configs/rpi_4_defconfig b/configs/rpi_4_defconfig index 8362242b97f..22280d9b70e 100644 --- a/configs/rpi_4_defconfig +++ b/configs/rpi_4_defconfig @@ -69,4 +69,31 @@ CONFIG_SYS_WHITE_ON_BLACK=y CONFIG_VIDEO_BCM2835=y CONFIG_CONSOLE_SCROLL_LINES=10 CONFIG_PHYS_TO_BUS=y -# CONFIG_HEXDUMP is not set +# HEXDUMP enabled for unit tests + +# SPI support (hardware SPI - matches Linux tpm-slb9670 overlay) +CONFIG_SPI=y +CONFIG_DM_SPI=y +CONFIG_BCM2835_SPI=y +# CONFIG_SOFT_SPI is not set +CONFIG_CMD_SPI=y + +# TPM and wolfTPM support +CONFIG_TPM=y +CONFIG_TPM_V2=y +CONFIG_CMD_TPM=y +CONFIG_TPM_WOLF=y +# CONFIG_WOLFTPM_LINUX_DEV is not set +CONFIG_WOLFTPM_SLB9672=y +CONFIG_CMD_WOLFTPM=y + +# Logging (debug level to see log_debug output) +CONFIG_LOG=y +CONFIG_LOGLEVEL=7 +CONFIG_LOG_MAX_LEVEL=7 +CONFIG_LOG_DEFAULT_LEVEL=7 + +# Unit testing support +CONFIG_UNIT_TEST=y +CONFIG_CONSOLE_RECORD=y +CONFIG_HEXDUMP=y diff --git a/doc/usage/cmd/wolftpm.rst b/doc/usage/cmd/wolftpm.rst new file mode 100644 index 00000000000..4b5fb251b35 --- /dev/null +++ b/doc/usage/cmd/wolftpm.rst @@ -0,0 +1,694 @@ +wolfTPM Support For Das U-Boot +============================== + +wolfTPM provides experimental support for U-Boot with the following key features: + +- Utilizes SOFT SPI driver in U-Boot for TPM communication +- Implements TPM 2.0 driver functionality through its internal TIS layer +- Provides native API access to all TPM 2.0 commands +- Includes wrapper API for common TPM 2.0 operations +- Supports two integration paths: + - ``__linux__``: Uses existing tpm interface via tpm2_linux.c + - ``__UBOOT__``: Direct SPI communication through tpm_io_uboot.c + +wolfTPM U-Boot Commands +---------------------- + +The following commands are available through the ``tpm2`` command (powered by wolfTPM): + +Basic Commands +~~~~~~~~~~~~~~ + +- ``help`` - Show help text +- ``device [num device]`` - Show all devices or set the specified device +- ``info`` - Show information about the TPM +- ``state`` - Show internal state from the TPM (if available) +- ``autostart`` - Initialize the TPM, perform a Startup(clear) and run a full selftest sequence +- ``init`` - Initialize the software stack (must be first command) +- ``startup []`` - Issue a TPM2_Startup command + - ````: TPM2_SU_CLEAR (reset state) or TPM2_SU_STATE (preserved state) + - ``[]``: optional shutdown with "off" +- ``self_test `` - Test TPM capabilities + - ````: "full" (all tests) or "continue" (untested tests only) + +PCR Operations +~~~~~~~~~~~~~~ + +- ``pcr_extend []`` - Extend PCR with digest +- ``pcr_read []`` - Read PCR to memory +- ``pcr_allocate []`` - Reconfig PCR bank algorithm +- ``pcr_setauthpolicy | pcr_setauthvalue []`` - Change PCR access key +- ``pcr_print`` - Print current PCR state + +Security Management +~~~~~~~~~~~~~~~~~~~ + +- ``clear `` - Issue TPM2_Clear command + - ````: TPM2_RH_LOCKOUT or TPM2_RH_PLATFORM +- ``change_auth []`` - Change hierarchy password + - ````: TPM2_RH_LOCKOUT, TPM2_RH_ENDORSEMENT, TPM2_RH_OWNER, or TPM2_RH_PLATFORM +- ``dam_reset []`` - Reset internal error counter +- ``dam_parameters []`` - Set DAM parameters +- ``caps`` - Show TPM capabilities and info + +Firmware Management +~~~~~~~~~~~~~~~~~~~ + +- ``firmware_update `` - Update TPM firmware +- ``firmware_cancel`` - Cancel TPM firmware update + +Infineon TPM Firmware Update Guide +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +**WARNING: Firmware updates are risky. A failed update can brick your TPM. +Only proceed if you have a valid reason to update (security patches, new features) +and understand the risks.** + +The firmware update commands are for Infineon SLB9672/SLB9673 TPMs only. The process +requires extracting manifest and firmware data from Infineon's combined ``.BIN`` file. + +**Prerequisites:** + +- Infineon firmware file (e.g., ``TPM20_16.13.17733.0_R1.BIN``) +- wolfTPM's ``ifx_fw_extract`` tool (in ``lib/wolftpm/examples/firmware/``) +- Your TPM's KeyGroupId (shown by ``tpm2 caps`` command) + +**Step 1: Get your TPM's KeyGroupId** + +Run ``tpm2 caps`` to find your TPM's KeyGroupId:: + + U-Boot> tpm2 caps + Mfg IFX (1), Vendor SLB9672, Fw 16.13 (0x4545), FIPS 140-2 1, CC-EAL4 1 + Operational mode: Normal TPM operational mode (0x0) + KeyGroupId 0x5, FwCounter 1255 (255 same) + ... + +In this example, KeyGroupId is ``0x5``. + +**Step 2: Build the extraction tool (on host machine)** + +:: + + cd lib/wolftpm/examples/firmware + gcc -o ifx_fw_extract ifx_fw_extract.c + +**Step 3: List available key groups in firmware file** + +:: + + ./ifx_fw_extract TPM20_16.13.17733.0_R1.BIN + Found group 00000005 + +Verify your TPM's KeyGroupId matches one in the firmware file. + +**Step 4: Extract manifest and firmware data** + +Use your KeyGroupId (0x5 in this example):: + + ./ifx_fw_extract TPM20_16.13.17733.0_R1.BIN 0x5 manifest.bin firmware.bin + Found group 00000005 + Chosen group found: 00000005 + Manifest size is 3229 + Data size is 925539 + Wrote 3229 bytes to manifest.bin + Wrote 925539 bytes to firmware.bin + +**Step 5: Copy files to SD card** + +Copy ``manifest.bin`` and ``firmware.bin`` to your boot partition (FAT):: + + cp manifest.bin firmware.bin /Volumes/bootfs/ # macOS + cp manifest.bin firmware.bin /boot/firmware/ # Linux + +**Step 6: Load files into memory** + +In U-Boot, load the files from SD card into RAM:: + + U-Boot> fatload mmc 0:1 0x10000000 manifest.bin + 3229 bytes read in 32 ms (97.7 KiB/s) + + U-Boot> fatload mmc 0:1 0x10100000 firmware.bin + 925539 bytes read in 86 ms (10.3 MiB/s) + +**Step 7: Perform firmware update (CAUTION!)** + +Convert file sizes to hex: + +- manifest.bin: 3229 bytes = 0xC9D +- firmware.bin: 925539 bytes = 0xE1F63 + +Run the firmware update:: + + U-Boot> tpm2 firmware_update 0x10000000 0xC9D 0x10100000 0xE1F63 + TPM2 Firmware Update + Infineon Firmware Update Tool + Manifest Address: 0x10000000 (size: 3229) + Firmware Address: 0x10100000 (size: 925539) + tpm2 init: rc = 0 (Success) + Mfg IFX (1), Vendor SLB9672, Fw 16.13 (0x4545) + Operational mode: Normal TPM operational mode (0x0) + KeyGroupId 0x5, FwCounter 1255 (255 same) + Firmware Update (normal mode): + Mfg IFX (1), Vendor SLB9672, Fw 16.13 (0x4545) + Operational mode: Normal TPM operational mode (0x0) + KeyGroupId 0x5, FwCounter 1255 (255 same) + tpm2 firmware_update: rc=0 (Success) + +**DO NOT power off or reset during the update!** + +**Step 8: Verify update** + +After the update completes, verify with:: + + U-Boot> tpm2 caps + +The firmware version should show the new version. + +**Recovery Mode:** + +If the TPM enters recovery mode (opMode shows 0x02 or 0x8x), the firmware update +command will automatically use recovery mode. You may need to run the update again +to complete the process. + +Canceling a Firmware Update +^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +If an update is in progress and needs to be abandoned (opMode 0x01), use:: + + U-Boot> tpm2 firmware_cancel + tpm2 init: rc = 0 (Success) + tpm2 firmware_cancel: rc=0 (Success) + +**IMPORTANT: After running firmware_cancel, you MUST reboot/power cycle the system +before running any other TPM commands.** If you attempt to run commands without +rebooting, you will get ``TPM_RC_REBOOT`` (error 304):: + + U-Boot> tpm2 firmware_update ... + tpm2 init: rc = 304 (TPM_RC_REBOOT) + Infineon firmware update failed 0x130: TPM_RC_REBOOT + +After rebooting, the TPM will return to normal operation and you can retry the +firmware update or continue with normal TPM operations. + +**Note:** If no firmware update is in progress, ``firmware_cancel`` returns +``TPM_RC_COMMAND_CODE`` (0x143), which is expected and harmless:: + + U-Boot> tpm2 firmware_cancel + tpm2 firmware_cancel: rc=323 (TPM_RC_COMMAND_CODE) + +Enabling wolfTPM in U-Boot +-------------------------- + +Enable wolfTPM support in U-Boot by adding these options to your board's defconfig:: + + CONFIG_TPM=y + CONFIG_TPM_V2=y + CONFIG_TPM_WOLF=y + CONFIG_CMD_WOLFTPM=y + + if with __LINUX__: + CONFIG_TPM_LINUX_DEV=y + +Or use ``make menuconfig`` and enable: + +Enabling Debug Output +~~~~~~~~~~~~~~~~~~~~~ + +wolfTPM commands use U-Boot's logging system (``log_debug()``). To enable debug +output, you must first enable the logging subsystem in your board's defconfig:: + + CONFIG_LOG=y + CONFIG_LOG_MAX_LEVEL=7 + CONFIG_LOG_DEFAULT_LEVEL=7 + +Or via ``make menuconfig``: + +- Console → Enable logging support +- Console → Maximum log level to record = 7 +- Console → Default logging level to display = 7 + +Log levels: +- 7 = DEBUG (to show wolfTPM command debug output) + +**Note:** Without ``CONFIG_LOG=y``, the ``log level`` command will not exist +and ``log_debug()`` calls will produce no output. + +wolfTPM Library Debug +^^^^^^^^^^^^^^^^^^^^^ + +For lower-level wolfTPM library debug output (TPM protocol messages), edit +``include/configs/user_settings.h`` and uncomment:: + + #define DEBUG_WOLFTPM /* Basic wolfTPM debug messages */ + #define WOLFTPM_DEBUG_VERBOSE /* Verbose debug messages */ + #define WOLFTPM_DEBUG_IO /* IO-level debug (SPI transfers) */ + +After enabling, rebuild U-Boot:: + + make clean + make -j4 + +Menuconfig Paths +^^^^^^^^^^^^^^^^ + +The following menuconfig paths are useful for wolfTPM: + +- Device Drivers → TPM → TPM 2.0 Support +- Device Drivers → TPM → wolfTPM Support +- Command line interface → Security commands → Enable wolfTPM commands +- Console → Enable logging support (for ``log_debug()`` output) + +Building and Running wolfTPM with U-Boot using QEMU +--------------------------------------------------- + +To build and run wolfTPM with U-Boot using QEMU and a TPM simulator, follow these steps: + +1. Install swtpm:: + + git clone https://github.com/stefanberger/swtpm.git + cd swtpm + ./autogen.sh + make + +2. Build U-Boot:: + + make distclean + export CROSS_COMPILE=aarch64-linux-gnu- + export ARCH=aarch64 + make qemu_arm64_defconfig + make -j4 + +3. Create TPM directory:: + + mkdir -p /tmp/mytpm1 + +4. Start swtpm (in first terminal):: + + swtpm socket --tpm2 --tpmstate dir=/tmp/mytpm1 --ctrl type=unixio,path=/tmp/mytpm1/swtpm-sock --log level=20 + +5. Start QEMU (in second terminal):: + + qemu-system-aarch64 -machine virt -nographic -cpu cortex-a57 -bios u-boot.bin -chardev socket,id=chrtpm,path=/tmp/mytpm1/swtpm-sock -tpmdev emulator,id=tpm0,chardev=chrtpm -device tpm-tis-device,tpmdev=tpm0 + +6. Example output:: + + U-Boot 2025.07-rc1-ge15cbf232ddf-dirty (May 06 2025 - 16:25:56 -0700) + ... + => tpm2 help + tpm2 - Issue a TPMv2.x command + Usage: + tpm2 [] + ... + => tpm2 info + tpm_tis@0 v2.0: VendorID 0x1014, DeviceID 0x0001, RevisionID 0x01 [open] + => tpm2 startup TPM2_SU_CLEAR + => tpm2 get_capability 0x6 0x20e 0x200 1 + Capabilities read from TPM: + Property 0x6a2e45a9: 0x6c3646a9 + => tpm2 pcr_read 10 0x100000 + PCR #10 sha256 32 byte content (20 known updates): + 20 25 73 0a 00 56 61 6c 75 65 3a 0a 00 23 23 20 + 4f 75 74 20 6f 66 20 6d 65 6d 6f 72 79 0a 00 23 + +7. Example commands:: + + => tpm2 info + tpm_tis@0 v2.0: VendorID 0x1014, DeviceID 0x0001, RevisionID 0x01 [open] + ... + => tpm2 pcr_read 10 0x100000 + PCR #10 sha256 32 byte content (20 known updates): + 20 25 73 0a 00 56 61 6c 75 65 3a 0a 00 23 23 20 + 4f 75 74 20 6f 66 20 6d 65 6d 6f 72 79 0a 00 23 + +8. Exiting the QEMU: + Press Ctrl-A followed by X + +Testing wolfTPM +--------------- + +wolfTPM includes a comprehensive test suite based on the existing TPM2 tests. +The tests are located in: + +- ``test/cmd/wolftpm.c`` - C unit tests (based on ``test/dm/tpm.c`` and ``test/cmd/hash.c``) +- ``test/py/tests/test_wolftpm.py`` - Python integration tests (based on ``test/py/tests/test_tpm2.py``) + +Running C Unit Tests +~~~~~~~~~~~~~~~~~~~~ + +The C unit tests use the U-Boot test framework and can be run in sandbox mode +or on real hardware. To run all wolfTPM tests:: + + # Build sandbox with tests enabled + make sandbox_defconfig + # Enable wolfTPM in menuconfig + make menuconfig + make -j4 + + # Run U-Boot sandbox + ./u-boot -T + + # In U-Boot sandbox, run the unit tests + => ut cmd + +Individual tests can be run by name:: + + => ut cmd cmd_test_wolftpm_autostart + => ut cmd cmd_test_wolftpm_init + => ut cmd cmd_test_wolftpm_self_test + => ut cmd cmd_test_wolftpm_caps + => ut cmd cmd_test_wolftpm_clear + => ut cmd cmd_test_wolftpm_pcr_read + => ut cmd cmd_test_wolftpm_pcr_extend + +Running Python Tests +~~~~~~~~~~~~~~~~~~~~ + +The Python tests require pytest and can be run against real hardware or QEMU +with swtpm. First, ensure swtpm is running (see QEMU instructions above). + +To run all wolfTPM Python tests:: + + # From the U-Boot root directory + ./test/py/test.py --bd qemu_arm64 -k test_wolftpm + +To run individual Python tests:: + + ./test/py/test.py --bd qemu_arm64 -k test_wolftpm_autostart + ./test/py/test.py --bd qemu_arm64 -k test_wolftpm_caps + ./test/py/test.py --bd qemu_arm64 -k test_wolftpm_pcr_read + +To skip wolfTPM tests (e.g., when no TPM hardware is available), set the +environment variable in your board configuration:: + + env__wolftpm_device_test_skip = True + +Running Tests Manually in QEMU +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +You can also test wolfTPM commands manually in QEMU: + +1. Start swtpm:: + + mkdir -p /tmp/mytpm + swtpm socket --tpm2 --tpmstate dir=/tmp/mytpm \ + --ctrl type=unixio,path=/tmp/mytpm/swtpm-sock --log level=20 + +2. Start QEMU with TPM:: + + qemu-system-aarch64 -machine virt -cpu cortex-a57 -m 1024 \ + -bios u-boot.bin \ + -chardev socket,id=chrtpm,path=/tmp/mytpm/swtpm-sock \ + -tpmdev emulator,id=tpm0,chardev=chrtpm \ + -device tpm-tis-device,tpmdev=tpm0 \ + -nographic + +3. Run wolfTPM commands at the U-Boot prompt:: + + => wolftpm autostart + => wolftpm caps + => wolftpm pcr_read 0 sha256 + => wolftpm pcr_print + => wolftpm self_test full + => wolftpm clear TPM2_RH_LOCKOUT + => wolftpm dam_parameters 3 10 0 + +Test Coverage +~~~~~~~~~~~~~ + +The test suite covers the following wolfTPM functionality: + ++---------------------------+------------------------------------------+ +| Test Name | Description | ++===========================+==========================================+ +| wolftpm_autostart | TPM initialization and startup | ++---------------------------+------------------------------------------+ +| wolftpm_init | TPM device initialization | ++---------------------------+------------------------------------------+ +| wolftpm_self_test | Full TPM self-test | ++---------------------------+------------------------------------------+ +| wolftpm_self_test_continue| Continue incomplete self-tests | ++---------------------------+------------------------------------------+ +| wolftpm_caps | Read TPM capabilities | ++---------------------------+------------------------------------------+ +| wolftpm_clear | Clear TPM state | ++---------------------------+------------------------------------------+ +| wolftpm_pcr_read | Read PCR values | ++---------------------------+------------------------------------------+ +| wolftpm_pcr_extend | Extend PCR with digest | ++---------------------------+------------------------------------------+ +| wolftpm_pcr_print | Print all PCR values | ++---------------------------+------------------------------------------+ +| wolftpm_pcr_allocate | Reconfigure PCR bank algorithm | ++---------------------------+------------------------------------------+ +| wolftpm_dam_reset | Reset DAM counter | ++---------------------------+------------------------------------------+ +| wolftpm_dam_parameters | Set DAM parameters | ++---------------------------+------------------------------------------+ +| wolftpm_change_auth | Change hierarchy password | ++---------------------------+------------------------------------------+ +| wolftpm_info | Display TPM info | ++---------------------------+------------------------------------------+ +| wolftpm_state | Display TPM state | ++---------------------------+------------------------------------------+ +| wolftpm_device | Show/set TPM device | ++---------------------------+------------------------------------------+ +| wolftpm_startup_clear | TPM2_Startup with CLEAR mode | ++---------------------------+------------------------------------------+ +| wolftpm_startup_state | TPM2_Startup with STATE mode | ++---------------------------+------------------------------------------+ +| wolftpm_startup_shutdown | TPM2_Shutdown command | ++---------------------------+------------------------------------------+ +| wolftpm_get_capability | Read TPM capabilities by property | ++---------------------------+------------------------------------------+ + +TODO: Commands Not Yet Tested +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The following commands are implemented in ``cmd/wolftpm.c`` but do not yet have +test coverage due to special requirements: + ++---------------------------+------------------------------------------+------------------+ +| Command | Description | Notes | ++===========================+==========================================+==================+ +| pcr_setauthpolicy | Set PCR authorization policy | Requires | +| | | wolfCrypt | ++---------------------------+------------------------------------------+------------------+ +| pcr_setauthvalue | Set PCR authorization value | Requires | +| | | wolfCrypt | ++---------------------------+------------------------------------------+------------------+ +| firmware_update | Update TPM firmware (Infineon only) | Requires | +| | | Infineon HW | ++---------------------------+------------------------------------------+------------------+ +| firmware_cancel | Cancel firmware update (Infineon only) | Requires | +| | | Infineon HW | ++---------------------------+------------------------------------------+------------------+ + +**Note:** The ``pcr_setauthpolicy`` and ``pcr_setauthvalue`` commands require +``WOLFTPM2_NO_WOLFCRYPT`` to be undefined (i.e., wolfCrypt must be enabled). +The ``firmware_update`` and ``firmware_cancel`` commands require Infineon +SLB9672/SLB9673 hardware. + +To add tests for these commands: + +1. Add C unit test in ``test/cmd/wolftpm.c``:: + + static int cmd_test_wolftpm_(struct unit_test_state *uts) + { + ut_assertok(run_command("wolftpm autostart", 0)); + ut_assertok(run_command("wolftpm ", 0)); + return 0; + } + CMD_TEST(cmd_test_wolftpm_, 0); + +2. Add Python test in ``test/py/tests/test_wolftpm.py``:: + + @pytest.mark.buildconfigspec('tpm_wolf') + def test_wolftpm_(ubman): + force_init(ubman) + ubman.run_command('wolftpm ') + output = ubman.run_command('echo $?') + assert output.endswith('0') + +TODO: Testing on Raspberry Pi Hardware +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +For testing with real TPM hardware (e.g., Infineon SLB9672 TPM HAT on Raspberry Pi): + +1. Build U-Boot for Raspberry Pi:: + + make distclean + export CROSS_COMPILE=aarch64-linux-gnu- + export ARCH=aarch64 + make rpi_arm64_defconfig + make -j$(nproc) + +2. Backup current boot configuration:: + + sudo cp /boot/firmware/config.txt /boot/firmware/config.txt.backup + +3. Copy U-Boot to boot partition:: + + sudo cp u-boot.bin /boot/firmware/ + +4. Edit ``/boot/firmware/config.txt`` and add:: + + # U-Boot for wolfTPM testing + enable_uart=1 + kernel=u-boot.bin + arm_64bit=1 + +5. Connect serial console (recommended) - USB-to-serial adapter on GPIO 14/15 + (pins 8/10) at 115200 baud. + +6. Reboot and test at U-Boot prompt:: + + U-Boot> tpm2 device + U-Boot> tpm2 info + U-Boot> tpm2 autostart + U-Boot> tpm2 caps + U-Boot> tpm2 pcr_read 0 0x1000000 SHA256 + +7. To restore normal Linux boot:: + + sudo cp /boot/firmware/config.txt.backup /boot/firmware/config.txt + sudo reboot + +**Note:** The Raspberry Pi build uses GPIO-based soft SPI for TPM communication. +Standard SPI0 pins are used: GPIO 11 (SCLK), GPIO 10 (MOSI), GPIO 9 (MISO), +GPIO 7 (CE1 for TPM). Adjust ``arch/arm/dts/bcm2711-rpi-4-b-u-boot.dtsi`` if +your TPM HAT uses different GPIO pins. + +TODO: Python Test Framework +~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The Python tests in ``test/py/tests/test_wolftpm.py`` require additional setup +for QEMU boards (external ``u-boot-test-flash``, ``u-boot-test-console``, etc. +scripts). The C unit tests (``test/cmd/wolftpm.c``) provide equivalent coverage +and work directly with ``ut cmd`` in U-Boot. + +**Status:** Python tests need verification with proper QEMU test infrastructure +or deprecation in favor of C unit tests which are fully functional. + +Testing wolfTPM SPI Path in Sandbox +----------------------------------- + +The sandbox build includes a TPM SPI emulator that tests the complete wolfTPM +SPI communication path without requiring hardware. This validates the SPI HAL +code path in ``lib/wolftpm/hal/tpm_io_uboot.c``. + +SPI Code Path +~~~~~~~~~~~~~ + +When testing in sandbox, the following code path is exercised:: + + wolftpm commands (cmd/wolftpm.c) + ↓ + wolfTPM library (lib/wolftpm/) + ↓ + SPI HAL (lib/wolftpm/hal/tpm_io_uboot.c) + ↓ + U-Boot SPI API (spi_get_bus_and_cs, spi_xfer) + ↓ + Sandbox SPI Master (drivers/spi/sandbox_spi.c) + ↓ + TPM SPI Emulator (drivers/tpm/tpm_spi_sandbox.c) + +The TPM SPI emulator implements the TPM TIS (Trusted Platform Module Interface +Specification) over SPI protocol: + +- 4-byte SPI header: ``[R/W|len-1][0xD4][addr_hi][addr_lo]`` +- Wait-state handling with ready byte +- TIS register emulation (ACCESS, STS, FIFO, DID/VID, RID) +- TIS state machine (IDLE → READY → RECEPTION → COMPLETION) + +Building Sandbox with SPI Support +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +To build sandbox with TPM SPI emulator support:: + + make sandbox_defconfig + make -j$(nproc) + +The following configs are enabled by default in sandbox: + +- ``CONFIG_TPM=y`` +- ``CONFIG_TPM_V2=y`` +- ``CONFIG_TPM_WOLF=y`` +- ``CONFIG_CMD_WOLFTPM=y`` +- ``CONFIG_TPM2_SPI_SANDBOX=y`` + +Running SPI Tests +~~~~~~~~~~~~~~~~~ + +**Important:** The sandbox TPM SPI device requires the ``-D`` flag to load the +full device tree. Without this flag, the SPI bus will not be available. + +Run wolfTPM commands via SPI:: + + # Build sandbox + make sandbox_defconfig && make -j$(nproc) + + # Run with full device tree + ./u-boot -D + + # At U-Boot prompt, test wolfTPM SPI path + => wolftpm autostart + => wolftpm caps + => wolftpm info + => wolftpm pcr_read 0 0x1000000 SHA256 + => wolftpm pcr_print + +Non-interactive testing:: + + # Single command + ./u-boot -D -c "wolftpm autostart" + + # Multiple commands + ./u-boot -D -c "wolftpm autostart; wolftpm caps; wolftpm info" + +Expected output:: + + TPM SPI initialized: bus 0, cs 1 + TPM2: Caps 0x30000697, Did 0x001d, Vid 0x15d1, Rid 0x36 + TPM2_Startup pass + wolfTPM2_Reset complete + TPM2_SelfTest pass + +Enabling Debug Output +~~~~~~~~~~~~~~~~~~~~~ + +To see SPI-level debug messages, enable logging before running:: + + # At U-Boot prompt + => log level 7 + +Or enable in defconfig:: + + CONFIG_LOG=y + CONFIG_LOG_MAX_LEVEL=7 + CONFIG_LOG_DEFAULT_LEVEL=7 + +For wolfTPM library-level SPI debug, edit ``include/configs/user_settings.h``:: + + #define DEBUG_WOLFTPM + #define WOLFTPM_DEBUG_IO /* Shows SPI transfer details */ + +Device Tree Configuration +~~~~~~~~~~~~~~~~~~~~~~~~~ + +The sandbox SPI TPM is defined in ``arch/sandbox/dts/sandbox.dtsi``:: + + &spi0 { + tpm_spi: tpm@1 { + reg = <1>; + compatible = "sandbox,tpm-spi"; + spi-max-frequency = <10000000>; + sandbox,emul = <&tpm_spi_emul>; + }; + }; + + tpm_spi_emul: tpm-spi-emul { + compatible = "sandbox,tpm-spi-emul"; + }; + +The emulator driver is in ``drivers/tpm/tpm_spi_sandbox.c``. diff --git a/drivers/mtd/spi/sandbox.c b/drivers/mtd/spi/sandbox.c index e5ebc3479fb..41bd07817aa 100644 --- a/drivers/mtd/spi/sandbox.c +++ b/drivers/mtd/spi/sandbox.c @@ -571,16 +571,28 @@ int sandbox_spi_get_emul(struct sandbox_state *state, info = &state->spi[busnum][cs]; if (!info->emul) { - /* Use the same device tree node as the SPI flash device */ - debug("%s: busnum=%u, cs=%u: binding SPI flash emulation: ", - __func__, busnum, cs); - ret = sandbox_sf_bind_emul(state, busnum, cs, bus, - dev_ofnode(slave), slave->name); - if (ret) { - debug("failed (err=%d)\n", ret); - return ret; + struct udevice *emul; + ofnode node = dev_ofnode(slave); + + /* First check for sandbox,emul phandle property */ + ret = uclass_get_device_by_phandle(UCLASS_SPI_EMUL, slave, + "sandbox,emul", &emul); + if (!ret) { + debug("%s: busnum=%u, cs=%u: using phandle emulator\n", + __func__, busnum, cs); + info->emul = emul; + } else { + /* Fall back to SPI flash emulation binding */ + debug("%s: busnum=%u, cs=%u: binding SPI flash emulation: ", + __func__, busnum, cs); + ret = sandbox_sf_bind_emul(state, busnum, cs, bus, + node, slave->name); + if (ret) { + debug("failed (err=%d)\n", ret); + return ret; + } + debug("OK\n"); } - debug("OK\n"); } *emulp = info->emul; diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig index 8c6c095a8cf..9625b1e073e 100644 --- a/drivers/spi/Kconfig +++ b/drivers/spi/Kconfig @@ -117,6 +117,15 @@ config ATMEL_SPI many AT91 (ARM) chips. This driver can be used to access the SPI Flash, such as AT25DF321. +config BCM2835_SPI + bool "BCM2835/BCM2711 SPI driver" + depends on ARCH_BCM283X + help + Enable the BCM2835/BCM2711 SPI controller driver. This driver + can be used to access SPI devices on Raspberry Pi boards + including Pi 3 and Pi 4. It uses the hardware SPI controller + rather than GPIO bit-banging. + config BCM63XX_HSSPI bool "BCM63XX HSSPI driver" depends on (ARCH_BMIPS || ARCH_BCMBCA) diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile index 0dc2d23e172..47a1c6194b1 100644 --- a/drivers/spi/Makefile +++ b/drivers/spi/Makefile @@ -27,6 +27,7 @@ obj-$(CONFIG_APPLE_SPI) += apple_spi.o obj-$(CONFIG_ATH79_SPI) += ath79_spi.o obj-$(CONFIG_ATMEL_QSPI) += atmel-quadspi.o obj-$(CONFIG_ATMEL_SPI) += atmel_spi.o +obj-$(CONFIG_BCM2835_SPI) += bcm2835_spi.o obj-$(CONFIG_BCM63XX_HSSPI) += bcm63xx_hsspi.o obj-$(CONFIG_BCMBCA_HSSPI) += bcmbca_hsspi.o obj-$(CONFIG_BCM63XX_SPI) += bcm63xx_spi.o diff --git a/drivers/spi/bcm2835_spi.c b/drivers/spi/bcm2835_spi.c new file mode 100644 index 00000000000..c44c2a6ed3e --- /dev/null +++ b/drivers/spi/bcm2835_spi.c @@ -0,0 +1,429 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * BCM2835/BCM2711 SPI controller driver for U-Boot + * + * Copyright (C) 2025 wolfSSL Inc. + * Author: Aidan Garske + * + * Based on Linux driver by Chris Boot, Martin Sperl, et al. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* SPI register offsets */ +#define BCM2835_SPI_CS 0x00 /* Control and Status */ +#define BCM2835_SPI_FIFO 0x04 /* TX and RX FIFOs */ +#define BCM2835_SPI_CLK 0x08 /* Clock Divider */ +#define BCM2835_SPI_DLEN 0x0c /* Data Length */ +#define BCM2835_SPI_LTOH 0x10 /* LoSSI mode TOH */ +#define BCM2835_SPI_DC 0x14 /* DMA DREQ Controls */ + +/* CS register bits */ +#define BCM2835_SPI_CS_LEN_LONG BIT(25) +#define BCM2835_SPI_CS_DMA_LEN BIT(24) +#define BCM2835_SPI_CS_CSPOL2 BIT(23) +#define BCM2835_SPI_CS_CSPOL1 BIT(22) +#define BCM2835_SPI_CS_CSPOL0 BIT(21) +#define BCM2835_SPI_CS_RXF BIT(20) +#define BCM2835_SPI_CS_RXR BIT(19) +#define BCM2835_SPI_CS_TXD BIT(18) +#define BCM2835_SPI_CS_RXD BIT(17) +#define BCM2835_SPI_CS_DONE BIT(16) +#define BCM2835_SPI_CS_LEN BIT(13) +#define BCM2835_SPI_CS_REN BIT(12) +#define BCM2835_SPI_CS_ADCS BIT(11) +#define BCM2835_SPI_CS_INTR BIT(10) +#define BCM2835_SPI_CS_INTD BIT(9) +#define BCM2835_SPI_CS_DMAEN BIT(8) +#define BCM2835_SPI_CS_TA BIT(7) +#define BCM2835_SPI_CS_CSPOL BIT(6) +#define BCM2835_SPI_CS_CLEAR_RX BIT(5) +#define BCM2835_SPI_CS_CLEAR_TX BIT(4) +#define BCM2835_SPI_CS_CPOL BIT(3) +#define BCM2835_SPI_CS_CPHA BIT(2) +#define BCM2835_SPI_CS_CS_10 BIT(1) +#define BCM2835_SPI_CS_CS_01 BIT(0) + +/* Default clock rate - 250 MHz for Pi 4 */ +#define BCM2835_SPI_DEFAULT_CLK 250000000 + +struct bcm2835_spi_priv { + void __iomem *regs; + u32 clk_hz; + u32 cs_reg; /* Cached CS register value */ + u32 speed_hz; + u8 mode; + struct gpio_desc cs_gpio; + int cs_gpio_valid; + int cs_asserted; /* Track if CS should stay asserted between transfers */ +}; + +struct bcm2835_spi_plat { + fdt_addr_t base; + u32 clk_hz; +}; + +static inline u32 bcm2835_spi_readl(struct bcm2835_spi_priv *priv, u32 reg) +{ + return readl(priv->regs + reg); +} + +static inline void bcm2835_spi_writel(struct bcm2835_spi_priv *priv, + u32 reg, u32 val) +{ + writel(val, priv->regs + reg); +} + +static void bcm2835_spi_reset(struct bcm2835_spi_priv *priv) +{ + /* Clear FIFOs and disable SPI */ + bcm2835_spi_writel(priv, BCM2835_SPI_CS, + BCM2835_SPI_CS_CLEAR_RX | BCM2835_SPI_CS_CLEAR_TX); +} + +/* GPIO base for software CS control */ +static void __iomem *g_gpio_base = (void __iomem *)0xFE200000; + +/* Software CS control - assert (LOW = active) */ +static void bcm2835_spi_cs_assert(int cs_pin) +{ + /* GPCLR0 - clear pin (drive LOW) */ + writel(1 << cs_pin, g_gpio_base + 0x28); +} + +/* Software CS control - deassert (HIGH = inactive) */ +static void bcm2835_spi_cs_deassert(int cs_pin) +{ + /* GPSET0 - set pin (drive HIGH) */ + writel(1 << cs_pin, g_gpio_base + 0x1C); +} + +static int bcm2835_spi_xfer(struct udevice *dev, unsigned int bitlen, + const void *dout, void *din, unsigned long flags) +{ + struct udevice *bus = dev_get_parent(dev); + struct bcm2835_spi_priv *priv = dev_get_priv(bus); + const u8 *tx = dout; + u8 *rx = din; + u32 len = bitlen / 8; + u32 cs_reg; + u32 tx_count = 0, rx_count = 0; + int timeout; + int cs = spi_chip_select(dev); /* Get chip select from slave device */ + int cs_pin = (cs == 0) ? 8 : 7; /* CS0=GPIO8, CS1=GPIO7 */ + u32 stat; + + if (bitlen == 0) { + /* Handle CS-only operations (deassert) */ + if (flags & SPI_XFER_END) { + bcm2835_spi_cs_deassert(cs_pin); + priv->cs_asserted = 0; + } + return 0; + } + + if (bitlen % 8) { + dev_err(dev, "Non-byte-aligned transfer not supported\n"); + return -EINVAL; + } + + /* + * SOFTWARE GPIO CHIP SELECT - like Linux driver + * Don't use hardware CS bits - set to 0 (unused) + */ + cs_reg = priv->cs_reg & ~(BCM2835_SPI_CS_CS_10 | BCM2835_SPI_CS_CS_01); + + /* Assert CS at start of transaction (SPI_XFER_BEGIN) */ + if (flags & SPI_XFER_BEGIN) { + bcm2835_spi_cs_assert(cs_pin); + priv->cs_asserted = 1; + udelay(1); /* CS setup time */ + } + + /* Clear FIFOs for new transaction */ + if (flags & SPI_XFER_BEGIN) { + bcm2835_spi_writel(priv, BCM2835_SPI_CS, + cs_reg | BCM2835_SPI_CS_CLEAR_RX | + BCM2835_SPI_CS_CLEAR_TX); + udelay(1); + } + + /* Start transfer with TA=1 (but CS is controlled by GPIO, not hardware) */ + bcm2835_spi_writel(priv, BCM2835_SPI_CS, cs_reg | BCM2835_SPI_CS_TA); + + /* Poll for completion - transfer byte by byte */ + timeout = 100000; + while ((tx_count < len || rx_count < len) && timeout > 0) { + stat = bcm2835_spi_readl(priv, BCM2835_SPI_CS); + + /* TX FIFO not full - send next byte */ + while ((stat & BCM2835_SPI_CS_TXD) && tx_count < len) { + u8 byte = tx ? tx[tx_count] : 0; + bcm2835_spi_writel(priv, BCM2835_SPI_FIFO, byte); + tx_count++; + stat = bcm2835_spi_readl(priv, BCM2835_SPI_CS); + } + + /* RX FIFO has data - read it */ + while ((stat & BCM2835_SPI_CS_RXD) && rx_count < len) { + u8 byte = bcm2835_spi_readl(priv, BCM2835_SPI_FIFO) & 0xff; + if (rx) + rx[rx_count] = byte; + rx_count++; + stat = bcm2835_spi_readl(priv, BCM2835_SPI_CS); + } + + timeout--; + } + + /* Wait for DONE */ + timeout = 10000; + while (!(bcm2835_spi_readl(priv, BCM2835_SPI_CS) & BCM2835_SPI_CS_DONE) && + timeout > 0) { + udelay(1); + timeout--; + } + + /* Read any remaining RX data from FIFO */ + while (bcm2835_spi_readl(priv, BCM2835_SPI_CS) & BCM2835_SPI_CS_RXD) { + u8 byte = bcm2835_spi_readl(priv, BCM2835_SPI_FIFO) & 0xff; + if (rx && rx_count < len) + rx[rx_count++] = byte; + } + + /* Clear TA to complete this transfer (doesn't affect GPIO CS) */ + bcm2835_spi_writel(priv, BCM2835_SPI_CS, cs_reg); + + /* + * SOFTWARE GPIO CHIP SELECT control: + * - SPI_XFER_END: deassert CS (GPIO HIGH) + * - No END flag: keep CS asserted for next transfer + */ + if (flags & SPI_XFER_END) { + bcm2835_spi_cs_deassert(cs_pin); + priv->cs_asserted = 0; + } else { + /* Keep CS asserted for next transfer (e.g., wait state polling) */ + priv->cs_asserted = 1; + } + + if (timeout == 0) { + bcm2835_spi_cs_deassert(cs_pin); /* Make sure CS is released */ + return -ETIMEDOUT; + } + + return 0; +} + +static int bcm2835_spi_set_speed(struct udevice *bus, uint speed) +{ + struct bcm2835_spi_priv *priv = dev_get_priv(bus); + u32 cdiv; + + if (speed == 0) + speed = 1000000; /* Default 1 MHz */ + + priv->speed_hz = speed; + + /* Calculate clock divider */ + if (speed >= priv->clk_hz / 2) { + cdiv = 2; /* Fastest possible */ + } else { + cdiv = (priv->clk_hz + speed - 1) / speed; + cdiv += (cdiv & 1); /* Must be even */ + if (cdiv >= 65536) + cdiv = 0; /* Slowest: clk/65536 */ + } + + bcm2835_spi_writel(priv, BCM2835_SPI_CLK, cdiv); + + return 0; +} + +static int bcm2835_spi_set_mode(struct udevice *bus, uint mode) +{ + struct bcm2835_spi_priv *priv = dev_get_priv(bus); + u32 cs_reg = 0; + + priv->mode = mode; + + /* Set clock polarity and phase */ + if (mode & SPI_CPOL) + cs_reg |= BCM2835_SPI_CS_CPOL; + if (mode & SPI_CPHA) + cs_reg |= BCM2835_SPI_CS_CPHA; + + /* CS bits will be set in xfer based on slave's chip select */ + priv->cs_reg = cs_reg; + + return 0; +} + +static int bcm2835_spi_claim_bus(struct udevice *dev) +{ + return 0; +} + +static int bcm2835_spi_release_bus(struct udevice *dev) +{ + return 0; +} + +/* Setup GPIO pins for SPI0 with SOFTWARE chip select */ +static void bcm2835_spi_setup_gpio(void) +{ + u32 val; + + /* + * SPI0 pin configuration: + * GPIO7 (CE1) - OUTPUT (software CS) - GPFSEL0 bits 23:21 = 001 + * GPIO8 (CE0) - OUTPUT (software CS) - GPFSEL0 bits 26:24 = 001 + * GPIO9 (MISO) - ALT0 (SPI) - GPFSEL0 bits 29:27 = 100 + * GPIO10 (MOSI) - ALT0 (SPI) - GPFSEL1 bits 2:0 = 100 + * GPIO11 (SCLK) - ALT0 (SPI) - GPFSEL1 bits 5:3 = 100 + */ + + /* Set GPIO7, GPIO8 to OUTPUT, GPIO9 to ALT0 in GPFSEL0 */ + val = readl(g_gpio_base + 0x00); + val &= ~((7 << 21) | (7 << 24) | (7 << 27)); /* Clear GPIO7,8,9 */ + val |= (1 << 21); /* GPIO7 = OUTPUT (001) */ + val |= (1 << 24); /* GPIO8 = OUTPUT (001) */ + val |= (4 << 27); /* GPIO9 = ALT0 (100) for MISO */ + writel(val, g_gpio_base + 0x00); + + /* Set GPIO10, GPIO11 to ALT0 in GPFSEL1 */ + val = readl(g_gpio_base + 0x04); + val &= ~((7 << 0) | (7 << 3)); /* Clear GPIO10,11 */ + val |= (4 << 0); /* GPIO10 = ALT0 (100) for MOSI */ + val |= (4 << 3); /* GPIO11 = ALT0 (100) for SCLK */ + writel(val, g_gpio_base + 0x04); + + /* Deassert both CS lines (HIGH = inactive) */ + bcm2835_spi_cs_deassert(7); /* CE1 */ + bcm2835_spi_cs_deassert(8); /* CE0 */ +} + +/* TPM Reset via GPIO4 and GPIO24 */ +static void bcm2835_spi_tpm_reset(void) +{ + void __iomem *gpio_base = (void __iomem *)0xFE200000; + u32 val; + + /* Set GPIO4 as output (GPFSEL0, bits 14:12) */ + val = readl(gpio_base + 0x00); /* GPFSEL0 */ + val &= ~(7 << 12); /* Clear bits 14:12 for GPIO4 */ + val |= (1 << 12); /* Set to output */ + writel(val, gpio_base + 0x00); + + /* Set GPIO24 as output (GPFSEL2, bits 14:12) */ + val = readl(gpio_base + 0x08); /* GPFSEL2 */ + val &= ~(7 << 12); /* Clear bits 14:12 for GPIO24 */ + val |= (1 << 12); /* Set to output */ + writel(val, gpio_base + 0x08); + + /* Assert reset on BOTH pins (LOW) */ + writel((1 << 4) | (1 << 24), gpio_base + 0x28); /* GPCLR0 */ + mdelay(100); + + /* Release reset on BOTH pins (HIGH) */ + writel((1 << 4) | (1 << 24), gpio_base + 0x1C); /* GPSET0 */ + mdelay(150); /* Wait for TPM to initialize */ +} + +static int bcm2835_spi_probe(struct udevice *bus) +{ + struct bcm2835_spi_plat *plat = dev_get_plat(bus); + struct bcm2835_spi_priv *priv = dev_get_priv(bus); + int ret; + + priv->regs = (void __iomem *)plat->base; + priv->clk_hz = plat->clk_hz ? plat->clk_hz : BCM2835_SPI_DEFAULT_CLK; + + /* Setup GPIO pins for SPI0 (ALT0 function) */ + bcm2835_spi_setup_gpio(); + + /* Reset TPM before using SPI */ + bcm2835_spi_tpm_reset(); + + /* Try to get CS GPIO from device tree */ + ret = gpio_request_by_name(bus, "cs-gpios", 0, &priv->cs_gpio, + GPIOD_IS_OUT | GPIOD_ACTIVE_LOW); + if (ret == 0) { + priv->cs_gpio_valid = 1; + /* Deassert CS initially */ + dm_gpio_set_value(&priv->cs_gpio, 1); + } else { + priv->cs_gpio_valid = 0; + } + + /* Reset the SPI controller */ + bcm2835_spi_reset(priv); + + /* Set default speed and mode */ + bcm2835_spi_set_speed(bus, 1000000); /* 1 MHz default */ + bcm2835_spi_set_mode(bus, SPI_MODE_0); + + return 0; +} + +static int bcm2835_spi_of_to_plat(struct udevice *bus) +{ + struct bcm2835_spi_plat *plat = dev_get_plat(bus); + fdt_addr_t addr; + + addr = dev_read_addr(bus); + if (addr == FDT_ADDR_T_NONE) { + dev_err(bus, "Failed to get SPI base address\n"); + return -EINVAL; + } + + /* + * On BCM2711 (Pi 4), the device tree often uses VideoCore bus addresses + * which start with 0x7E. The ARM needs to access these via the ARM + * peripheral base at 0xFE000000. + */ + if ((addr & 0xFF000000) == 0x7E000000) { + addr = (addr & 0x00FFFFFF) | 0xFE000000; + } + + plat->base = addr; + + /* Try to get clock rate from device tree */ + plat->clk_hz = dev_read_u32_default(bus, "clock-frequency", + BCM2835_SPI_DEFAULT_CLK); + + return 0; +} + +static const struct dm_spi_ops bcm2835_spi_ops = { + .claim_bus = bcm2835_spi_claim_bus, + .release_bus = bcm2835_spi_release_bus, + .xfer = bcm2835_spi_xfer, + .set_speed = bcm2835_spi_set_speed, + .set_mode = bcm2835_spi_set_mode, +}; + +static const struct udevice_id bcm2835_spi_ids[] = { + { .compatible = "brcm,bcm2835-spi" }, + { .compatible = "brcm,bcm2711-spi" }, + { } +}; + +U_BOOT_DRIVER(bcm2835_spi) = { + .name = "bcm2835_spi", + .id = UCLASS_SPI, + .of_match = bcm2835_spi_ids, + .ops = &bcm2835_spi_ops, + .of_to_plat = bcm2835_spi_of_to_plat, + .plat_auto = sizeof(struct bcm2835_spi_plat), + .priv_auto = sizeof(struct bcm2835_spi_priv), + .probe = bcm2835_spi_probe, +}; diff --git a/drivers/tpm/Kconfig b/drivers/tpm/Kconfig index 219ea606b50..d4694946b94 100644 --- a/drivers/tpm/Kconfig +++ b/drivers/tpm/Kconfig @@ -158,6 +158,14 @@ config TPM2_TIS_SANDBOX such as basic configuration, PCR extension and PCR read. Extended functionalities are not implemented. +config TPM2_SPI_SANDBOX + bool "Enable sandbox TPM SPI emulator" + depends on TPM_V2 && SANDBOX && DM_SPI + help + This driver emulates a TPM connected via SPI for sandbox testing. + It implements the TPM TIS SPI protocol and can be used to test + wolfTPM SPI HAL code without physical hardware. + config TPM2_TIS_SPI bool "Enable support for TPMv2.x SPI chips" depends on TPM_V2 && DM_SPI @@ -200,6 +208,34 @@ config TPM2_EVENT_LOG_SIZE allocated twice. One for the eventlog it self and one for the configuration table that is required from the TCG2 spec +config WOLFTPM_LINUX_DEV + bool "Use device-level TPM interface (bypass wolfTPM TIS layer)" + depends on TPM_V2 && TPM_WOLF + default y + help + Enable wolfTPM to use the underlying TPM driver instead of its own + TIS (TPM Interface Specification) layer. On U-Boot, this uses the + U-Boot TPM driver model (tpm_xfer). On Linux, this uses /dev/tpm0. + This is the recommended setting for U-Boot. + +config WOLFTPM_SLB9672 + bool "Enable support for Infineon SLB9672 TPM" + depends on TPM_V2 && TPM_WOLF + help + Enable support for Infineon SLB9672 TPM features in wolfTPM. + +config WOLFTPM_SLB9673 + bool "Enable support for Infineon SLB9673 TPM" + depends on TPM_V2 && TPM_WOLF + help + Enable support for Infineon SLB9673 TPM features in wolfTPM. + +config WOLFTPM_FIRMWARE_UPGRADE + bool "Enable firmware upgrade support for wolfTPM" + depends on TPM_V2 && TPM_WOLF + help + Enable support for Infineon TPM firmware upgrade commands in wolfTPM. + endif # TPM_V2 endmenu diff --git a/drivers/tpm/Makefile b/drivers/tpm/Makefile index b83ce703ec0..39de454cacf 100644 --- a/drivers/tpm/Makefile +++ b/drivers/tpm/Makefile @@ -10,7 +10,16 @@ obj-$(CONFIG_TPM_TIS_SANDBOX) += tpm_tis_sandbox.o sandbox_common.o obj-$(CONFIG_$(PHASE_)TPM2_CR50_I2C) += cr50_i2c.o obj-$(CONFIG_TPM2_TIS_SANDBOX) += tpm2_tis_sandbox.o sandbox_common.o +obj-$(CONFIG_TPM2_SPI_SANDBOX) += tpm_spi_sandbox.o obj-$(CONFIG_TPM2_TIS_SPI) += tpm2_tis_core.o tpm2_tis_spi.o obj-$(CONFIG_TPM2_TIS_I2C) += tpm2_tis_core.o tpm2_tis_i2c.o obj-$(CONFIG_TPM2_FTPM_TEE) += tpm2_ftpm_tee.o obj-$(CONFIG_TPM2_MMIO) += tpm2_tis_core.o tpm2_tis_mmio.o + +# wolfTPM helper functions +ifeq ($(CONFIG_TPM_WOLF),y) +ccflags-y += -Ilib/wolftpm \ + -Iinclude/configs \ + -DWOLFTPM_USER_SETTINGS +obj-y += wolftpm_common.o +endif diff --git a/drivers/tpm/tpm_spi_sandbox.c b/drivers/tpm/tpm_spi_sandbox.c new file mode 100644 index 00000000000..694c5d721f0 --- /dev/null +++ b/drivers/tpm/tpm_spi_sandbox.c @@ -0,0 +1,410 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Sandbox TPM SPI Emulator + * + * Copyright (c) 2025 wolfSSL Inc. + * Author: Aidan Garske + * + * Emulates TPM TIS SPI protocol for testing wolfTPM SPI HAL + * without hardware. Wraps the existing sandbox TPM2 state machine. + */ + +#include +#include +#include +#include +#include +#include +#include + +/* TIS register addresses (locality 0) */ +#define TPM_ACCESS_REG 0x0000 +#define TPM_INT_ENABLE_REG 0x0008 +#define TPM_INTF_CAPS_REG 0x0014 +#define TPM_STS_REG 0x0018 +#define TPM_DATA_FIFO_REG 0x0024 +#define TPM_DID_VID_REG 0x0F00 +#define TPM_RID_REG 0x0F04 + +/* TIS access register bits */ +#define TPM_ACCESS_VALID 0x80 +#define TPM_ACCESS_ACTIVE_LOCALITY 0x20 +#define TPM_ACCESS_REQUEST_PENDING 0x04 +#define TPM_ACCESS_REQUEST_USE 0x02 + +/* TIS status register bits */ +#define TPM_STS_VALID 0x80 +#define TPM_STS_COMMAND_READY 0x40 +#define TPM_STS_GO 0x20 +#define TPM_STS_DATA_AVAIL 0x10 +#define TPM_STS_DATA_EXPECT 0x08 + +/* Interface capabilities */ +#define TPM_INTF_CAPS_VALUE 0x30000697 /* Typical Infineon value */ + +/* Device/Vendor ID - Infineon SLB9670 */ +#define TPM_DID_VID_VALUE 0x001D15D1 + +/* Revision ID */ +#define TPM_RID_VALUE 0x36 + +/* Maximum buffer sizes */ +#define TPM_CMD_BUF_SIZE 4096 +#define TPM_RSP_BUF_SIZE 4096 +#define MAX_SPI_FRAMESIZE 64 + +/* TPM TIS SPI protocol states */ +enum tpm_spi_state { + TPM_SPI_IDLE, + TPM_SPI_HEADER, /* Receiving 4-byte header */ + TPM_SPI_WAIT_STATE, /* Sending wait state bytes */ + TPM_SPI_DATA, /* Transfer data */ +}; + +/* TIS state machine */ +enum tpm_tis_state { + TIS_IDLE, + TIS_READY, /* Ready to receive command */ + TIS_RECEPTION, /* Receiving command data */ + TIS_EXECUTION, /* Executing command */ + TIS_COMPLETION, /* Response available */ +}; + +struct sandbox_tpm_spi { + /* SPI protocol state */ + enum tpm_spi_state spi_state; + u8 header[4]; + int header_pos; + bool is_read; + u32 addr; + int xfer_len; + int data_pos; + + /* TIS state */ + enum tpm_tis_state tis_state; + u8 access_reg; + u32 sts_reg; + u32 intf_caps; + + /* Command/response buffers */ + u8 cmd_buf[TPM_CMD_BUF_SIZE]; + int cmd_len; + int cmd_pos; + u8 rsp_buf[TPM_RSP_BUF_SIZE]; + int rsp_len; + int rsp_pos; + + /* Burst count for status register */ + u16 burst_count; +}; + +/* + * Parse TIS SPI header + * Format: [R/W|len-1][0xD4][addr_hi][addr_lo] + * Bit 7 of byte 0: 1=read, 0=write + * Bits 5:0 of byte 0: transfer length - 1 + */ +static void parse_spi_header(struct sandbox_tpm_spi *priv) +{ + priv->is_read = (priv->header[0] & 0x80) != 0; + priv->xfer_len = (priv->header[0] & 0x3F) + 1; + priv->addr = (priv->header[2] << 8) | priv->header[3]; + priv->data_pos = 0; +} + +/* + * Read from TIS register + */ +static u8 tis_reg_read(struct sandbox_tpm_spi *priv, u32 addr) +{ + u32 reg = addr & 0x0FFF; /* Mask off locality bits */ + + switch (reg) { + case TPM_ACCESS_REG: + return priv->access_reg; + + case TPM_STS_REG: + case TPM_STS_REG + 1: + case TPM_STS_REG + 2: + case TPM_STS_REG + 3: { + int byte_off = reg - TPM_STS_REG; + u32 sts = priv->sts_reg; + + /* Update burst count in status */ + sts |= ((u32)priv->burst_count << 8); + return (sts >> (byte_off * 8)) & 0xFF; + } + + case TPM_INTF_CAPS_REG: + case TPM_INTF_CAPS_REG + 1: + case TPM_INTF_CAPS_REG + 2: + case TPM_INTF_CAPS_REG + 3: { + int byte_off = reg - TPM_INTF_CAPS_REG; + + return (priv->intf_caps >> (byte_off * 8)) & 0xFF; + } + + case TPM_DID_VID_REG: + case TPM_DID_VID_REG + 1: + case TPM_DID_VID_REG + 2: + case TPM_DID_VID_REG + 3: { + int byte_off = reg - TPM_DID_VID_REG; + + return (TPM_DID_VID_VALUE >> (byte_off * 8)) & 0xFF; + } + + case TPM_RID_REG: + return TPM_RID_VALUE; + + default: + /* + * Handle FIFO reads - the FIFO can be accessed at any address + * from 0x0024 up to 0x0F00 for multi-byte transfers. + */ + if (reg >= TPM_DATA_FIFO_REG && reg < TPM_DID_VID_REG) { + if (priv->tis_state == TIS_COMPLETION && + priv->rsp_pos < priv->rsp_len) { + u8 data = priv->rsp_buf[priv->rsp_pos++]; + + /* Update status when all data read */ + if (priv->rsp_pos >= priv->rsp_len) { + priv->sts_reg &= ~TPM_STS_DATA_AVAIL; + priv->sts_reg |= TPM_STS_COMMAND_READY; + priv->tis_state = TIS_READY; + } + return data; + } + return 0xFF; + } + return 0xFF; + } +} + +/* + * Write to TIS register + */ +static void tis_reg_write(struct sandbox_tpm_spi *priv, u32 addr, u8 value) +{ + u32 reg = addr & 0x0FFF; + + switch (reg) { + case TPM_ACCESS_REG: + if (value & TPM_ACCESS_REQUEST_USE) { + /* Request locality */ + priv->access_reg |= TPM_ACCESS_ACTIVE_LOCALITY; + priv->access_reg |= TPM_ACCESS_VALID; + } + break; + + case TPM_STS_REG: + if (value & TPM_STS_COMMAND_READY) { + /* Abort current command and go to ready state */ + priv->tis_state = TIS_READY; + priv->cmd_len = 0; + priv->cmd_pos = 0; + priv->rsp_len = 0; + priv->rsp_pos = 0; + priv->sts_reg = TPM_STS_VALID | TPM_STS_COMMAND_READY; + priv->burst_count = MAX_SPI_FRAMESIZE; + } + if (value & TPM_STS_GO) { + /* Execute command */ + if (priv->tis_state == TIS_RECEPTION && + priv->cmd_len > 0) { + /* + * Generate a simple success response. + * A full implementation would call the + * sandbox TPM2 state machine here. + */ + priv->rsp_buf[0] = 0x80; /* TPM_ST_NO_SESSIONS */ + priv->rsp_buf[1] = 0x01; + priv->rsp_buf[2] = 0x00; /* Response size: 10 */ + priv->rsp_buf[3] = 0x00; + priv->rsp_buf[4] = 0x00; + priv->rsp_buf[5] = 0x0A; + priv->rsp_buf[6] = 0x00; /* TPM_RC_SUCCESS */ + priv->rsp_buf[7] = 0x00; + priv->rsp_buf[8] = 0x00; + priv->rsp_buf[9] = 0x00; + priv->rsp_len = 10; + priv->rsp_pos = 0; + + priv->tis_state = TIS_COMPLETION; + priv->sts_reg = TPM_STS_VALID | + TPM_STS_DATA_AVAIL; + } + } + break; + + default: + /* + * Handle FIFO writes - the FIFO is at 0x0024 but any address + * from 0x0024 up to 0x0F00 can be used for FIFO access when + * doing multi-byte transfers (address auto-increments). + */ + if (reg >= TPM_DATA_FIFO_REG && reg < TPM_DID_VID_REG) { + if (priv->tis_state == TIS_READY) { + /* Start receiving command */ + priv->tis_state = TIS_RECEPTION; + priv->cmd_len = 0; + priv->cmd_pos = 0; + priv->sts_reg = TPM_STS_VALID | TPM_STS_DATA_EXPECT; + } + if (priv->tis_state == TIS_RECEPTION) { + if (priv->cmd_len < TPM_CMD_BUF_SIZE) { + priv->cmd_buf[priv->cmd_len++] = value; + + /* Check if we have complete command */ + if (priv->cmd_len >= 6) { + u32 expected_len; + + expected_len = (priv->cmd_buf[2] << 24) | + (priv->cmd_buf[3] << 16) | + (priv->cmd_buf[4] << 8) | + priv->cmd_buf[5]; + if (priv->cmd_len >= expected_len) { + /* Command complete */ + priv->sts_reg &= + ~TPM_STS_DATA_EXPECT; + } + } + } + } + } + break; + } +} + +/* + * SPI emulation transfer callback + */ +static int sandbox_tpm_spi_xfer(struct udevice *dev, unsigned int bitlen, + const void *dout, void *din, unsigned long flags) +{ + struct sandbox_tpm_spi *priv = dev_get_priv(dev); + int bytes = bitlen / 8; + const u8 *tx = dout; + u8 *rx = din; + int i; + + /* Handle CS assert - reset state machine */ + if (flags & SPI_XFER_BEGIN) { + priv->spi_state = TPM_SPI_HEADER; + priv->header_pos = 0; + } + + for (i = 0; i < bytes; i++) { + u8 tx_byte = tx ? tx[i] : 0; + u8 rx_byte = 0; + + switch (priv->spi_state) { + case TPM_SPI_IDLE: + /* Should not happen during active transfer */ + rx_byte = 0xFF; + break; + + case TPM_SPI_HEADER: + /* Receive 4-byte header */ + priv->header[priv->header_pos++] = tx_byte; + rx_byte = 0x00; + + if (priv->header_pos >= 4) { + parse_spi_header(priv); + log_debug("TPM SPI: %s len=%d addr=0x%04x\n", + priv->is_read ? "read" : "write", + priv->xfer_len, priv->addr); + /* Return wait state in last header byte */ + rx_byte = 0x01; /* Ready immediately */ + priv->spi_state = TPM_SPI_DATA; + } + break; + + case TPM_SPI_DATA: + if (priv->is_read) { + /* Read from TPM register */ + rx_byte = tis_reg_read(priv, + priv->addr + priv->data_pos); + } else { + /* Write to TPM register */ + tis_reg_write(priv, priv->addr + priv->data_pos, + tx_byte); + rx_byte = 0x00; + } + priv->data_pos++; + break; + + default: + rx_byte = 0xFF; + break; + } + + if (rx) + rx[i] = rx_byte; + } + + /* Handle CS deassert - return to idle */ + if (flags & SPI_XFER_END) + priv->spi_state = TPM_SPI_IDLE; + + return 0; +} + +static int sandbox_tpm_spi_probe(struct udevice *dev) +{ + struct sandbox_tpm_spi *priv = dev_get_priv(dev); + + /* Initialize TIS state */ + priv->spi_state = TPM_SPI_IDLE; + priv->tis_state = TIS_IDLE; + priv->access_reg = TPM_ACCESS_VALID; + priv->sts_reg = TPM_STS_VALID; + priv->intf_caps = TPM_INTF_CAPS_VALUE; + priv->burst_count = MAX_SPI_FRAMESIZE; + priv->cmd_len = 0; + priv->rsp_len = 0; + + log_debug("TPM SPI sandbox emulator probed\n"); + + return 0; +} + +static const struct dm_spi_emul_ops sandbox_tpm_spi_ops = { + .xfer = sandbox_tpm_spi_xfer, +}; + +static const struct udevice_id sandbox_tpm_spi_ids[] = { + { .compatible = "sandbox,tpm-spi-emul" }, + { } +}; + +U_BOOT_DRIVER(sandbox_tpm_spi_emul) = { + .name = "sandbox_tpm_spi_emul", + .id = UCLASS_SPI_EMUL, + .of_match = sandbox_tpm_spi_ids, + .ops = &sandbox_tpm_spi_ops, + .probe = sandbox_tpm_spi_probe, + .priv_auto = sizeof(struct sandbox_tpm_spi), +}; + +/* + * SPI slave driver for TPM device + * This gets probed when a device with "sandbox,tpm-spi" is found in DTS. + * The actual SPI transfers are handled by the emulator above. + */ +static int sandbox_tpm_spi_slave_probe(struct udevice *dev) +{ + log_debug("TPM SPI slave device probed\n"); + return 0; +} + +static const struct udevice_id sandbox_tpm_spi_slave_ids[] = { + { .compatible = "sandbox,tpm-spi" }, + { } +}; + +U_BOOT_DRIVER(sandbox_tpm_spi) = { + .name = "sandbox_tpm_spi", + .id = UCLASS_SPI_GENERIC, + .of_match = sandbox_tpm_spi_slave_ids, + .probe = sandbox_tpm_spi_slave_probe, +}; diff --git a/drivers/tpm/wolftpm_common.c b/drivers/tpm/wolftpm_common.c new file mode 100644 index 00000000000..6dea24c5c9a --- /dev/null +++ b/drivers/tpm/wolftpm_common.c @@ -0,0 +1,135 @@ +/* wolftpm.c +* +* SPDX-License-Identifier: GPL-2.0+ +* +* (C) Copyright 2025 +* Aidan Garske +*/ + +#define LOG_CATEGORY UCLASS_BOOTSTD + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifndef WOLFTPM2_NO_WRAPPER +#ifdef WOLFTPM_FIRMWARE_UPGRADE + +/******************************************************************************/ +/* --- BEGIN helper functions -- */ +/******************************************************************************/ + +typedef struct { + byte* manifest_buf; + byte* firmware_buf; + size_t manifest_bufSz; + size_t firmware_bufSz; +} fw_info_t; + +int TPM2_IFX_FwData_Cb(uint8_t* data, uint32_t data_req_sz, + uint32_t offset, void* cb_ctx) +{ + fw_info_t* fwinfo = (fw_info_t*)cb_ctx; + if (offset > fwinfo->firmware_bufSz) { + return BUFFER_E; + } + if (offset + data_req_sz > (uint32_t)fwinfo->firmware_bufSz) { + data_req_sz = (uint32_t)fwinfo->firmware_bufSz - offset; + } + if (data_req_sz > 0) { + XMEMCPY(data, &fwinfo->firmware_buf[offset], data_req_sz); + } + return data_req_sz; +} + +const char* TPM2_IFX_GetOpModeStr(int opMode) +{ + const char* opModeStr = "Unknown"; + switch (opMode) { + case 0x00: + opModeStr = "Normal TPM operational mode"; + break; + case 0x01: + opModeStr = "TPM firmware update mode (abandon possible)"; + break; + case 0x02: + opModeStr = "TPM firmware update mode (abandon not possible)"; + break; + case 0x03: + opModeStr = "After successful update, but before finalize"; + break; + case 0x04: + opModeStr = "After finalize or abandon, reboot required"; + break; + default: + break; + } + return opModeStr; +} + +void TPM2_IFX_PrintInfo(WOLFTPM2_CAPS* caps) +{ + printf("Mfg %s (%d), Vendor %s, Fw %u.%u (0x%x)\n", + caps->mfgStr, caps->mfg, caps->vendorStr, caps->fwVerMajor, + caps->fwVerMinor, caps->fwVerVendor); + printf("Operational mode: %s (0x%x)\n", + TPM2_IFX_GetOpModeStr(caps->opMode), caps->opMode); + printf("KeyGroupId 0x%x, FwCounter %d (%d same)\n", + caps->keyGroupId, caps->fwCounter, caps->fwCounterSame); +} +#endif /* WOLFTPM_FIRMWARE_UPGRADE */ + +int TPM2_PCRs_Print(void) +{ + int rc; + int pcrCount, pcrIndex; + GetCapability_In capIn; + GetCapability_Out capOut; + TPML_PCR_SELECTION* pcrSel; + + memset(&capIn, 0, sizeof(capIn)); + capIn.capability = TPM_CAP_PCRS; + capIn.property = 0; + capIn.propertyCount = 1; + rc = TPM2_GetCapability(&capIn, &capOut); + if (rc != TPM_RC_SUCCESS) { + log_debug("TPM2_GetCapability failed rc=%d (%s)\n", rc, TPM2_GetRCString(rc)); + return rc; + } + pcrSel = &capOut.capabilityData.data.assignedPCR; + printf("Assigned PCR's:\n"); + for (pcrCount=0; pcrCount < (int)pcrSel->count; pcrCount++) { + printf("\t%s: ", TPM2_GetAlgName(pcrSel->pcrSelections[pcrCount].hash)); + for (pcrIndex=0; + pcrIndexpcrSelections[pcrCount].sizeofSelect*8; + pcrIndex++) { + if ((pcrSel->pcrSelections[pcrCount].pcrSelect[pcrIndex/8] & + ((1 << (pcrIndex % 8)))) != 0) { + printf(" %d", pcrIndex); + } + } + printf("\n"); + } + return TPM_RC_SUCCESS; +} + +int TPM2_Init_Device(WOLFTPM2_DEV* dev, void* userCtx) +{ + /* Use TPM2_IoCb callback which calls TPM2_IoCb_Uboot_SPI for packet-level access */ + int rc = wolfTPM2_Init(dev, TPM2_IoCb, userCtx); + log_debug("tpm2 init: rc = %d (%s)\n", rc, TPM2_GetRCString(rc)); + return rc; +} + +#endif /* WOLFTPM2_NO_WRAPPER */ + +/******************************************************************************/ +/* --- END helper functions -- */ +/******************************************************************************/ diff --git a/include/configs/user_settings.h b/include/configs/user_settings.h new file mode 100644 index 00000000000..5aaa27a3d3f --- /dev/null +++ b/include/configs/user_settings.h @@ -0,0 +1,118 @@ +/* user_settings.h + * + * SPDX-License-Identifier: GPL-2.0+ + * + * (C) Copyright 2025 + * Aidan Garske + */ + +#ifndef USER_SETTINGS_H +#define USER_SETTINGS_H + +#ifdef __cplusplus +extern "C" { +#endif + +/******************************************************************************/ +/* --- BEGIN wolfTPM U-boot Settings -- */ +/******************************************************************************/ + +/* ========================================================================= + * TPM Chip Configuration + * ========================================================================= + * + * CONFIG_TPM_AUTODETECT: For swtpm/QEMU testing (no specific chip) + * !CONFIG_TPM_AUTODETECT: For real hardware (SLB9672/SLB9673) + */ +#ifdef CONFIG_TPM_AUTODETECT + #define WOLFTPM_AUTODETECT +#else + /* Real hardware - Infineon SLB9672/SLB9673 + * Firmware upgrade only supported by these chips */ + #define WOLFTPM_FIRMWARE_UPGRADE + #define WOLFTPM_SLB9672 + /* #define WOLFTPM_SLB9673 */ +#endif + +/* Include delay.h and types.h for + * U-boot time delay and types */ +#include +#include +#include + +/* wolfCrypt disabled - pcr_setauthpolicy/pcr_setauthvalue not available + * To enable wolfCrypt, you would need to: + * 1. Uncomment the line below to undefine WOLFTPM2_NO_WOLFCRYPT + * 2. Add wolfCrypt source files to the U-Boot build (lib/Makefile) + * 3. Add wolfCrypt settings for embedded/no-OS use + */ +#undef WOLFTPM2_NO_WOLFCRYPT +#define WOLFTPM2_NO_WOLFCRYPT + +/* ========================================================================= + * TPM Communication Mode Selection (Auto-detected based on chip type) + * ========================================================================= + * + * For real SPI hardware (SLB9672/SLB9673): + * - Uses wolfTPM's native TIS layer with raw SPI via tpm_io_uboot.c + * - Requires CONFIG_SPI and CONFIG_DM_SPI enabled in U-Boot + * + * For swtpm/QEMU testing (no specific chip defined): + * - Uses WOLFTPM_LINUX_DEV mode with U-Boot's TPM driver (tpm_xfer()) + * - Works with MMIO-based TPM via tpm2_tis_mmio.c + */ + +#if defined(WOLFTPM_SLB9672) || defined(WOLFTPM_SLB9673) + /* Real SPI hardware - use native wolfTPM TIS with raw SPI */ + /* WOLFTPM_LINUX_DEV is NOT defined */ + #define WOLFTPM_EXAMPLE_HAL + + /* SPI bus and chip select for TPM + * Official Raspberry Pi tpm-slb9670 overlay uses CE1 (GPIO7) + * This matches LetsTrust and most Infineon evaluation boards */ + #ifndef TPM_SPI_BUS + #define TPM_SPI_BUS 0 + #endif + #ifndef TPM_SPI_CS + #define TPM_SPI_CS 1 /* CE1/GPIO7 - official RPi TPM overlay setting */ + #endif +#else + /* swtpm/QEMU - use U-Boot's TPM driver with MMIO communication mode */ + #define WOLFTPM_LINUX_DEV +#endif + +#define XSLEEP_MS(ms) udelay(ms * 1000) + +/* Timeout configuration */ +#ifdef WOLFTPM_FIRMWARE_UPGRADE + /* Firmware update requires much longer timeout for TPM processing */ + #define TPM_TIMEOUT_TRIES 2000000 +#else + /* Normal operations - reduce from default 1,000,000 to prevent long hangs */ + #define TPM_TIMEOUT_TRIES 10000 +#endif + +/* Add small delay between poll attempts to avoid tight spin loop */ +#define XTPM_WAIT() udelay(100) + +/* Do not include API's that use heap(), they are not required */ +#define WOLFTPM2_NO_HEAP + +/* Debugging - disabled for clean output */ +/* #define DEBUG_WOLFTPM */ +/* #define WOLFTPM_DEBUG_VERBOSE */ +/* #define WOLFTPM_DEBUG_IO */ +/* #define WOLFTPM_DEBUG_TIMEOUT */ + +/* SPI Wait state checking - most TPMs use this */ +#define WOLFTPM_CHECK_WAIT_STATE + +/******************************************************************************/ +/* --- END wolfTPM U-boot Settings -- */ +/******************************************************************************/ + +#ifdef __cpluspluss +} +#endif + +#endif /* USER_SETTINGS_H */ diff --git a/include/hash.h b/include/hash.h index 8b3f79ec473..242de9f0e84 100644 --- a/include/hash.h +++ b/include/hash.h @@ -6,6 +6,8 @@ #ifndef _HASH_H #define _HASH_H +#include + #ifdef USE_HOSTCC #include #endif @@ -163,4 +165,19 @@ int hash_progressive_lookup_algo(const char *algo_name, */ int hash_parse_string(const char *algo_name, const char *str, uint8_t *result); +#ifdef WOLFTPM2_NO_WOLFCRYPT +/** + * wc_Sha384Hash() - Calculate SHA384 hash + * @data: Data to hash + * @len: Length of data + * @hash: Output buffer for hash + * + * This is a wrapper function to provide wolfCrypt-compatible SHA384 hashing + * when wolfCrypt is not available. + * + * Return: 0 on success, -1 on error + */ +int wc_Sha384Hash(const unsigned char* data, unsigned int len, unsigned char* hash); +#endif /* WOLFTPM2_NO_WOLFCRYPT */ + #endif diff --git a/include/linux/byteorder/generic.h b/include/linux/byteorder/generic.h index bee0ff60336..def601eed2b 100644 --- a/include/linux/byteorder/generic.h +++ b/include/linux/byteorder/generic.h @@ -89,12 +89,6 @@ #define le32_to_cpu __le32_to_cpu #define cpu_to_le16 __cpu_to_le16 #define le16_to_cpu __le16_to_cpu -#define cpu_to_be64 __cpu_to_be64 -#define be64_to_cpu __be64_to_cpu -#define cpu_to_be32 __cpu_to_be32 -#define be32_to_cpu __be32_to_cpu -#define cpu_to_be16 __cpu_to_be16 -#define be16_to_cpu __be16_to_cpu #define cpu_to_le64p __cpu_to_le64p #define le64_to_cpup __le64_to_cpup #define cpu_to_le32p __cpu_to_le32p @@ -120,6 +114,31 @@ #define cpu_to_be16s __cpu_to_be16s #define be16_to_cpus __be16_to_cpus +/* + * Check if byte-order functions are already defined by the system: + * wolfTPM, wolfCrypt, and U-boot all define these functions, so + * we need to check if they are already defined before defining + * them again. + */ +#ifndef cpu_to_be16 +#define cpu_to_be16 __cpu_to_be16 +#endif +#ifndef cpu_to_be32 +#define cpu_to_be32 __cpu_to_be32 +#endif +#ifndef cpu_to_be64 +#define cpu_to_be64 __cpu_to_be64 +#endif +#ifndef be16_to_cpu +#define be16_to_cpu __be16_to_cpu +#endif +#ifndef be32_to_cpu +#define be32_to_cpu __be32_to_cpu +#endif +#ifndef be64_to_cpu +#define be64_to_cpu __be64_to_cpu +#endif + /* * They have to be macros in order to do the constant folding * correctly - if the argument passed into a inline function diff --git a/include/tpm-common.h b/include/tpm-common.h index bfb84a931d1..1ea4463fbbe 100644 --- a/include/tpm-common.h +++ b/include/tpm-common.h @@ -337,4 +337,26 @@ enum tpm_version tpm_get_version(struct udevice *dev); /* Iterate on all TPM devices */ #define for_each_tpm_device(dev) uclass_foreach_dev_probe(UCLASS_TPM, (dev)) +/** + * tpm_show_device() - Show all TPM devices + * + * Return: 0 on success, -ve on failure + */ +int tpm_show_device(void); + +/** + * tpm_set_device() - Set the TPM device to use + * + * @num: The number of the TPM device to use + * Return: 0 on success, -ve on failure + */ +int tpm_set_device(unsigned long num); + +/** + * get_tpm() - Get the TPM device + * + * Return: 0 on success, -ve on failure + */ +int get_tpm(struct udevice **devp); + #endif /* __TPM_COMMON_H */ diff --git a/include/wolftpm.h b/include/wolftpm.h new file mode 100644 index 00000000000..dfa8af327f4 --- /dev/null +++ b/include/wolftpm.h @@ -0,0 +1,33 @@ +/* wolftpm.h +* +* SPDX-License-Identifier: GPL-2.0+ +* +* (C) Copyright 2025 +* Aidan Garske +*/ + +#ifndef __WOLFTPM_H__ +#define __WOLFTPM_H__ + +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#ifdef WOLFTPM_FIRMWARE_UPGRADE +int TPM2_IFX_FwData_Cb(uint8_t* data, uint32_t data_req_sz, uint32_t offset, void* cb_ctx); +const char* TPM2_IFX_GetOpModeStr(int opMode); +void TPM2_IFX_PrintInfo(WOLFTPM2_CAPS* caps); +#endif + +int TPM2_PCRs_Print(void); +int TPM2_Init_Device(WOLFTPM2_DEV* dev, void* userCtx); + +#ifdef __cplusplus +} +#endif + +#endif /* __WOLFTPM_H__ */ diff --git a/lib/Kconfig b/lib/Kconfig index 931d5206936..24477ea53c9 100644 --- a/lib/Kconfig +++ b/lib/Kconfig @@ -500,6 +500,19 @@ config TPM If you want a fully functional TPM enable all hashing algorithms. If you enabled measured boot all hashing algorithms are selected. +config TPM_WOLF + bool "Enable wolfTPM support" + depends on DM + imply DM_RNG + select SHA1 + help + This option enables support for wolfTPM in U-Boot. WolfTPM can be + used to update ARM specific platforms. Enabling this option allows + U-Boot to interact with the TPM using wolfTPM commands such as + firmware updates, PCR extend, and more. It is especially useful on + platforms that require support for secure boot and other TPM-related + functionality. + config SPL_TPM bool "Trusted Platform Module (TPM) Support in SPL" depends on SPL_DM diff --git a/lib/Makefile b/lib/Makefile index 70667f3728c..ba3a9e85c11 100644 --- a/lib/Makefile +++ b/lib/Makefile @@ -55,6 +55,7 @@ obj-$(CONFIG_BITREVERSE) += bitrev.o obj-y += list_sort.o endif +# U-boot TPM obj-$(CONFIG_$(PHASE_)TPM) += tpm-common.o ifeq ($(CONFIG_$(PHASE_)TPM),y) obj-$(CONFIG_TPM) += tpm_api.o @@ -64,6 +65,23 @@ obj-$(CONFIG_EFI_TCG2_PROTOCOL) += tpm_tcg2.o obj-$(CONFIG_MEASURED_BOOT) += tpm_tcg2.o endif +# wolfTPM with TPM 2.0 support (including TPM firmware update) +ifeq ($(CONFIG_TPM_WOLF),y) +ifeq ($(CONFIG_TPM_V2),y) +ccflags-y += -Ilib/wolftpm \ + -Iinclude/configs \ + -DWOLFTPM_USER_SETTINGS +obj-y += wolftpm/hal/tpm_io.o +obj-$(CONFIG_WOLFTPM_LINUX_DEV) += wolftpm/src/tpm2_linux.o +obj-y += wolftpm/src/tpm2.o +obj-y += wolftpm/src/tpm2_packet.o +obj-y += wolftpm/src/tpm2_tis.o +obj-y += wolftpm/src/tpm2_wrap.o +obj-y += wolftpm/src/tpm2_param_enc.o +obj-y += wolftpm.o +endif +endif + obj-$(CONFIG_$(PHASE_)CRC8) += crc8.o obj-$(CONFIG_$(PHASE_)CRC16) += crc16.o obj-$(CONFIG_$(PHASE_)CRC16) += crc16-ccitt.o diff --git a/lib/wolftpm b/lib/wolftpm new file mode 160000 index 00000000000..19559386121 --- /dev/null +++ b/lib/wolftpm @@ -0,0 +1 @@ +Subproject commit 1955938612159325826329e1f7e6bb0d1e0f799f diff --git a/lib/wolftpm.c b/lib/wolftpm.c new file mode 100644 index 00000000000..a2b8a302365 --- /dev/null +++ b/lib/wolftpm.c @@ -0,0 +1,56 @@ +/* wolftpm.c + * + * SPDX-License-Identifier: GPL-2.0+ + * + * (C) Copyright 2025 + * Aidan Garske + */ + +/* wolfTPM wrapper layer to expose U-boot API + * when wolfCrypt is not available. This is used by + * the U-boot firmware update command. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* Add wolfTPM type definitions */ +typedef uint8_t byte; +typedef uint32_t word32; + +#ifdef WOLFTPM2_NO_WOLFCRYPT +int wc_Sha384Hash(const byte* data, word32 len, byte* hash) +{ + struct hash_algo *algo; + u8 *output; + void *buf; + + if (hash_lookup_algo("sha384", &algo)) { + printf("Unknown hash algorithm 'sha384'\n"); + return -1; + } + + output = (u8 *)memalign(ARCH_DMA_MINALIGN, + algo->digest_size); + if (!output) { + return -ENOMEM; + } + + buf = (void *)map_sysmem((ulong)data, len); + algo->hash_func_ws(buf, len, output, algo->chunk_size); + unmap_sysmem(buf); + + memcpy(hash, output, algo->digest_size); + + free(output); + return 0; +} +#endif /* WOLFTPM2_NO_WOLFCRYPT */ diff --git a/scripts/check-local-export b/scripts/check-local-export index 6ccc2f46741..ce520772866 100755 --- a/scripts/check-local-export +++ b/scripts/check-local-export @@ -5,6 +5,15 @@ # # Exit with error if a local exported symbol is found. # EXPORT_SYMBOL should be used for global symbols. +# +# NOTE: This check is skipped on macOS with older bash that lacks +# associative arrays support (bash 4.0+). The check will run properly +# on Linux build systems. + +# Check for bash 4+ (required for associative arrays) +if [[ ${BASH_VERSINFO[0]} -lt 4 ]]; then + exit 0 +fi set -e diff --git a/test/cmd/Makefile b/test/cmd/Makefile index 2476068aee6..08fbc31a06a 100644 --- a/test/cmd/Makefile +++ b/test/cmd/Makefile @@ -45,3 +45,4 @@ endif obj-$(CONFIG_ARM_FFA_TRANSPORT) += armffa.o endif obj-$(CONFIG_CMD_SPAWN) += spawn.o +obj-$(CONFIG_TPM_WOLF) += wolftpm.o diff --git a/test/cmd/wolftpm.c b/test/cmd/wolftpm.c new file mode 100644 index 00000000000..b2e6f82a098 --- /dev/null +++ b/test/cmd/wolftpm.c @@ -0,0 +1,364 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Tests for wolfTPM commands + * + * Copyright (C) 2025 wolfSSL Inc. + * Author: Aidan Garske + * + * Based on test/py/tests/test_tpm2.py and test/dm/tpm.c + * + * Note: These tests verify command success via return code only. + * Console output is not checked since it varies with debug levels. + * Run with: ut cmd + */ + +#include +#include +#include +#include +#include +#include + +/** + * Test wolfTPM autostart command + * + * This initializes the TPM, performs startup and self-test + */ +static int cmd_test_wolftpm_autostart(struct unit_test_state *uts) +{ + /* Initialize and autostart the TPM */ + ut_assertok(run_command("tpm2 autostart", 0)); + + return 0; +} +CMD_TEST(cmd_test_wolftpm_autostart, 0); + +/** + * Test wolfTPM init command + */ +static int cmd_test_wolftpm_init(struct unit_test_state *uts) +{ + ut_assertok(run_command("tpm2 init", 0)); + + return 0; +} +CMD_TEST(cmd_test_wolftpm_init, 0); + +/** + * Test wolfTPM info command + * + * Display TPM device information + */ +static int cmd_test_wolftpm_info(struct unit_test_state *uts) +{ + /* First autostart to ensure TPM is ready */ + ut_assertok(run_command("tpm2 autostart", 0)); + + /* Get TPM info */ + ut_assertok(run_command("tpm2 info", 0)); + + return 0; +} +CMD_TEST(cmd_test_wolftpm_info, 0); + +/** + * Test wolfTPM state command + * + * Display TPM internal state + */ +static int cmd_test_wolftpm_state(struct unit_test_state *uts) +{ + /* First autostart to ensure TPM is ready */ + ut_assertok(run_command("tpm2 autostart", 0)); + + /* Get TPM state */ + ut_assertok(run_command("tpm2 state", 0)); + + return 0; +} +CMD_TEST(cmd_test_wolftpm_state, 0); + +/** + * Test wolfTPM device command + * + * Show all TPM devices + */ +static int cmd_test_wolftpm_device(struct unit_test_state *uts) +{ + /* Show TPM devices - no autostart needed */ + ut_assertok(run_command("tpm2 device", 0)); + + return 0; +} +CMD_TEST(cmd_test_wolftpm_device, 0); + +/** + * Test wolfTPM self_test command + */ +static int cmd_test_wolftpm_self_test(struct unit_test_state *uts) +{ + /* First autostart to ensure TPM is ready */ + ut_assertok(run_command("tpm2 autostart", 0)); + + /* Run full self test */ + ut_assertok(run_command("tpm2 self_test full", 0)); + + return 0; +} +CMD_TEST(cmd_test_wolftpm_self_test, 0); + +/** + * Test wolfTPM self_test continue command + */ +static int cmd_test_wolftpm_self_test_continue(struct unit_test_state *uts) +{ + /* First autostart to ensure TPM is ready */ + ut_assertok(run_command("tpm2 autostart", 0)); + + /* Run continue self test */ + ut_assertok(run_command("tpm2 self_test continue", 0)); + + return 0; +} +CMD_TEST(cmd_test_wolftpm_self_test_continue, 0); + +/** + * Test wolfTPM startup command with TPM2_SU_CLEAR + * + * Issue TPM2_Startup with CLEAR mode (reset state) + */ +static int cmd_test_wolftpm_startup_clear(struct unit_test_state *uts) +{ + /* First init to prepare TPM */ + ut_assertok(run_command("tpm2 init", 0)); + + /* Issue startup with CLEAR mode */ + ut_assertok(run_command("tpm2 startup TPM2_SU_CLEAR", 0)); + + return 0; +} +CMD_TEST(cmd_test_wolftpm_startup_clear, 0); + +/** + * Test wolfTPM startup command with TPM2_SU_STATE + * + * Issue TPM2_Startup with STATE mode (preserved state) + */ +static int cmd_test_wolftpm_startup_state(struct unit_test_state *uts) +{ + /* First autostart to ensure TPM has state */ + ut_assertok(run_command("tpm2 autostart", 0)); + + /* Shutdown first to prepare for STATE startup */ + run_command("tpm2 startup TPM2_SU_STATE off", 0); + + /* Re-init */ + ut_assertok(run_command("tpm2 init", 0)); + + /* Issue startup with STATE mode - may return already started */ + run_command("tpm2 startup TPM2_SU_STATE", 0); + + return 0; +} +CMD_TEST(cmd_test_wolftpm_startup_state, 0); + +/** + * Test wolfTPM get_capability command + * + * Read TPM capabilities by property + */ +static int cmd_test_wolftpm_get_capability(struct unit_test_state *uts) +{ + /* First autostart to ensure TPM is ready */ + ut_assertok(run_command("tpm2 autostart", 0)); + + /* Get capability - property 0x6 (TPM_CAP_TPM_PROPERTIES), 0x20e (PT_MANUFACTURER) */ + ut_assertok(run_command("tpm2 get_capability 0x6 0x20e 0x1000000 1", 0)); + + return 0; +} +CMD_TEST(cmd_test_wolftpm_get_capability, 0); + +/** + * Test wolfTPM caps command (get capabilities) + * + * Display TPM capabilities and vendor info + */ +static int cmd_test_wolftpm_caps(struct unit_test_state *uts) +{ + /* First autostart to ensure TPM is ready */ + ut_assertok(run_command("tpm2 autostart", 0)); + + /* Get TPM capabilities */ + ut_assertok(run_command("tpm2 caps", 0)); + + return 0; +} +CMD_TEST(cmd_test_wolftpm_caps, 0); + +/** + * Test wolfTPM clear command + * + * Reset TPM internal state using LOCKOUT hierarchy + */ +static int cmd_test_wolftpm_clear(struct unit_test_state *uts) +{ + /* First autostart to ensure TPM is ready */ + ut_assertok(run_command("tpm2 autostart", 0)); + + /* Clear using LOCKOUT hierarchy */ + ut_assertok(run_command("tpm2 clear TPM2_RH_LOCKOUT", 0)); + + return 0; +} +CMD_TEST(cmd_test_wolftpm_clear, 0); + +/** + * Test wolfTPM pcr_read command + * + * Read PCR value from a specific index to a memory address + */ +static int cmd_test_wolftpm_pcr_read(struct unit_test_state *uts) +{ + /* First autostart to ensure TPM is ready */ + ut_assertok(run_command("tpm2 autostart", 0)); + + /* Read PCR 0 with SHA256 to memory address 0x1000000 */ + ut_assertok(run_command("tpm2 pcr_read 0 0x1000000 SHA256", 0)); + + return 0; +} +CMD_TEST(cmd_test_wolftpm_pcr_read, 0); + +/** + * Test wolfTPM pcr_extend command + * + * Extend a PCR with a digest value + */ +static int cmd_test_wolftpm_pcr_extend(struct unit_test_state *uts) +{ + /* First autostart to ensure TPM is ready */ + ut_assertok(run_command("tpm2 autostart", 0)); + + /* Clear to start fresh */ + run_command("tpm2 clear TPM2_RH_LOCKOUT", 0); + + /* Extend PCR 16 (resettable PCR) with digest from memory + * PCR 16-23 are typically available for debug/testing + */ + ut_assertok(run_command("tpm2 pcr_extend 16 0x1000000 SHA256", 0)); + + return 0; +} +CMD_TEST(cmd_test_wolftpm_pcr_extend, 0); + +/** + * Test wolfTPM pcr_print command + * + * Print all PCR values + */ +static int cmd_test_wolftpm_pcr_print(struct unit_test_state *uts) +{ + /* First autostart to ensure TPM is ready */ + ut_assertok(run_command("tpm2 autostart", 0)); + + /* Print all PCRs */ + ut_assertok(run_command("tpm2 pcr_print", 0)); + + return 0; +} +CMD_TEST(cmd_test_wolftpm_pcr_print, 0); + +/** + * Test wolfTPM pcr_allocate command + * + * Reconfigure PCR bank algorithm. Note: A TPM restart is required + * for changes to take effect, so we just verify the command succeeds. + */ +static int cmd_test_wolftpm_pcr_allocate(struct unit_test_state *uts) +{ + /* First autostart to ensure TPM is ready */ + ut_assertok(run_command("tpm2 autostart", 0)); + + /* Allocate SHA256 bank on - this should succeed */ + ut_assertok(run_command("tpm2 pcr_allocate SHA256 on", 0)); + + return 0; +} +CMD_TEST(cmd_test_wolftpm_pcr_allocate, 0); + +/** + * Test wolfTPM dam_reset command + * + * Reset Dictionary Attack Mitigation counter + */ +static int cmd_test_wolftpm_dam_reset(struct unit_test_state *uts) +{ + /* First autostart to ensure TPM is ready */ + ut_assertok(run_command("tpm2 autostart", 0)); + + /* Reset DAM counter */ + ut_assertok(run_command("tpm2 dam_reset", 0)); + + return 0; +} +CMD_TEST(cmd_test_wolftpm_dam_reset, 0); + +/** + * Test wolfTPM dam_parameters command + * + * Set Dictionary Attack Mitigation parameters + */ +static int cmd_test_wolftpm_dam_parameters(struct unit_test_state *uts) +{ + /* First autostart to ensure TPM is ready */ + ut_assertok(run_command("tpm2 autostart", 0)); + + /* Set DAM parameters: + * - max_tries: 3 + * - recovery_time: 10 seconds + * - lockout_recovery: 0 seconds + */ + ut_assertok(run_command("tpm2 dam_parameters 3 10 0", 0)); + + return 0; +} +CMD_TEST(cmd_test_wolftpm_dam_parameters, 0); + +/** + * Test wolfTPM change_auth command + * + * Change hierarchy authorization password + * Note: Requires WOLFTPM2_NO_WOLFCRYPT to NOT be defined + */ +static int cmd_test_wolftpm_change_auth(struct unit_test_state *uts) +{ + /* First autostart and clear to ensure clean state */ + ut_assertok(run_command("tpm2 autostart", 0)); + run_command("tpm2 clear TPM2_RH_LOCKOUT", 0); + + /* Change LOCKOUT password to "testpw" + * This may fail if WOLFTPM2_NO_WOLFCRYPT is defined + */ + if (run_command("tpm2 change_auth TPM2_RH_LOCKOUT testpw", 0) == 0) { + /* Clear with new password to verify it worked */ + ut_assertok(run_command("tpm2 clear TPM2_RH_LOCKOUT testpw", 0)); + } + + return 0; +} +CMD_TEST(cmd_test_wolftpm_change_auth, 0); + +/** + * Cleanup test - ensure TPM is cleared after tests + */ +static int cmd_test_wolftpm_cleanup(struct unit_test_state *uts) +{ + /* Clear TPM to reset any passwords or test state */ + run_command("tpm2 autostart", 0); + run_command("tpm2 clear TPM2_RH_LOCKOUT", 0); + run_command("tpm2 clear TPM2_RH_PLATFORM", 0); + + return 0; +} +CMD_TEST(cmd_test_wolftpm_cleanup, 0); diff --git a/test/py/tests/test_wolftpm.py b/test/py/tests/test_wolftpm.py new file mode 100644 index 00000000000..9d67211f96c --- /dev/null +++ b/test/py/tests/test_wolftpm.py @@ -0,0 +1,373 @@ +# SPDX-License-Identifier: GPL-2.0+ +# Copyright (C) 2025 wolfSSL Inc. +# Author: Aidan Garske +# +# Based on test_tpm2.py by Miquel Raynal + +""" +Test the wolfTPM related commands. These tests require a TPM device +(real hardware or software TPM emulator like swtpm). + +Notes: +* These tests will prove the password mechanism. The TPM chip must be cleared of + any password. +* Tests are designed to be similar to test_tpm2.py but use wolfTPM wrapper APIs. + +Configuration: +* Set env__wolftpm_device_test_skip to True to skip these tests. +""" + +import os.path +import pytest +import utils +import re +import time + + +def force_init(ubman, force=False): + """Initialize wolfTPM before running tests. + + When a test fails, U-Boot may be reset. Because TPM stack must be initialized + after each reboot, we must ensure these lines are always executed before + trying any command or they will fail with no reason. + """ + skip_test = ubman.config.env.get('env__wolftpm_device_test_skip', False) + if skip_test: + pytest.skip('skip wolfTPM device test') + output = ubman.run_command('wolftpm autostart') + if force or 'Error' not in output: + ubman.run_command('echo --- start of init ---') + ubman.run_command('wolftpm clear TPM2_RH_LOCKOUT') + output = ubman.run_command('echo $?') + if not output.endswith('0'): + ubman.run_command('wolftpm clear TPM2_RH_PLATFORM') + ubman.run_command('echo --- end of init ---') + + +@pytest.mark.buildconfigspec('tpm_wolf') +def test_wolftpm_autostart(ubman): + """Test wolfTPM autostart command. + + Initialize the software stack, perform startup and self-test. + """ + skip_test = ubman.config.env.get('env__wolftpm_device_test_skip', False) + if skip_test: + pytest.skip('skip wolfTPM device test') + ubman.run_command('wolftpm autostart') + output = ubman.run_command('echo $?') + assert output.endswith('0') + + +@pytest.mark.buildconfigspec('tpm_wolf') +def test_wolftpm_init(ubman): + """Test wolfTPM init command. + + Initialize the TPM device for communication. + """ + skip_test = ubman.config.env.get('env__wolftpm_device_test_skip', False) + if skip_test: + pytest.skip('skip wolfTPM device test') + ubman.run_command('wolftpm init') + output = ubman.run_command('echo $?') + assert output.endswith('0') + + +@pytest.mark.buildconfigspec('tpm_wolf') +def test_wolftpm_self_test_full(ubman): + """Test wolfTPM full self_test command. + + Perform a full TPM self-test to verify all components are operational. + """ + skip_test = ubman.config.env.get('env__wolftpm_device_test_skip', False) + if skip_test: + pytest.skip('skip wolfTPM device test') + ubman.run_command('wolftpm autostart') + ubman.run_command('wolftpm self_test full') + output = ubman.run_command('echo $?') + assert output.endswith('0') + + +@pytest.mark.buildconfigspec('tpm_wolf') +def test_wolftpm_self_test_continue(ubman): + """Test wolfTPM continue self_test command. + + Ask the TPM to finish any remaining self tests. + """ + skip_test = ubman.config.env.get('env__wolftpm_device_test_skip', False) + if skip_test: + pytest.skip('skip wolfTPM device test') + ubman.run_command('wolftpm autostart') + ubman.run_command('wolftpm self_test continue') + output = ubman.run_command('echo $?') + assert output.endswith('0') + + +@pytest.mark.buildconfigspec('tpm_wolf') +def test_wolftpm_caps(ubman): + """Test wolfTPM caps command. + + Display TPM capabilities and vendor information. + """ + force_init(ubman) + caps_output = ubman.run_command('wolftpm caps') + output = ubman.run_command('echo $?') + assert output.endswith('0') + # Should contain manufacturer info + assert 'Mfg' in caps_output or 'Vendor' in caps_output or 'wolfTPM' in caps_output + + +@pytest.mark.buildconfigspec('tpm_wolf') +def test_wolftpm_clear(ubman): + """Test wolfTPM clear command. + + Clear the TPM internal state using LOCKOUT hierarchy. + LOCKOUT/PLATFORM hierarchies must not have a password set. + """ + skip_test = ubman.config.env.get('env__wolftpm_device_test_skip', False) + if skip_test: + pytest.skip('skip wolfTPM device test') + ubman.run_command('wolftpm autostart') + ubman.run_command('wolftpm clear TPM2_RH_LOCKOUT') + output = ubman.run_command('echo $?') + assert output.endswith('0') + + ubman.run_command('wolftpm clear TPM2_RH_PLATFORM') + output = ubman.run_command('echo $?') + assert output.endswith('0') + + +@pytest.mark.buildconfigspec('tpm_wolf') +def test_wolftpm_change_auth(ubman): + """Test wolfTPM change_auth command. + + Change the owner/hierarchy password. + """ + force_init(ubman) + + # Change LOCKOUT password to 'unicorn' + ubman.run_command('wolftpm change_auth TPM2_RH_LOCKOUT unicorn') + output = ubman.run_command('echo $?') + assert output.endswith('0') + + # Clear with new password to verify + ubman.run_command('wolftpm clear TPM2_RH_LOCKOUT unicorn') + output = ubman.run_command('echo $?') + ubman.run_command('wolftpm clear TPM2_RH_PLATFORM') + assert output.endswith('0') + + +@pytest.mark.buildconfigspec('tpm_wolf') +def test_wolftpm_dam_parameters(ubman): + """Test wolfTPM dam_parameters command. + + Change Dictionary Attack Mitigation parameters: + - Max number of failed authentication before lockout: 3 + - Time before failure counter is decremented: 10 sec + - Time after lockout failure before retry: 0 sec + """ + force_init(ubman) + + # Set DAM parameters + ubman.run_command('wolftpm dam_parameters 3 10 0') + output = ubman.run_command('echo $?') + assert output.endswith('0') + + +@pytest.mark.buildconfigspec('tpm_wolf') +def test_wolftpm_dam_reset(ubman): + """Test wolfTPM dam_reset command. + + Reset the Dictionary Attack Mitigation counter. + """ + force_init(ubman) + + ubman.run_command('wolftpm dam_reset') + output = ubman.run_command('echo $?') + assert output.endswith('0') + + +@pytest.mark.buildconfigspec('tpm_wolf') +def test_wolftpm_pcr_read(ubman): + """Test wolfTPM pcr_read command. + + Read PCR value from a specific index. + """ + force_init(ubman) + + # Read PCR 0 with SHA256 + read_pcr = ubman.run_command('wolftpm pcr_read 0 sha256') + output = ubman.run_command('echo $?') + assert output.endswith('0') + + # Check for PCR output + assert 'PCR' in read_pcr or 'digest' in read_pcr.lower() or 'sha256' in read_pcr.lower() + + +@pytest.mark.buildconfigspec('tpm_wolf') +def test_wolftpm_pcr_extend(ubman): + """Test wolfTPM pcr_extend command. + + Extend a PCR with a digest value. + PCR 16-23 are typically available for debug/testing. + """ + force_init(ubman) + ram = utils.find_ram_base(ubman) + + # Read PCR 16 first + read_pcr = ubman.run_command('wolftpm pcr_read 16 sha256') + output = ubman.run_command('echo $?') + assert output.endswith('0') + + # Extend PCR 16 with zeroed memory + ubman.run_command('wolftpm pcr_extend 16 0x%x sha256' % ram) + output = ubman.run_command('echo $?') + assert output.endswith('0') + + # Read again to verify it changed + read_pcr_after = ubman.run_command('wolftpm pcr_read 16 sha256') + output = ubman.run_command('echo $?') + assert output.endswith('0') + + +@pytest.mark.buildconfigspec('tpm_wolf') +def test_wolftpm_pcr_print(ubman): + """Test wolfTPM pcr_print command. + + Print all assigned PCRs. + """ + force_init(ubman) + + pcr_output = ubman.run_command('wolftpm pcr_print') + output = ubman.run_command('echo $?') + assert output.endswith('0') + + # Should contain PCR info + assert 'PCR' in pcr_output or 'Assigned' in pcr_output + + +@pytest.mark.buildconfigspec('tpm_wolf') +def test_wolftpm_info(ubman): + """Test wolfTPM info command. + + Display TPM device information. + """ + force_init(ubman) + + ubman.run_command('wolftpm info') + output = ubman.run_command('echo $?') + assert output.endswith('0') + + +@pytest.mark.buildconfigspec('tpm_wolf') +def test_wolftpm_state(ubman): + """Test wolfTPM state command. + + Display TPM internal state. + """ + force_init(ubman) + + ubman.run_command('wolftpm state') + output = ubman.run_command('echo $?') + assert output.endswith('0') + + +@pytest.mark.buildconfigspec('tpm_wolf') +def test_wolftpm_device(ubman): + """Test wolfTPM device command. + + Show all TPM devices. + """ + skip_test = ubman.config.env.get('env__wolftpm_device_test_skip', False) + if skip_test: + pytest.skip('skip wolfTPM device test') + ubman.run_command('wolftpm device') + output = ubman.run_command('echo $?') + assert output.endswith('0') + + +@pytest.mark.buildconfigspec('tpm_wolf') +def test_wolftpm_startup_clear(ubman): + """Test wolfTPM startup command with TPM2_SU_CLEAR. + + Issue TPM2_Startup with CLEAR mode (reset state). + """ + skip_test = ubman.config.env.get('env__wolftpm_device_test_skip', False) + if skip_test: + pytest.skip('skip wolfTPM device test') + ubman.run_command('wolftpm init') + ubman.run_command('wolftpm startup TPM2_SU_CLEAR') + output = ubman.run_command('echo $?') + assert output.endswith('0') + + +@pytest.mark.buildconfigspec('tpm_wolf') +def test_wolftpm_startup_state(ubman): + """Test wolfTPM startup command with TPM2_SU_STATE. + + Issue TPM2_Startup with STATE mode (preserved state). + """ + skip_test = ubman.config.env.get('env__wolftpm_device_test_skip', False) + if skip_test: + pytest.skip('skip wolfTPM device test') + # First autostart to have valid state + ubman.run_command('wolftpm autostart') + # Shutdown with STATE + ubman.run_command('wolftpm startup TPM2_SU_STATE off') + # Re-init + ubman.run_command('wolftpm init') + # Startup with STATE - may return already started + ubman.run_command('wolftpm startup TPM2_SU_STATE') + output = ubman.run_command('echo $?') + # May return non-zero if already started, just verify command ran + assert output is not None + + +@pytest.mark.buildconfigspec('tpm_wolf') +def test_wolftpm_startup_shutdown(ubman): + """Test wolfTPM startup shutdown command. + + Issue TPM2_Shutdown. + """ + skip_test = ubman.config.env.get('env__wolftpm_device_test_skip', False) + if skip_test: + pytest.skip('skip wolfTPM device test') + ubman.run_command('wolftpm autostart') + ubman.run_command('wolftpm startup TPM2_SU_CLEAR off') + output = ubman.run_command('echo $?') + assert output.endswith('0') + + +@pytest.mark.buildconfigspec('tpm_wolf') +def test_wolftpm_get_capability(ubman): + """Test wolfTPM get_capability command. + + Read TPM capabilities by property. + """ + force_init(ubman) + ram = utils.find_ram_base(ubman) + + # Get capability - TPM_CAP_TPM_PROPERTIES (0x6), PT_MANUFACTURER (0x20e) + ubman.run_command('wolftpm get_capability 0x6 0x20e 0x%x 1' % ram) + output = ubman.run_command('echo $?') + assert output.endswith('0') + + +@pytest.mark.buildconfigspec('tpm_wolf') +def test_wolftpm_pcr_allocate(ubman): + """Test wolfTPM pcr_allocate command. + + Reconfigure PCR bank algorithm. + Note: A TPM restart is required for changes to take effect. + """ + force_init(ubman) + + # Allocate SHA256 bank on + ubman.run_command('wolftpm pcr_allocate SHA256 on') + output = ubman.run_command('echo $?') + assert output.endswith('0') + + +@pytest.mark.buildconfigspec('tpm_wolf') +def test_wolftpm_cleanup(ubman): + """Cleanup test - ensure TPM is cleared after tests.""" + force_init(ubman, True) -- 2.43.0