diff --git a/.github/workflows/pikeos-build.yml b/.github/workflows/pikeos-build.yml new file mode 100644 index 00000000..8832456a --- /dev/null +++ b/.github/workflows/pikeos-build.yml @@ -0,0 +1,48 @@ +name: PikeOS Build (WOLFTPM_PIKEOS) + +# Build and host test of the PikeOS TPM driver transport (--enable-pikeos). +# The PikeOS SDK is not available in CI, so tests/pikeos_stub supplies p4.h and +# vm.h with the SDK's signatures, and tests/pikeos_unit.test stubs the vm_* calls. + +on: + push: + branches: [ 'master', 'main', 'release/**' ] + pull_request: + branches: [ '*' ] + types: [opened, synchronize, reopened, ready_for_review] + +concurrency: + group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} + cancel-in-progress: true + +jobs: + pikeos: + if: github.event_name != 'pull_request' || github.event.pull_request.draft == false + runs-on: ubuntu-latest + container: + image: ghcr.io/wolfssl/wolftpm-ci:v1.1 + defaults: + run: + shell: bash + steps: + - uses: actions/checkout@v4 + with: + submodules: true + + - name: Configure (PikeOS transport, no wolfCrypt) + run: | + ./autogen.sh + ./configure --enable-pikeos --disable-wolfcrypt --disable-examples + + - name: Build library and run transport test + run: | + make src/libwolftpm.la CFLAGS="-Itests/pikeos_stub -Werror" + make tests/pikeos_unit.test CFLAGS="-Itests/pikeos_stub -Werror" + ./tests/pikeos_unit.test + + - name: Build library and run transport test (debug) + run: | + make clean + make src/libwolftpm.la tests/pikeos_unit.test \ + CFLAGS="-Itests/pikeos_stub -Werror -DDEBUG_WOLFTPM -DWOLFTPM_DEBUG_VERBOSE" + ./tests/pikeos_unit.test diff --git a/.gitignore b/.gitignore index 71a6162a..84e7c1aa 100644 --- a/.gitignore +++ b/.gitignore @@ -76,6 +76,7 @@ tests/fwtpm_unit.test tests/fwtpm_hal_unit.test tests/fwtpm_hal_unit.log tests/swtpm_uart_unit.test +tests/pikeos_unit.test tests/swtpm_uart_unit.log tests/fwtpm_check.sh.log tests/packet_auth.test diff --git a/CMakeLists.txt b/CMakeLists.txt index 01147b63..f2f72029 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -82,6 +82,7 @@ set(TPM_SOURCES src/tpm2.c src/tpm2_linux.c src/tpm2_packet.c + src/tpm2_pikeos.c src/tpm2_param_enc.c src/tpm2_swtpm.c src/tpm2_tis.c @@ -196,7 +197,7 @@ endif() set(WOLFTPM_INTERFACE "auto" CACHE STRING "Select interface to TPM") set_property(CACHE WOLFTPM_INTERFACE - PROPERTY STRINGS "auto;SWTPM;WINAPI;DEVTPM;SPI;I2C;MMIO") + PROPERTY STRINGS "auto;SWTPM;WINAPI;DEVTPM;PIKEOS;SPI;I2C;MMIO") # automatically set message("INTERFACE ${WOLFTPM_INTERFACE}") @@ -236,6 +237,9 @@ if("${WOLFTPM_INTERFACE}" STREQUAL "SWTPM") elseif("${WOLFTPM_INTERFACE}" STREQUAL "DEVTPM") list(APPEND WOLFTPM_DEFINITIONS "-DWOLFTPM_LINUX_DEV") +elseif("${WOLFTPM_INTERFACE}" STREQUAL "PIKEOS") + list(APPEND WOLFTPM_DEFINITIONS "-DWOLFTPM_PIKEOS") + elseif("${WOLFTPM_INTERFACE}" STREQUAL "WINAPI") list(APPEND WOLFTPM_DEFINITIONS "-DWOLFTPM_WINAPI") if(BUILD_WOLFTPM_LIB) diff --git a/configure.ac b/configure.ac index c73c4f33..61449572 100644 --- a/configure.ac +++ b/configure.ac @@ -247,6 +247,22 @@ then AM_CFLAGS="$AM_CFLAGS -DWOLFTPM_LINUX_DEV" fi +# PikeOS TPM driver through the vm_open/vm_read/vm_write file provider +AC_ARG_ENABLE([pikeos], + [AS_HELP_STRING([--enable-pikeos],[Enable use of TPM through the PikeOS driver file provider (default: disabled)])], + [ ENABLED_PIKEOS=$enableval ], + [ ENABLED_PIKEOS=no ] + ) + +if test "x$ENABLED_PIKEOS" = "xyes" +then + if test "x$ENABLED_DEVTPM" = "xyes" + then + AC_MSG_ERROR([Cannot enable both pikeos and devtpm]) + fi + AM_CFLAGS="$AM_CFLAGS -DWOLFTPM_PIKEOS" +fi + # If the user explicitly picked any hardware path (vendor, bus, kernel # driver, autodetect), don't default-enable the software TPMs — the SPI HAL # in hal/tpm_io_linux.c is excluded when WOLFTPM_SWTPM is defined. @@ -255,7 +271,7 @@ for _wt_v in "$enable_infineon" "$enable_st" "$enable_st33" \ "$enable_microchip" "$enable_mchp" \ "$enable_nuvoton" "$enable_nations" "$enable_sealsq" \ "$enable_spi" "$enable_i2c" "$enable_mmio" \ - "$enable_devtpm" "$enable_autodetect" \ + "$enable_devtpm" "$enable_pikeos" "$enable_autodetect" \ "$enable_winapi" "$enable_wintbs"; do if test -n "$_wt_v" && test "x$_wt_v" != "xno"; then WOLFTPM_HW_SELECTED=yes @@ -284,7 +300,7 @@ fi # SW TPM device Support AC_ARG_ENABLE([swtpm], - [AS_HELP_STRING([--enable-swtpm],[Enable use of TPM through the SW socket driver (default: enabled on Linux x86_64/aarch64, disabled elsewhere or when any --enable-/--enable-spi/--enable-i2c/--enable-mmio/--enable-devtpm/--enable-autodetect is explicitly set)])], + [AS_HELP_STRING([--enable-swtpm],[Enable use of TPM through the SW socket driver (default: enabled on Linux x86_64/aarch64, disabled elsewhere or when any --enable-/--enable-spi/--enable-i2c/--enable-mmio/--enable-devtpm/--enable-pikeos/--enable-autodetect is explicitly set)])], [ ENABLED_SWTPM=$enableval ], [ ENABLED_SWTPM=$WOLFTPM_DEFAULT_SWTPM ] ) @@ -306,6 +322,10 @@ then then AC_MSG_ERROR([Cannot enable both swtpm and devtpm]) fi + if test "x$ENABLED_PIKEOS" = "xyes" + then + AC_MSG_ERROR([Cannot enable both swtpm and pikeos]) + fi AM_CFLAGS="$AM_CFLAGS -DWOLFTPM_SWTPM" @@ -345,7 +365,7 @@ AC_SUBST([DISTCHECK_SWTPM_PORT_FLAG]) # Firmware TPM (fwTPM) - software TPM 2.0 simulator AC_ARG_ENABLE([fwtpm], - [AS_HELP_STRING([--enable-fwtpm],[Enable firmware TPM (fwTPM) server (default: enabled on Linux x86_64/aarch64, disabled elsewhere or when any --enable-/--enable-spi/--enable-i2c/--enable-mmio/--enable-devtpm/--enable-autodetect is explicitly set)])], + [AS_HELP_STRING([--enable-fwtpm],[Enable firmware TPM (fwTPM) server (default: enabled on Linux x86_64/aarch64, disabled elsewhere or when any --enable-/--enable-spi/--enable-i2c/--enable-mmio/--enable-devtpm/--enable-pikeos/--enable-autodetect is explicitly set)])], [ ENABLED_FWTPM=$enableval ], [ ENABLED_FWTPM=$WOLFTPM_DEFAULT_FWTPM ] ) @@ -1114,6 +1134,7 @@ AM_CONDITIONAL([BUILD_INFINEON_SPDM], [test "x$BUILD_SPDM_INFINEON" = "xyes"]) AM_CONDITIONAL([BUILD_DEVTPM], [test "x$ENABLED_DEVTPM" = "xyes"]) AM_CONDITIONAL([BUILD_SWTPM], [test "x$ENABLED_SWTPM" = "xyes" || test "x$ENABLED_SWTPM" = "xuart"]) AM_CONDITIONAL([BUILD_SWTPM_UART], [test "x$ENABLED_SWTPM" = "xuart"]) +AM_CONDITIONAL([BUILD_PIKEOS], [test "x$ENABLED_PIKEOS" = "xyes"]) AM_CONDITIONAL([BUILD_WINAPI], [test "x$ENABLED_WINAPI" = "xyes"]) AM_CONDITIONAL([BUILD_NUVOTON], [test "x$ENABLED_NUVOTON" = "xyes"]) AM_CONDITIONAL([BUILD_NATIONS], [test "x$ENABLED_NATIONS" = "xyes"]) @@ -1264,6 +1285,7 @@ SPI_ACTUAL=yes if test "x$ENABLED_I2C" = "xyes" || \ test "x$ENABLED_SWTPM" = "xyes" || test "x$ENABLED_SWTPM" = "xuart" || \ test "x$ENABLED_DEVTPM" = "xyes" || \ + test "x$ENABLED_PIKEOS" = "xyes" || \ test "x$ENABLED_WINAPI" = "xyes" then SPI_ACTUAL=no @@ -1271,6 +1293,7 @@ fi echo " * SPI: $SPI_ACTUAL" echo " * I2C: $ENABLED_I2C" echo " * Linux kernel TPM device: $ENABLED_DEVTPM" +echo " * PikeOS TPM driver: $ENABLED_PIKEOS" echo " * SWTPM: $ENABLED_SWTPM" if test "x$ENABLED_SWTPM" = "xyes"; then echo " * SWTPM Port: $SWTPM_PORT" diff --git a/docs/build-options.md b/docs/build-options.md index 4145ff98..93dc20fe 100644 --- a/docs/build-options.md +++ b/docs/build-options.md @@ -75,6 +75,7 @@ Selecting an Infineon device with its argument looks like this: | Option | Default | Effect and macros | | --- | --- | --- | | `--enable-devtpm` | disabled | Use the Linux kernel driver (`/dev/tpmrm0` or `/dev/tpm0`). Defines `WOLFTPM_LINUX_DEV`. Cannot be combined with swTPM. | +| `--enable-pikeos` | disabled | Use the PikeOS TPM driver through its file provider (`vm_open`/`vm_write`/`vm_read`). Defines `WOLFTPM_PIKEOS`. The device defaults to `tpm0:dev0`; override with `-DTPM2_PIKEOS_DEV="..."`. The application must call `vm_init()` first. PikeOS native has no `ssize_t`, so build the examples with `-DNO_FILESYSTEM`. Cannot be combined with swTPM or devtpm. On PikeOS native, `printf` output (including `DEBUG_WOLFTPM`) only appears when stdout is redirected to a console device such as `con:`. | | `--enable-swtpm` | see below | Talk to a simulator over the swtpm TCP protocol. Defines `WOLFTPM_SWTPM` and `TPM2_SWTPM_PORT`. | | `--enable-swtpm=uart` | disabled | swtpm protocol over a UART serial port, for fwTPM on embedded targets such as STM32H5. Defines `WOLFTPM_SWTPM`, `WOLFTPM_SWTPM_UART` and `TPM2_SWTPM_PORT`, where the port value is the baud rate (default 115200). | | `--with-swtpm-port=PORT` | 2321 | Sets `TPM2_SWTPM_PORT`. For `--enable-swtpm=uart` it sets the baud rate instead. | @@ -88,7 +89,7 @@ Selecting an Infineon device with its argument looks like this: - the host CPU is x86_64, amd64 or aarch64; - the host OS is not Windows (mingw, cygwin, msys, win32); - wolfCrypt is enabled; -- no hardware path was selected with any of `--enable-spi`, `--enable-i2c`, `--enable-mmio`, `--enable-devtpm`, `--enable-autodetect`, `--enable-winapi`, `--enable-infineon`, `--enable-st`, `--enable-st33`, `--enable-microchip`, `--enable-nuvoton`, `--enable-nations` or `--enable-sealsq`. +- no hardware path was selected with any of `--enable-spi`, `--enable-i2c`, `--enable-mmio`, `--enable-devtpm`, `--enable-pikeos`, `--enable-autodetect`, `--enable-winapi`, `--enable-infineon`, `--enable-st`, `--enable-st33`, `--enable-microchip`, `--enable-nuvoton`, `--enable-nations` or `--enable-sealsq`. This applies to macOS and BSD as well as Linux. Everywhere else the default is off. diff --git a/docs/getting-started.md b/docs/getting-started.md index 814ce639..0771eb2e 100644 --- a/docs/getting-started.md +++ b/docs/getting-started.md @@ -31,7 +31,7 @@ make ``` !!! note - On any non-Windows x86_64/aarch64 host (Linux, macOS, or BSD), a bare `./configure` automatically enables the software TPM backends (swTPM and fwTPM). This lets `make check` run without any TPM hardware attached. Selecting a hardware path turns this default off: any `--enable-`, or `--enable-spi`, `--enable-i2c`, `--enable-mmio`, `--enable-devtpm`, `--enable-autodetect`, `--enable-winapi`, or `--enable-wintbs`. See [System Interfaces](system-interfaces.md). + On any non-Windows x86_64/aarch64 host (Linux, macOS, or BSD), a bare `./configure` automatically enables the software TPM backends (swTPM and fwTPM). This lets `make check` run without any TPM hardware attached. Selecting a hardware path turns this default off: any `--enable-`, or `--enable-spi`, `--enable-i2c`, `--enable-mmio`, `--enable-devtpm`, `--enable-pikeos`, `--enable-autodetect`, `--enable-winapi`, or `--enable-wintbs`. See [System Interfaces](system-interfaces.md). For hardware specific build steps, see [Supported Hardware](supported-hardware.md). diff --git a/docs/hal-io-callback.md b/docs/hal-io-callback.md index 0e0bafeb..383b7aa5 100644 --- a/docs/hal-io-callback.md +++ b/docs/hal-io-callback.md @@ -8,6 +8,7 @@ The available system TPM interfaces are: * Linux `/dev/tpm0`: enabled with `WOLFTPM_LINUX_DEV` or `--enable-devtpm`. * Windows TBS: enabled with `WOLFTPM_WINAPI` or `--enable-winapi`. +* PikeOS TPM driver: enabled with `WOLFTPM_PIKEOS` or `--enable-pikeos`. * Software TPM simulator: enabled with `WOLFTPM_SWTPM` or `--enable-swtpm`. If you use a HAL IO callback, it is registered on library initialization with: diff --git a/examples/tpm_test.h b/examples/tpm_test.h index d05452ae..ed0c46f1 100644 --- a/examples/tpm_test.h +++ b/examples/tpm_test.h @@ -118,6 +118,8 @@ static const char pemFileKey[] = PEM_FILE_KEY; #include "system/time/sys_time.h" #elif defined(WOLFTPM_ZEPHYR) #include +#elif defined(WOLFTPM_PIKEOS) + #include #else #include #endif @@ -140,6 +142,10 @@ static const char pemFileKey[] = PEM_FILE_KEY; #elif defined(WOLFTPM_ZEPHYR) (void)reset; return (double)k_uptime_get() / 1000.0; + #elif defined(WOLFTPM_PIKEOS) + P4_time_t now = p4_get_time(); /* nanoseconds since boot */ + (void)reset; + return (double)now / 1000000000.0; #else struct timeval tv; gettimeofday(&tv, 0); diff --git a/hal/tpm_io.c b/hal/tpm_io.c index ade038c9..245748e6 100644 --- a/hal/tpm_io.c +++ b/hal/tpm_io.c @@ -46,7 +46,8 @@ /* Native Windows, native Linux and TPM Simulator do not need an IO callback */ #if ! (defined(WOLFTPM_LINUX_DEV) || \ defined(WOLFTPM_SWTPM) || \ - defined(WOLFTPM_WINAPI) ) && \ + defined(WOLFTPM_WINAPI) || \ + defined(WOLFTPM_PIKEOS) ) && \ (defined(WOLFTPM_EXAMPLE_HAL) || defined(WOLFTPM_MMIO) || \ defined(WOLFTPM_FWTPM_HAL)) diff --git a/hal/tpm_io.h b/hal/tpm_io.h index 1d4da11b..65a7096a 100644 --- a/hal/tpm_io.h +++ b/hal/tpm_io.h @@ -57,7 +57,7 @@ */ #if defined(WOLFTPM_LINUX_DEV) || defined(WOLFTPM_SWTPM) || \ - defined(WOLFTPM_WINAPI) + defined(WOLFTPM_WINAPI) || defined(WOLFTPM_PIKEOS) /* HAL not required, so use NULL */ #define TPM2_IoCb NULL diff --git a/src/include.am b/src/include.am index c07f4a75..c774b05d 100644 --- a/src/include.am +++ b/src/include.am @@ -15,7 +15,8 @@ src_libwolftpm_la_SOURCES = \ src/tpm2_crypto.c \ src/tpm2_param_enc.c \ src/tpm2_cryptocb.c \ - src/tpm2_linux.c + src/tpm2_linux.c \ + src/tpm2_pikeos.c if BUILD_SWTPM src_libwolftpm_la_SOURCES += src/tpm2_swtpm.c endif diff --git a/src/tpm2.c b/src/tpm2.c index 8f74566a..bf1f58a6 100644 --- a/src/tpm2.c +++ b/src/tpm2.c @@ -38,6 +38,9 @@ #ifdef WOLFTPM_WINAPI #include #endif +#ifdef WOLFTPM_PIKEOS + #include +#endif #include #ifdef WOLFTPM_SPDM #include @@ -81,6 +84,9 @@ static THREAD_LS_T TPM2_CTX* gActiveTPM; #elif defined(WOLFTPM_WINAPI) #define INTERNAL_SEND_COMMAND TPM2_WinApi_SendCommand #define TPM2_INTERNAL_CLEANUP(ctx) TPM2_WinApi_Cleanup(ctx) +#elif defined(WOLFTPM_PIKEOS) +#define INTERNAL_SEND_COMMAND TPM2_PIKEOS_SendCommand +#define TPM2_INTERNAL_CLEANUP(ctx) #else #define INTERNAL_SEND_COMMAND TPM2_TIS_SendCommand #define TPM2_INTERNAL_CLEANUP(ctx) @@ -1072,11 +1078,18 @@ TPM_RC TPM2_Init_ex(TPM2_CTX* ctx, TPM2HalIoCb ioCb, void* userCtx, #endif #if defined(WOLFTPM_LINUX_DEV) || defined(WOLFTPM_SWTPM) || \ - defined(WOLFTPM_WINAPI) + defined(WOLFTPM_WINAPI) || defined(WOLFTPM_PIKEOS) if (ioCb != NULL || userCtx != NULL) { rc = BAD_FUNC_ARG; goto exit; } +#ifdef WOLFTPM_PIKEOS + /* No TIS layer to wait on: the driver owns chip startup and locality */ + if (timeoutTries > 0) { + rc = BAD_FUNC_ARG; + goto exit; + } +#endif #elif defined(WOLFTPM_LINUX_DEV_AUTODETECT) /* Accept IO callback for SPI fallback path */ if (ioCb != NULL) { @@ -1240,6 +1253,10 @@ TPM_RC TPM2_Cleanup(TPM2_CTX* ctx) TPM2_SwtpmClose(ctx); #endif +#ifdef WOLFTPM_PIKEOS + TPM2_PIKEOS_Cleanup(ctx); +#endif + ctx->locality = TPM2_LOCALITY_UNINITIALIZED; return TPM_RC_SUCCESS; diff --git a/src/tpm2_pikeos.c b/src/tpm2_pikeos.c new file mode 100644 index 00000000..dab52a5c --- /dev/null +++ b/src/tpm2_pikeos.c @@ -0,0 +1,108 @@ +/* tpm2_pikeos.c + * + * Copyright (C) 2006-2026 wolfSSL Inc. + * + * This file is part of wolfTPM. + * + * wolfTPM is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * wolfTPM is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335, USA + */ + +#ifdef HAVE_CONFIG_H + #include +#endif + +#include + +#ifdef WOLFTPM_PIKEOS +#include + +/* The PikeOS TPM driver is reached through a file provider that takes a + * complete TPM command per vm_write and returns the complete response per + * vm_read. The task must have called vm_init() before the first command. */ +#ifndef TPM2_PIKEOS_DEV + #define TPM2_PIKEOS_DEV "tpm0:dev0" +#endif + +int TPM2_PIKEOS_SendCommand(TPM2_CTX* ctx, TPM2_Packet* packet) +{ + P4_e_t e; + P4_size_t written = 0; + P4_size_t readSz = 0; + + if (ctx == NULL || packet == NULL) { + return BAD_FUNC_ARG; + } + + if (!ctx->pikeosCtx.isOpen) { + e = vm_open(TPM2_PIKEOS_DEV, VM_O_RD_WR, &ctx->pikeosCtx.fd); + if (e != P4_E_OK) { + #ifdef DEBUG_WOLFTPM + printf("Failed to open %s: %d\n", TPM2_PIKEOS_DEV, (int)e); + #endif + return TPM_RC_FAILURE; + } + ctx->pikeosCtx.isOpen = 1; + } + +#ifdef WOLFTPM_DEBUG_VERBOSE + printf("Command size: %d\n", packet->pos); + TPM2_PrintBin(packet->buf, packet->pos); +#endif + + e = vm_write(&ctx->pikeosCtx.fd, packet->buf, (P4_size_t)packet->pos, + &written); + if (e != P4_E_OK || written != (P4_size_t)packet->pos) { + #ifdef DEBUG_WOLFTPM + printf("Failed write to %s: %d\n", TPM2_PIKEOS_DEV, (int)e); + #endif + return TPM_RC_FAILURE; + } + + e = vm_read(&ctx->pikeosCtx.fd, packet->buf, (P4_size_t)packet->size, + &readSz); + if (e != P4_E_OK || readSz < TPM2_HEADER_SIZE || + readSz > (P4_size_t)packet->size) { + #ifdef DEBUG_WOLFTPM + printf("Failed read from %s: %d (size %d)\n", TPM2_PIKEOS_DEV, + (int)e, (int)readSz); + #endif + return TPM_RC_FAILURE; + } + /* bound the response parser to the bytes actually received */ + packet->size = (int)readSz; + +#ifdef WOLFTPM_DEBUG_VERBOSE + printf("Response size: %d\n", (int)readSz); + TPM2_PrintBin(packet->buf, (int)readSz); +#endif + + return TPM_RC_SUCCESS; +} + +int TPM2_PIKEOS_Cleanup(TPM2_CTX* ctx) +{ + int rc = TPM_RC_SUCCESS; + + if (ctx != NULL && ctx->pikeosCtx.isOpen) { + if (vm_close(&ctx->pikeosCtx.fd) != P4_E_OK) { + rc = TPM_RC_FAILURE; + } + ctx->pikeosCtx.isOpen = 0; + } + + return rc; +} + +#endif /* WOLFTPM_PIKEOS */ diff --git a/src/tpm2_spdm.c b/src/tpm2_spdm.c index b9c5e532..616b19f0 100644 --- a/src/tpm2_spdm.c +++ b/src/tpm2_spdm.c @@ -44,7 +44,7 @@ /* TIS functions for SPI/I2C TPM transport */ #if defined(WOLFTPM_SPDM_TCG) && \ !defined(WOLFTPM_LINUX_DEV) && !defined(WOLFTPM_SWTPM) && \ - !defined(WOLFTPM_WINAPI) + !defined(WOLFTPM_WINAPI) && !defined(WOLFTPM_PIKEOS) #include #include #define WOLFTPM_SPDM_TIS_IO diff --git a/src/tpm2_wrap.c b/src/tpm2_wrap.c index 097fc830..94b950cb 100644 --- a/src/tpm2_wrap.c +++ b/src/tpm2_wrap.c @@ -129,9 +129,9 @@ static int wolfTPM2_Init_ex(TPM2_CTX* ctx, TPM2HalIoCb ioCb, void* userCtx, /* Fall through to Startup/SelfTest below */ #elif defined(WOLFTPM_LINUX_DEV) || defined(WOLFTPM_SWTPM) || \ - defined(WOLFTPM_WINAPI) + defined(WOLFTPM_WINAPI) || defined(WOLFTPM_PIKEOS) rc = TPM2_Init_minimal(ctx); - /* Using standard file I/O for the Linux TPM device */ + /* The OS TPM driver does the I/O, so no TIS callback */ (void)ioCb; (void)userCtx; (void)timeoutTries; @@ -7346,8 +7346,10 @@ int wolfTPM2_ResetPCR(WOLFTPM2_DEV* dev, int pcrIndex) int wolfTPM2_SetLocality(WOLFTPM2_DEV* dev, int locality) { -#if defined(WOLFTPM_LINUX_DEV) || defined(WOLFTPM_WINAPI) - /* The Linux kernel driver and Windows TBS own the TPM locality */ +#if defined(WOLFTPM_LINUX_DEV) || defined(WOLFTPM_WINAPI) || \ + defined(WOLFTPM_PIKEOS) + /* The Linux kernel driver, Windows TBS and PikeOS driver own the TPM + * locality */ (void)dev; (void)locality; return NOT_COMPILED_IN; @@ -12447,7 +12449,7 @@ static int tpm2_ifx_firmware_start(WOLFTPM2_DEV* dev, TPM_ALG_ID hashAlg, * since TPM reset into firmware upgrade mode */ #if !defined(WOLFTPM_LINUX_DEV) && !defined(WOLFTPM_SWTPM) && \ - !defined(WOLFTPM_WINAPI) + !defined(WOLFTPM_WINAPI) && !defined(WOLFTPM_PIKEOS) /* Do chip startup and request locality again */ #ifdef WOLFTPM_LINUX_DEV_AUTODETECT if (dev->ctx.fd < 0) /* Only needed for SPI path */ @@ -12563,7 +12565,7 @@ static int tpm2_ifx_firmware_data(WOLFTPM2_DEV* dev, XSLEEP_MS(300); #if !defined(WOLFTPM_LINUX_DEV) && !defined(WOLFTPM_SWTPM) && \ - !defined(WOLFTPM_WINAPI) + !defined(WOLFTPM_WINAPI) && !defined(WOLFTPM_PIKEOS) /* Do chip startup and request locality again */ #ifdef WOLFTPM_LINUX_DEV_AUTODETECT if (dev->ctx.fd < 0) /* Only needed for SPI path */ @@ -13093,7 +13095,7 @@ static int tpm2_st33_firmware_start_common(WOLFTPM2_DEV* dev, XSLEEP_MS(300); #if !defined(WOLFTPM_LINUX_DEV) && !defined(WOLFTPM_SWTPM) && \ - !defined(WOLFTPM_WINAPI) + !defined(WOLFTPM_WINAPI) && !defined(WOLFTPM_PIKEOS) /* Do chip startup and request locality again */ #ifdef WOLFTPM_LINUX_DEV_AUTODETECT if (dev->ctx.fd < 0) /* Only needed for SPI path */ @@ -13218,7 +13220,7 @@ static int tpm2_st33_firmware_data(WOLFTPM2_DEV* dev, TPM_CC ccData, XSLEEP_MS(300); #if !defined(WOLFTPM_LINUX_DEV) && !defined(WOLFTPM_SWTPM) && \ - !defined(WOLFTPM_WINAPI) + !defined(WOLFTPM_WINAPI) && !defined(WOLFTPM_PIKEOS) /* Do chip startup and request locality again */ #ifdef WOLFTPM_LINUX_DEV_AUTODETECT if (dev->ctx.fd < 0) /* Only needed for SPI path */ diff --git a/tests/include.am b/tests/include.am index ce3aa5df..9e6034bc 100644 --- a/tests/include.am +++ b/tests/include.am @@ -56,6 +56,20 @@ tests_swtpm_uart_unit_test_CFLAGS = \ tests_swtpm_uart_unit_test_LDADD = $(LIB_STATIC_ADD) endif +# Host test of the PikeOS transport against the stubs in tests/pikeos_stub. +# Not built by make, as a PikeOS target has the real vm_* calls. On a host the +# library needs the stub headers too: CFLAGS=-Itests/pikeos_stub. +if BUILD_PIKEOS +check_PROGRAMS += tests/pikeos_unit.test +tests_pikeos_unit_test_SOURCES = tests/pikeos_unit_tests.c +tests_pikeos_unit_test_CFLAGS = $(AM_CFLAGS) +tests_pikeos_unit_test_CPPFLAGS = -I$(srcdir)/tests/pikeos_stub $(AM_CPPFLAGS) +tests_pikeos_unit_test_LDADD = $(src_libwolftpm_la_OBJECTS) \ + $(src_libwolftpm_la_LIBADD) $(LIB_STATIC_ADD) +tests_pikeos_unit_test_DEPENDENCIES = src/libwolftpm.la +endif +EXTRA_DIST += tests/pikeos_stub/p4.h tests/pikeos_stub/vm.h + if BUILD_FWTPM_TIS check_PROGRAMS += tests/fwtpm_hal_unit.test noinst_PROGRAMS += tests/fwtpm_hal_unit.test diff --git a/tests/pikeos_stub/p4.h b/tests/pikeos_stub/p4.h new file mode 100644 index 00000000..5ca55cb1 --- /dev/null +++ b/tests/pikeos_stub/p4.h @@ -0,0 +1,12 @@ +/* Minimal stand-in for the PikeOS SDK p4.h, with the SDK's signatures. It lets + * the transport build and run on a host; it is not used on a PikeOS target. */ +#ifndef PIKEOS_STUB_P4_H +#define PIKEOS_STUB_P4_H +typedef int P4_e_t; +typedef unsigned long P4_size_t; +typedef unsigned long long P4_time_t; +#define P4_E_OK 0 +#define P4_MSEC(to) ((to) * 1000000ULL) +P4_e_t p4_sleep(P4_time_t timeout); +P4_time_t p4_get_time(void); +#endif diff --git a/tests/pikeos_stub/vm.h b/tests/pikeos_stub/vm.h new file mode 100644 index 00000000..863d4c44 --- /dev/null +++ b/tests/pikeos_stub/vm.h @@ -0,0 +1,14 @@ +/* Minimal stand-in for the PikeOS SDK vm.h, with the SDK's signatures. It lets + * the transport build and run on a host; it is not used on a PikeOS target. */ +#ifndef PIKEOS_STUB_VM_H +#define PIKEOS_STUB_VM_H +#include +typedef struct { unsigned int gdh; } vm_file_desc_t; +#define VM_O_RD_WR 3 +P4_e_t vm_open(const char *name, unsigned int oflags, vm_file_desc_t *fd); +P4_e_t vm_read(vm_file_desc_t *fd, void *buff, P4_size_t buff_size, + P4_size_t *read_size); +P4_e_t vm_write(vm_file_desc_t *fd, const void *buff, P4_size_t buff_size, + P4_size_t *written_size); +P4_e_t vm_close(vm_file_desc_t *fd); +#endif diff --git a/tests/pikeos_unit_tests.c b/tests/pikeos_unit_tests.c new file mode 100644 index 00000000..ee97bd36 --- /dev/null +++ b/tests/pikeos_unit_tests.c @@ -0,0 +1,307 @@ +/* pikeos_unit_tests.c + * + * Copyright (C) 2006-2026 wolfSSL Inc. + * + * This file is part of wolfTPM. + * + * wolfTPM is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * wolfTPM is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335, USA + */ + +/* Runs the PikeOS transport on a host against stubbed vm_* calls, through the + * public TPM2_Init_ex/TPM2_Startup/TPM2_Cleanup API so the build's transport + * dispatch is covered as well. */ + +#ifdef HAVE_CONFIG_H + #include +#endif + +#include +#include +#include + +#include +#include +#include + +#define TEST_ERR 7 + +static const byte startupCmd[] = { + 0x80, 0x01, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x01, 0x44, 0x00, 0x00 +}; +static const byte rspOk[] = { + 0x80, 0x01, 0x00, 0x00, 0x00, 0x0A, 0x00, 0x00, 0x00, 0x00 +}; +static const byte rspRetry[] = { + 0x80, 0x01, 0x00, 0x00, 0x00, 0x0A, 0x00, 0x00, 0x09, 0x22 +}; + +static struct { + int openRc, writeRc, readRc, closeRc; + int opens, writes, reads, closes, sleeps; + int retriesLeft; + int shortWrite; + P4_size_t readSzOverride; + int oversizeRead; + byte lastCmd[64]; + P4_size_t lastCmdSz; + unsigned int openFlags; + char openName[32]; +} stub; + +P4_e_t p4_sleep(P4_time_t timeout) +{ + (void)timeout; + stub.sleeps++; + return P4_E_OK; +} + +P4_time_t p4_get_time(void) +{ + return 0; +} + +P4_e_t vm_open(const char *name, unsigned int oflags, vm_file_desc_t *fd) +{ + stub.opens++; + stub.openFlags = oflags; + strncpy(stub.openName, name, sizeof(stub.openName) - 1); + fd->gdh = 1; + return stub.openRc; +} + +P4_e_t vm_write(vm_file_desc_t *fd, const void *buff, P4_size_t buff_size, + P4_size_t *written_size) +{ + (void)fd; + stub.writes++; + stub.lastCmdSz = buff_size; + if (buff_size <= sizeof(stub.lastCmd)) { + memcpy(stub.lastCmd, buff, buff_size); + } + *written_size = stub.shortWrite ? buff_size - 1 : buff_size; + return stub.writeRc; +} + +P4_e_t vm_read(vm_file_desc_t *fd, void *buff, P4_size_t buff_size, + P4_size_t *read_size) +{ + const byte* rsp = rspOk; + P4_size_t sz = sizeof(rspOk); + + (void)fd; + stub.reads++; + if (stub.retriesLeft > 0) { + stub.retriesLeft--; + rsp = rspRetry; + sz = sizeof(rspRetry); + } + if (stub.readSzOverride != 0 && stub.readSzOverride < sz) { + sz = stub.readSzOverride; + } + memcpy(buff, rsp, sz); + *read_size = stub.oversizeRead ? buff_size + 1 : sz; + return stub.readRc; +} + +P4_e_t vm_close(vm_file_desc_t *fd) +{ + (void)fd; + stub.closes++; + return stub.closeRc; +} + +static int gFailures; + +#define CHECK(cond) do { \ + if (!(cond)) { \ + printf("FAIL %s:%d: %s\n", __FILE__, __LINE__, #cond); \ + gFailures++; \ + } \ +} while (0) + +static TPM_RC sendStartup(void) +{ + Startup_In in; + + XMEMSET(&in, 0, sizeof(in)); + in.startupType = TPM_SU_CLEAR; + return TPM2_Startup(&in); +} + +static void testInit(TPM2_CTX* ctx) +{ + XMEMSET(&stub, 0, sizeof(stub)); + CHECK(TPM2_Init_ex(ctx, NULL, NULL, 0) == TPM_RC_SUCCESS); +} + +/* A command reaches the PikeOS file provider and is framed unchanged. The + * descriptor stays open across commands and is closed exactly once. */ +static void test_send_and_cleanup(void) +{ + TPM2_CTX ctx; + + testInit(&ctx); + CHECK(sendStartup() == TPM_RC_SUCCESS); + CHECK(stub.opens == 1); + CHECK(stub.openFlags == VM_O_RD_WR); + CHECK(strcmp(stub.openName, "tpm0:dev0") == 0); + CHECK(stub.writes == 1); + CHECK(stub.reads == 1); + CHECK(stub.lastCmdSz == sizeof(startupCmd)); + CHECK(memcmp(stub.lastCmd, startupCmd, sizeof(startupCmd)) == 0); + + CHECK(sendStartup() == TPM_RC_SUCCESS); + CHECK(stub.opens == 1); + CHECK(stub.writes == 2); + + CHECK(TPM2_Cleanup(&ctx) == TPM_RC_SUCCESS); + CHECK(stub.closes == 1); + CHECK(TPM2_Cleanup(&ctx) == TPM_RC_SUCCESS); + CHECK(stub.closes == 1); + printf("Test PikeOS: send and cleanup done\n"); +} + +/* A failed close still marks the descriptor closed */ +static void test_close_failure(void) +{ + TPM2_CTX ctx; + + testInit(&ctx); + CHECK(sendStartup() == TPM_RC_SUCCESS); + stub.closeRc = TEST_ERR; + (void)TPM2_Cleanup(&ctx); + CHECK(stub.closes == 1); + (void)TPM2_Cleanup(&ctx); + CHECK(stub.closes == 1); + printf("Test PikeOS: close failure done\n"); +} + +/* There is no TIS layer, so no HAL callback and no chip startup wait */ +static void test_init_args(void) +{ + TPM2_CTX ctx; + int user = 0; + + XMEMSET(&stub, 0, sizeof(stub)); + CHECK(TPM2_Init_ex(&ctx, NULL, NULL, 1) == BAD_FUNC_ARG); + CHECK(TPM2_Init_ex(&ctx, NULL, &user, 0) == BAD_FUNC_ARG); + CHECK(stub.opens == 0); + CHECK(TPM2_Init_minimal(&ctx) == TPM_RC_SUCCESS); + CHECK(stub.opens == 0); + (void)TPM2_Cleanup(&ctx); + printf("Test PikeOS: init arguments done\n"); +} + +/* An open failure sends nothing, and the next command tries the open again */ +static void test_open_failure(void) +{ + TPM2_CTX ctx; + + testInit(&ctx); + stub.openRc = TEST_ERR; + CHECK(sendStartup() == TPM_RC_FAILURE); + CHECK(stub.writes == 0); + + stub.openRc = P4_E_OK; + CHECK(sendStartup() == TPM_RC_SUCCESS); + CHECK(stub.opens == 2); + CHECK(stub.writes == 1); + (void)TPM2_Cleanup(&ctx); + CHECK(stub.closes == 1); + printf("Test PikeOS: open failure done\n"); +} + +static void test_write_failures(void) +{ + TPM2_CTX ctx; + + testInit(&ctx); + stub.writeRc = TEST_ERR; + CHECK(sendStartup() == TPM_RC_FAILURE); + CHECK(stub.reads == 0); + stub.writeRc = P4_E_OK; + + stub.shortWrite = 1; + CHECK(sendStartup() == TPM_RC_FAILURE); + CHECK(stub.reads == 0); + (void)TPM2_Cleanup(&ctx); + printf("Test PikeOS: write failures done\n"); +} + +/* A read error and a response length outside the header size or the command + * buffer all fail before the response is parsed */ +static void test_read_failures(void) +{ + TPM2_CTX ctx; + + testInit(&ctx); + stub.readRc = TEST_ERR; + CHECK(sendStartup() == TPM_RC_FAILURE); + stub.readRc = P4_E_OK; + + stub.readSzOverride = TPM2_HEADER_SIZE - 1; + CHECK(sendStartup() == TPM_RC_FAILURE); + stub.readSzOverride = 0; + + stub.oversizeRead = 1; + CHECK(sendStartup() == TPM_RC_FAILURE); + stub.oversizeRead = 0; + + CHECK(sendStartup() == TPM_RC_SUCCESS); + (void)TPM2_Cleanup(&ctx); + printf("Test PikeOS: read failures done\n"); +} + +#ifndef WOLFTPM_NO_RETRY +/* With retries enabled, TPM_RC_RETRY resubmits the same command through the + * transport. It is returned to the caller otherwise, the default. */ +static void test_retry(void) +{ + TPM2_CTX ctx; + + testInit(&ctx); + stub.retriesLeft = 1; + CHECK(sendStartup() == TPM_RC_RETRY); + CHECK(stub.writes == 1); + + testInit(&ctx); + CHECK(TPM2_SetCommandRetries(&ctx, 2) == TPM_RC_SUCCESS); + stub.retriesLeft = 2; + CHECK(sendStartup() == TPM_RC_SUCCESS); + CHECK(stub.writes == 3); + CHECK(stub.reads == 3); + CHECK(stub.opens == 1); + CHECK(stub.lastCmdSz == sizeof(startupCmd)); + CHECK(memcmp(stub.lastCmd, startupCmd, sizeof(startupCmd)) == 0); + (void)TPM2_Cleanup(&ctx); + printf("Test PikeOS: retry done\n"); +} +#endif /* !WOLFTPM_NO_RETRY */ + +int main(void) +{ + test_send_and_cleanup(); + test_close_failure(); + test_init_args(); + test_open_failure(); + test_write_failures(); + test_read_failures(); +#ifndef WOLFTPM_NO_RETRY + test_retry(); +#endif + + printf("PikeOS transport tests: %s\n", gFailures == 0 ? "passed" : "failed"); + return gFailures == 0 ? 0 : 1; +} diff --git a/wolftpm/include.am b/wolftpm/include.am index 713eea2c..47a9b0fe 100644 --- a/wolftpm/include.am +++ b/wolftpm/include.am @@ -9,6 +9,7 @@ nobase_include_HEADERS+= \ wolftpm/tpm2_types.h \ wolftpm/tpm2_wrap.h \ wolftpm/tpm2_linux.h \ + wolftpm/tpm2_pikeos.h \ wolftpm/tpm2_swtpm.h \ wolftpm/tpm2_winapi.h \ wolftpm/tpm2_param_enc.h \ diff --git a/wolftpm/tpm2.h b/wolftpm/tpm2.h index 3863f88c..14e461dc 100644 --- a/wolftpm/tpm2.h +++ b/wolftpm/tpm2.h @@ -2243,6 +2243,16 @@ struct wolfTPM_tcpContext { }; #endif /* WOLFTPM_SWTPM */ +#ifdef WOLFTPM_PIKEOS +#include +#include + +struct wolfTPM_pikeosContext { + vm_file_desc_t fd; + int isOpen; +}; +#endif /* WOLFTPM_PIKEOS */ + #ifdef WOLFTPM_WINAPI #include #include @@ -2327,6 +2337,9 @@ typedef struct TPM2_CTX { #ifdef WOLFTPM_WINAPI struct wolfTPM_winContext winCtx; #endif +#ifdef WOLFTPM_PIKEOS + struct wolfTPM_pikeosContext pikeosCtx; +#endif #ifndef WOLFTPM2_NO_WOLFCRYPT #ifdef WOLFTPM2_USE_WOLF_RNG WC_RNG rng; @@ -3855,7 +3868,7 @@ WOLFTPM_API TPM_RC TPM2_Init(TPM2_CTX* ctx, TPM2HalIoCb ioCb, void* userCtx); \param ctx pointer to a TPM2_CTX struct \param ioCb pointer to TPM2HalIoCb (HAL IO) callback function \param userCtx pointer to the user's context that will be stored as a member of the ctx struct - \param timeoutTries specifies the number of attempts to confirm that TPM2 startup has completed + \param timeoutTries specifies the number of attempts to confirm that TPM2 startup has completed. Must be 0 with WOLFTPM_PIKEOS, which has no TIS layer; use TPM2_Init or TPM2_Init_minimal \sa TPM2_GetRCString \sa TPM2_Init_minimal diff --git a/wolftpm/tpm2_pikeos.h b/wolftpm/tpm2_pikeos.h new file mode 100644 index 00000000..a215d9b8 --- /dev/null +++ b/wolftpm/tpm2_pikeos.h @@ -0,0 +1,46 @@ +/* tpm2_pikeos.h + * + * Copyright (C) 2006-2026 wolfSSL Inc. + * + * This file is part of wolfTPM. + * + * wolfTPM is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * wolfTPM is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335, USA + */ + +#ifndef _TPM2_PIKEOS_H_ +#define _TPM2_PIKEOS_H_ + +#ifdef WOLFTPM_PIKEOS + +#include +#include + +#ifdef __cplusplus + extern "C" { +#endif + +/* TPM2 IO for using TPM through a PikeOS file provider (vm_open/vm_read) */ +WOLFTPM_LOCAL int TPM2_PIKEOS_SendCommand(TPM2_CTX* ctx, TPM2_Packet* packet); + +/* Close the PikeOS TPM device */ +WOLFTPM_LOCAL int TPM2_PIKEOS_Cleanup(TPM2_CTX* ctx); + +#ifdef __cplusplus + } /* extern "C" */ +#endif + +#endif /* WOLFTPM_PIKEOS */ + +#endif /* _TPM2_PIKEOS_H_ */ diff --git a/wolftpm/tpm2_types.h b/wolftpm/tpm2_types.h index b46c536f..0a92147f 100644 --- a/wolftpm/tpm2_types.h +++ b/wolftpm/tpm2_types.h @@ -544,7 +544,8 @@ typedef int64_t INT64; /* On Linux with autodetect, also try /dev/tpm0 kernel driver at runtime */ #if defined(WOLFTPM_AUTODETECT) && defined(__linux__) && \ !defined(WOLFTPM_LINUX_DEV) && !defined(WOLFTPM_SWTPM) && \ - !defined(WOLFTPM_WINAPI) && !defined(WOLFTPM_FWTPM_HAL) + !defined(WOLFTPM_WINAPI) && !defined(WOLFTPM_FWTPM_HAL) && \ + !defined(WOLFTPM_PIKEOS) #define WOLFTPM_LINUX_DEV_AUTODETECT #endif @@ -635,7 +636,8 @@ typedef int64_t INT64; #endif #ifndef TPM_TIMEOUT_TRIES - #if defined(WOLFTPM_LINUX_DEV) || defined(WOLFTPM_SWTPM) || defined(WOLFTPM_WINAPI) + #if defined(WOLFTPM_LINUX_DEV) || defined(WOLFTPM_SWTPM) || \ + defined(WOLFTPM_WINAPI) || defined(WOLFTPM_PIKEOS) #define TPM_TIMEOUT_TRIES 0 #else #define TPM_TIMEOUT_TRIES 1000000 @@ -728,6 +730,9 @@ typedef int64_t INT64; #elif defined(WOLFSSL_ESPIDF) #include #define XSLEEP_MS(ms) esp_rom_delay_us((word32)(ms) * 1000U) + #elif defined(WOLFTPM_PIKEOS) + #include + #define XSLEEP_MS(ms) p4_sleep(P4_MSEC(ms)) #elif defined(__linux__) || defined(__ARM_EABI__) #include #define XSLEEP_MS(ms) do { \ @@ -782,6 +787,11 @@ typedef int64_t INT64; #include #define XTPM_GET_TIMEMS() ((word32)GetTickCount64()) #define WOLFTPM_HAVE_MONOTONIC_MS + #elif defined(WOLFTPM_PIKEOS) + #include + /* p4_get_time() is nanoseconds since boot */ + #define XTPM_GET_TIMEMS() ((word32)(p4_get_time() / 1000000ULL)) + #define WOLFTPM_HAVE_MONOTONIC_MS #else /* Include first, then feature-test: strict C99 may not expose * CLOCK_MONOTONIC, and __linux__ alone would fail to compile. */