diff --git a/Makefile b/Makefile index e111be20a..99e91890b 100644 --- a/Makefile +++ b/Makefile @@ -1232,6 +1232,7 @@ $(eval $(call assert_booleans,\ HARDEN_SLS \ HW_ASSISTED_COHERENCY \ MEASURED_BOOT \ + DISCRETE_TPM \ DICE_PROTECTION_ENVIRONMENT \ RMMD_ENABLE_EL3_TOKEN_SIGN \ DRTM_SUPPORT \ @@ -1418,6 +1419,7 @@ $(eval $(call add_defines,\ HW_ASSISTED_COHERENCY \ LOG_LEVEL \ MEASURED_BOOT \ + DISCRETE_TPM \ DICE_PROTECTION_ENVIRONMENT \ DRTM_SUPPORT \ NS_TIMER_SWITCH \ diff --git a/changelog.yaml b/changelog.yaml index 422b9dafe..3f0636941 100644 --- a/changelog.yaml +++ b/changelog.yaml @@ -962,6 +962,9 @@ subsections: - drivers/scmi-msg - scmi-msg + - title: TPM + scope: tpm + - title: UFS scope: ufs diff --git a/docs/design_documents/dtpm_drivers.rst b/docs/design_documents/dtpm_drivers.rst new file mode 100644 index 000000000..324af03cf --- /dev/null +++ b/docs/design_documents/dtpm_drivers.rst @@ -0,0 +1,119 @@ +Discrete TPM drivers +==================== + +This section focuses on the design and functionality of Discrete TPM drivers +in |TF-A|. The |TPM| technology is designed to provide +a dedicated, hardware-based solution for storing cryptographic keys and +performing security-related operations. + +Discrete TPMs are separate, standalone hardware components that are physically +isolated from the system's main processor. This isolation helps protect +sensitive information, such as encryption keys and platform credentials, from +being accessed or tampered with by malicious software or unauthorized users. +When a Discrete TPM interface is implemented correctly, the risk of software +based attacks is reduced, further reducing the attack surface. + +TPM measurements establish the security posture of a system and are used for +attestation. Performing measurements using a TPM in TF-A is beneficial from +a security standpoint because it ensures hardware-backed attestation earlier +in the boot flow, reducing the risk of a compromised root of trust. + +The design implemented in TF-A supports multiple types of TPM hardware interfaces +and hardware bus types in order to be compatible with different platforms. +Platforms opt to use a specific messaging interface, such as |CRB| or |FIFO|, +and a specific hardware bus interface, such as |I2C| or |SPI|. + +Driver architecture +------------------- + +The Discrete TPM drivers are split up into four layers, each serving a distinct +purpose in the overall architecture: + + - **Command Layer**: This layer provides various TPM commands based on the + `TCG TPM 2.0 Library Specification`_. It allows a system to initialize the + TPM interface, perform a TPM startup, set up a locality for operations like + PCR extend and read, and release the locality when finished. + - **Interface Layer**: This layer handles sending and receiving TPM commands + via a specific TPM interface like FIFO or CRB. It also includes functions + such as getting information, requesting access, and relinquishing access, + tailored to the specific interface. + - **Link Layer**: Discrete TPMs may appear as a SPI, I2C, or memory mapped + device. The link layer maps the command passed from the interface layer to + the appropriate bus type. It includes hardware link read and write functions + that use the platform bus interface to transfer commands. + - **Platform Layer**: The platform layer implements the details of how to + communicate to the TPM chip for a specific platform. The link layer uses the + platform layer to read and write to the TPM. + + .. note:: + The command, interface, and link layers are implemented in common code in + TF-A. The platform layer is implemented in platform specific code. + +The following diagram illustrates the Discrete TPM driver stack for the Raspberry +Pi 3 platform. + +|rpi3 dtpm driver stack| + +Header files +^^^^^^^^^^^^ +- TPM Drivers: ``include/drivers/tpm`` + + +Source files +^^^^^^^^^^^^ +- TPM Drivers: ``drivers/tpm`` + + +Build time config options +------------------------- + +- ``MBOOT_TPM_HASH_ALG``: The hash algorithm to be used by the TPM, currently + the only supported algorithm is ``sha256``. As additional Discrete TPMs are + tested and integrated in TF-A, support for more algorithms will become + available. +- ``DISCRETE_TPM``: Boolean flag to enable Discrete TPM support. Depending + on the selected TPM interface, the appropriate drivers will be built and + packaged into firmware. +- ``TPM_INTERFACE``: This flag is required when ``DISCRETE_TPM=1``, + currently the only supported interface is ``FIFO_SPI``. + Ideally there should be four options: + + .. code:: shell + + FIFO_I2C + FIFO_SPI + FIFO_MMIO + CRB + + .. note:: + ``MBOOT_TPM_HASH_ALG`` will automatically overwrite ``MBOOT_EL_HASH_ALG``. + This is to ensure the event log and the TPM are using the same hash + algorithm. + + +Discrete TPM Initialization +--------------------------- +The TPM needs to be initialized based on the platform, the hardware interfaces +need to be set up independently, and once they are setup, the TPM commands +``tpm_interface_init()`` and subsequently ``tpm_startup()`` can be called. +``tpm_startup()`` only needs to be called once after startup, or if the system +is reset. + +An example of platform specific TPM hardware initialization for the rpi3 can be +found in ``plat/rpi/rpi3/rpi3_bl1_setup.c`` and ``plat/rpi/rpi3/rpi3_bl1_mboot.c`` + + +Discrete TPM PCR Extend +----------------------- +Once the TPM is setup, the TPM ``pcr_extend`` operation can be used to extend +hashes and store them in PCR 0. + +An example of ``pcr_extend`` that is used during rpi3 measured boot can be found + in ``plat/rpi/rpi3/rpi3_bl1_mboot.c`` and ``plat/rpi/rpi3/rpi3_bl2_mboot.c``. + + +*Copyright (c) 2025, Arm Limited. All rights reserved.* + +.. |rpi3 dtpm driver stack| image:: + ../resources/diagrams/rpi3_dtpm_driver.png +.. _TCG TPM 2.0 Library Specification: https://trustedcomputinggroup.org/resource/tpm-library-specification/ diff --git a/docs/design_documents/index.rst b/docs/design_documents/index.rst index f1d83861f..11c1c5a57 100644 --- a/docs/design_documents/index.rst +++ b/docs/design_documents/index.rst @@ -7,10 +7,12 @@ Design Documents cmake_framework measured_boot_poc + measured_boot_dtpm_poc drtm_poc rse psci_osi_mode measured_boot + dtpm_drivers -------------- diff --git a/docs/design_documents/measured_boot.rst b/docs/design_documents/measured_boot.rst index 005903eda..1f76770ab 100644 --- a/docs/design_documents/measured_boot.rst +++ b/docs/design_documents/measured_boot.rst @@ -91,6 +91,14 @@ The Measured Boot implementation in TF-A supports: and the variable length crypto agile structure called TCG_PCR_EVENT2. Event Log driver implemented in TF-A covers later part. +#. Discrete TPM + + A Discrete TPM (Trusted Platform Module) can be used alongside Event Log to + extend measurements and validate Measured Boot functionality. The use of a + Discrete TPM in TF-A to extend measurements of images and other critical data + allows for an additional layer of security. The TPM can be used to attest the + integrity of the Event Log. + #. |RSE| It is one of the physical backends to extend the measurements. Please refer @@ -229,7 +237,7 @@ Responsibilities of these platform interfaces are - -------------- -*Copyright (c) 2023, Arm Limited. All rights reserved.* +*Copyright (c) 2023-2025, Arm Limited. All rights reserved.* .. _Arm® Server Base Security Guide: https://developer.arm.com/documentation/den0086/latest .. _TCG EFI Protocol Specification: https://trustedcomputinggroup.org/wp-content/uploads/EFI-Protocol-Specification-rev13-160330final.pdf diff --git a/docs/design_documents/measured_boot_dtpm_poc.rst b/docs/design_documents/measured_boot_dtpm_poc.rst new file mode 100644 index 000000000..63a12f263 --- /dev/null +++ b/docs/design_documents/measured_boot_dtpm_poc.rst @@ -0,0 +1,458 @@ +Measured Boot using a Discrete TPM (PoC) +======================================== + +Measured Boot is the process of cryptographically measuring the code and +critical data used at boot time, for example using a TPM, so that the +security state can be attested later. + +The current implementation of the driver included in |TF-A| supports several +backends and each has a different means to store the measurements. +This section focuses on the Discrete TPM backend, which stores measurements +in a PCR within the TPM. This backend can be paired with the `TCG event log`_ +to provide attestation of the measurements stored in the event log. See +details in :ref:`Measured Boot Design`. + +This section provides instructions to setup and build a proof of concept (PoC) +that showcases the use of Measured Boot with a Discrete TPM interface. + +.. note:: + The instructions given in this document are meant to build a PoC to + show how Measured Boot on TF-A can interact with a Discrete TPM interface. + This PoC is platform specific, and uses a SPI based Discrete TPM, the + Raspberry Pi communicates with the TPM via a GPIO pin bit-banged SPI interface. + For other platforms, different may be required to interface with the hardware + (e.g., different hardware communication protocols) and different TPM interfaces + (e.g., |FIFO| vs |CRB|). + +Components +~~~~~~~~~~ + + - **Platform**: The PoC is developed on the Raspberry Pi 3 (rpi3), due to quick + driver development and the availability of GPIO pins to interface with a TPM + expansion module. Measured boot capabilities using the TCG Event Log are + ported to the Raspberry Pi 3 platform inside TF-A. This PoC specifically uses + the Raspberry Pi 3 Model B V1.2, but this PoC is compatible with other + Raspberry Pi 3 models. + + - **Discrete TPM**: The TPM chip selected is a breakout board compatible with + the Raspberry Pi 3 GPIO pins. This PoC uses a |SPI| based LetsTrust TPM + breakout board equipped with a Infineon Optiga™ SLB 9670 TPM 2.0 chip. Link + to device: https://thepihut.com/products/letstrust-tpm-for-raspberry-pi + + .. note:: + If you have another TPM breakout board that uses the same + Infineon Optiga™ SLB 9670 TPM 2.0 SPI based chip, it will also work. + Ensure that the correct GPIO pins are utilized on the Raspberry Pi 3 to + avoid communication issues, and possible hardware failures. + + - **TF-A TPM Drivers**: To interface with a physical (Discrete) TPM chip in + TF-A, the PoC uses TF-A drivers that provide the command, interface, link, + and platform layers required to send and receive data to and from the TPM. + The drivers are located in TFA, and not in a |SP|, so that they may be used + in early stages such as BL2, and in some cases, BL1. The design of the TPM + Drivers is documented here: :ref:`Discrete TPM drivers`. + + - **U-boot BL33**: This PoC showcases measured boot up to BL33, and for + simplicity uses a U-boot image for BL33, so that the image is measured and + loaded. Currently U-boot does not have Discrete TPM support for the + Raspberry Pi 3 platform so the boot flow ends here. + + +Building the PoC for the Raspberry Pi 3 +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +**Build instructions for U-Boot.bin for Raspberry Pi 3.** + +First, the build requires a BL33 firmware image that can be packaged and measured +by TF-A. + +U-boot can be built for the Raspberry Pi 3, but there are some changes to be made +to allow the build to succeed. First Clone U-boot and enter the repo. + +.. code:: shell + + git clone https://github.com/u-boot/u-boot.git + cd u-boot + +Now to switch to a specific tag ``v2024.04`` for testing purposes, and then build +the defconfig labelled ``rpi_3_b_plus_defconfig``. + +.. code:: shell + + git checkout tags/v2024.04 -b tfa_dtpm_poc + make CROSS_COMPILE=aarch64-linux-gnu- rpi_3_b_plus_defconfig + +Lastly open the ``.config`` and change ``CONFIG_TEXT_BASE`` and +``CONFIG_SYS_UBOOT_START`` to ``0x11000000`` to match the BL33 starting point. + +.. code:: shell + + vim .config + CONFIG_TEXT_BASE=0x11000000 + CONFIG_SYS_UBOOT_START=0x11000000 + +To build the u-boot binary, use the following command. + +.. code:: shell + + make CROSS_COMPILE=aarch64-linux-gnu- -j$(nproc) + +**Build TF-A for Raspberry Pi 3 with Discrete TPM and Measured Boot.** + +Copy over the ``u-boot.bin`` file over to your TF-A working directory. + +.. code:: shell + + cp /path/to/u-boot/build/u-boot.bin /path/to/tfa/u-boot.bin + +TF-A build command: + +.. code:: shell + + CROSS_COMPILE=aarch64-linux-gnu- \ + make PLAT=rpi3 \ + RPI3_PRELOADED_DTB_BASE=0x200000 \ + BL33=u-boot.bin \ + SUPPORT_VFP=1 \ + DEBUG=0 \ + MEASURED_BOOT=1 \ + DISCRETE_TPM=1 \ + MBOOT_TPM_HASH_ALG=sha256 \ + TPM_INTERFACE=FIFO_SPI \ + MBEDTLS_DIR=/path/to/mbedtls/repo \ + LOG_LEVEL=40 \ + fip all + +This build command is similar to the one provided in the TF-A Raspberry Pi 3 +platform port, To learn more about the platform and its build options, visit +:ref:`Raspberry Pi 3`. + + - ``RPI3_PRELOADED_DTB_BASE`` is given a different address to accommodate the + larger BL1 and BL2 firmware sizes, this is to accommodate the TPM drivers + that are packaged in BL1 and BL2 for this PoC. + - ``BL33`` is the non trusted firmware, in this case the U-Boot binary built + earlier. + - ``SUPPORT_VFP`` is enabled, allows Vector Floating Point operations in EL3. + - ``MEASURED_BOOT`` is enabled to allow the Measured Boot flow. + - ``DISCRETE_TPM=1`` enables the build of Discrete TPM drivers. + - ``MBOOT_TPM_HASH_ALG=sha256`` sets the hash algorithm to sha256, this is + the only algorithm supported by both TF-A Measured Boot and the SLB 9670 + TPM 2.0. + - ``TPM_INTERFACE=FIFO_SPI`` specifies the use of the FIFO SPI interface. + - ``MBEDTLS_DIR`` is the path to your local mbedtls repo. + - ``LOG_LEVEL=40`` ensures that eventlog is printed at the end of BL1 and BL2. + + +**Hardware Setup:** + + - **TPM Connection**: Connect the LetsTrust TPM board to GPIO pins 17 - 26 on + the 40-pin GPIO header on the Raspberry Pi board. The 2x5 header of the TPM + module must be aligned to the pins in a specific orientation, match the 3v3 + and RST pins from the TPM board to pins 17 and 18 respectively on the + Raspberry Pi 3 header. See `rpi3 pinout`_. + + - **Serial Console**: Establish a serial connection to the Raspberry Pi 3 to + view serial output during the boot sequence. The GND, TXD, and RXD pins, + which are labelled 6, 8, and 10 on the Raspberry Pi 3 header respectively, + are the required pins to establish a serial connection. The recommended way + to connect to the board from another system is to use a USB to serial TTL + cable to output the serial console in a easy manner. + + - **SD Card Setup**: Format a SD Card as ``FAT32`` with a default Raspbian + installation that is similar to the default Raspberry Pi 3 boot partition, + this partition will utilize the default files installed in the root + directory with Rasbian such as: + + :: + + bcm2710-rpi3-b.dtb + bootcode.bin + config.txt + fixup.dat + start.elf + + Open ``config.txt`` and overwrite the file with the following lines: + + :: + + arm_64bit=1 + disable_commandline_tags=2 + enable_uart=1 + armstub=armstub8.bin + device_tree_address=0x200000 + device_tree_end=0x210000 + + These configurations are required to enable uart, enable 64bit mode, + use the build TF binary, and the modified rpi3 device tree address + and size. + + Copy ``armstub8.bin`` from the TF-A build path to the root folder of the + SD card. + + The SD Card is now ready to be booted. + +Running the PoC for the Raspberry Pi 3 +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Insert the SD Card into the Raspberry Pi 3 SD card port and boot the system. + +To access the serial console output from the Raspberry Pi 3 you can either: + + - Follow `instructions`_ to use PuTTY to connect to Raspberry Pi 3 serial console. + + - Use the linux ``screen`` command: + + .. code:: shell + + screen /dev/ttyUSB0 115200 + +Once booted the output from the serial console will look like this: + +.. code:: shell + + Raspberry Pi Bootcode + + Read File: config.txt, 153 + + Read File: start.elf, 2975040 (bytes) + + Read File: fixup.dat, 7265 (bytes) + + MESS:00:00:01.170422:0: brfs: File read: /mfs/sd/config.txt + MESS:00:00:01.174630:0: brfs: File read: 153 bytes + MESS:00:00:01.211473:0: HDMI0:EDID error reading EDID block 0 attempt 0 + MESS:00:00:01.217639:0: HDMI0:EDID error reading EDID block 0 attempt 1 + MESS:00:00:01.223977:0: HDMI0:EDID error reading EDID block 0 attempt 2 + MESS:00:00:01.230313:0: HDMI0:EDID error reading EDID block 0 attempt 3 + MESS:00:00:01.236650:0: HDMI0:EDID error reading EDID block 0 attempt 4 + MESS:00:00:01.242987:0: HDMI0:EDID error reading EDID block 0 attempt 5 + MESS:00:00:01.249324:0: HDMI0:EDID error reading EDID block 0 attempt 6 + MESS:00:00:01.255660:0: HDMI0:EDID error reading EDID block 0 attempt 7 + MESS:00:00:01.261997:0: HDMI0:EDID error reading EDID block 0 attempt 8 + MESS:00:00:01.268334:0: HDMI0:EDID error reading EDID block 0 attempt 9 + MESS:00:00:01.274429:0: HDMI0:EDID giving up on reading EDID block 0 + MESS:00:00:01.282647:0: brfs: File read: /mfs/sd/config.txt + MESS:00:00:01.286929:0: gpioman: gpioman_get_pin_num: pin LEDS_PWR_OK not defined + MESS:00:00:01.487295:0: gpioman: gpioman_get_pin_num: pin DISPLAY_DSI_PORT not defined + MESS:00:00:01.494853:0: gpioman: gpioman_get_pin_num: pin LEDS_PWR_OK not defined + MESS:00:00:01.500763:0: *** Restart logging + MESS:00:00:01.504638:0: brfs: File read: 153 bytes + MESS:00:00:01.510139:0: hdmi: HDMI0:EDID error reading EDID block 0 attempt 0 + MESS:00:00:01.517254:0: hdmi: HDMI0:EDID error reading EDID block 0 attempt 1 + MESS:00:00:01.524112:0: hdmi: HDMI0:EDID error reading EDID block 0 attempt 2 + MESS:00:00:01.530970:0: hdmi: HDMI0:EDID error reading EDID block 0 attempt 3 + MESS:00:00:01.537826:0: hdmi: HDMI0:EDID error reading EDID block 0 attempt 4 + MESS:00:00:01.544685:0: hdmi: HDMI0:EDID error reading EDID block 0 attempt 5 + MESS:00:00:01.551543:0: hdmi: HDMI0:EDID error reading EDID block 0 attempt 6 + MESS:00:00:01.558399:0: hdmi: HDMI0:EDID error reading EDID block 0 attempt 7 + MESS:00:00:01.565258:0: hdmi: HDMI0:EDID error reading EDID block 0 attempt 8 + MESS:00:00:01.572116:0: hdmi: HDMI0:EDID error reading EDID block 0 attempt 9 + MESS:00:00:01.578730:0: hdmi: HDMI0:EDID giving up on reading EDID block 0 + MESS:00:00:01.584634:0: hdmi: HDMI0:EDID error reading EDID block 0 attempt 0 + MESS:00:00:01.592427:0: hdmi: HDMI0:EDID error reading EDID block 0 attempt 1 + MESS:00:00:01.599286:0: hdmi: HDMI0:EDID error reading EDID block 0 attempt 2 + MESS:00:00:01.606142:0: hdmi: HDMI0:EDID error reading EDID block 0 attempt 3 + MESS:00:00:01.613001:0: hdmi: HDMI0:EDID error reading EDID block 0 attempt 4 + MESS:00:00:01.619858:0: hdmi: HDMI0:EDID error reading EDID block 0 attempt 5 + MESS:00:00:01.626717:0: hdmi: HDMI0:EDID error reading EDID block 0 attempt 6 + MESS:00:00:01.633575:0: hdmi: HDMI0:EDID error reading EDID block 0 attempt 7 + MESS:00:00:01.640431:0: hdmi: HDMI0:EDID error reading EDID block 0 attempt 8 + MESS:00:00:01.647288:0: hdmi: HDMI0:EDID error reading EDID block 0 attempt 9 + MESS:00:00:01.653905:0: hdmi: HDMI0:EDID giving up on reading EDID block 0 + MESS:00:00:01.659769:0: hdmi: HDMI:hdmi_get_state is deprecated, use hdmi_get_display_state instead + MESS:00:00:01.668264:0: HDMI0: hdmi_pixel_encoding: 162000000 + MESS:00:00:01.673988:0: vec: vec_middleware_power_on: vec_base: 0x7e806000 rev-id 0x00002708 @ vec: 0x7e806100 @ 0x00000420 enc: 0x7e806060 @ 0x00000220 cgmsae: 0x7e80605c @ 0x00000000 + MESS:00:00:01.880234:0: dtb_file 'bcm2710-rpi-3-b.dtb' + MESS:00:00:01.889713:0: brfs: File read: /mfs/sd/bcm2710-rpi-3-b.dtb + MESS:00:00:01.894375:0: Loaded 'bcm2710-rpi-3-b.dtb' to 0x200000 size 0x7cb2 + MESS:00:00:01.915761:0: brfs: File read: 31922 bytes + MESS:00:00:02.007202:0: brfs: File read: /mfs/sd/config.txt + MESS:00:00:02.017277:0: brfs: File read: 153 bytes + MESS:00:00:02.020772:0: Failed to open command line file 'cmdline.txt' + MESS:00:00:02.042302:0: gpioman: gpioman_get_pin_num: pin EMMC_ENABLE not defined + MESS:00:00:02.398066:0: kernel= + MESS:00:00:02.455255:0: brfs: File read: /mfs/sd/armstub8.bin + MESS:00:00:02.459284:0: Loaded 'armstub8.bin' to 0x0 size 0xdbe74 + MESS:00:00:02.465109:0: No compatible kernel found + MESS:00:00:02.469610:0: Device tree loaded to 0x200000 (size 0x823f) + MESS:00:00:02.476805:0: uart: Set PL011 baud rate to 103448.300000 Hz + MESS:00:00:02.483381:0: uart: Baud rate change done... + MESS:00:00:02.486793:0: uart: Baud rateNOTICE: Booting Trusted Firmware + NOTICE: BL1: v2.11.0(release):v2.11.0-187-g0cb1ddc9c-dirty + NOTICE: BL1: Built : 10:57:10, Jul 9 2024 + INFO: BL1: RAM 0x100ee000 - 0x100f9000 + INFO: Using crypto library 'mbed TLS' + NOTICE: TPM Chip: vendor-id 0xd1, device-id 0x0, revision-id: 0x16 + NOTICE: rpi3: Detected: Raspberry Pi 3 Model B (1GB, Sony, UK) [0x00a02082] + INFO: BL1: Loading BL2 + INFO: Loading image id=1 at address 0x100b4000 + INFO: Image id=1 loaded: 0x100b4000 - 0x100c0281 + INFO: TCG_EfiSpecIDEvent: + INFO: PCRIndex : 0 + INFO: EventType : 3 + INFO: Digest : 00 + INFO: : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + INFO: : 00 00 00 + INFO: EventSize : 33 + INFO: Signature : Spec ID Event03 + INFO: PlatformClass : 0 + INFO: SpecVersion : 2.0.2 + INFO: UintnSize : 1 + INFO: NumberOfAlgorithms : 1 + INFO: DigestSizes : + INFO: #0 AlgorithmId : SHA256 + INFO: DigestSize : 32 + INFO: VendorInfoSize : 0 + INFO: PCR_Event2: + INFO: PCRIndex : 0 + INFO: EventType : 3 + INFO: Digests Count : 1 + INFO: #0 AlgorithmId : SHA256 + INFO: Digest : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + INFO: : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + INFO: EventSize : 17 + INFO: Signature : StartupLocality + INFO: StartupLocality : 0 + INFO: PCR_Event2: + INFO: PCRIndex : 0 + INFO: EventType : 1 + INFO: Digests Count : 1 + INFO: #0 AlgorithmId : SHA256 + INFO: Digest : 55 11 51 d8 8b 7f 41 d3 18 16 f2 e8 80 bf 80 fa + INFO: : b4 03 6d 96 4c a0 0a 98 45 cf 25 2f 1e a9 09 3e + INFO: EventSize : 5 + INFO: Event : BL_2 + NOTICE: BL1: Booting BL2 + INFO: Entry point address = 0x100b4000 + INFO: SPSR = 0x3c5 + NOTICE: BL2: v2.11.0(release):v2.11.0-187-g0cb1ddc9c-dirty + NOTICE: BL2: Built : 10:56:39, Jul 9 2024 + INFO: Using crypto library 'mbed TLS' + NOTICE: TPM Chip: vendor-id 0xd1, device-id 0x0, revision-id: 0x16 + INFO: BL2: Doing platform setup + INFO: BL2: Loading image id 3 + INFO: Loading image id=3 at address 0x100e0000 + INFO: Image id=3 loaded: 0x100e0000 - 0x100e706b + INFO: BL2: Loading image id 5 + INFO: Loading image id=5 at address 0x11000000 + INFO: Image id=5 loaded: 0x11000000 - 0x110a8ad8 + INFO: TCG_EfiSpecIDEvent: + INFO: PCRIndex : 0 + INFO: EventType : 3 + INFO: Digest : 00 + INFO: : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + INFO: : 00 00 00 + INFO: EventSize : 33 + INFO: Signature : Spec ID Event03 + INFO: PlatformClass : 0 + INFO: SpecVersion : 2.0.2 + INFO: UintnSize : 1 + INFO: NumberOfAlgorithms : 1 + INFO: DigestSizes : + INFO: #0 AlgorithmId : SHA256 + INFO: DigestSize : 32 + INFO: VendorInfoSize : 0 + INFO: PCR_Event2: + INFO: PCRIndex : 0 + INFO: EventType : 3 + INFO: Digests Count : 1 + INFO: #0 AlgorithmId : SHA256 + INFO: Digest : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + INFO: : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + INFO: EventSize : 17 + INFO: Signature : StartupLocality + INFO: StartupLocality : 0 + INFO: PCR_Event2: + INFO: PCRIndex : 0 + INFO: EventType : 1 + INFO: Digests Count : 1 + INFO: #0 AlgorithmId : SHA256 + INFO: Digest : 55 11 51 d8 8b 7f 41 d3 18 16 f2 e8 80 bf 80 fa + INFO: : b4 03 6d 96 4c a0 0a 98 45 cf 25 2f 1e a9 09 3e + INFO: EventSize : 5 + INFO: Event : BL_2 + INFO: PCR_Event2: + INFO: PCRIndex : 0 + INFO: EventType : 1 + INFO: Digests Count : 1 + INFO: #0 AlgorithmId : SHA256 + INFO: Digest : f3 00 5c ed a2 12 8b 76 b7 82 da c5 28 c3 02 52 + INFO: : 19 e4 3a 82 f2 3c ab 1e 0d 78 84 9c b5 fe e2 4f + INFO: EventSize : 14 + INFO: Event : SECURE_RT_EL3 + INFO: PCR_Event2: + INFO: PCRIndex : 0 + INFO: EventType : 1 + INFO: Digests Count : 1 + INFO: #0 AlgorithmId : SHA256 + INFO: Digest : 90 28 81 42 12 b7 9b ca aa 0c 40 76 33 5a 69 71 + INFO: : b6 19 2b 90 f2 d2 69 b8 de 8e 6d 05 4d c2 73 f9 + INFO: EventSize : 6 + INFO: Event : BL_33 + NOTICE: BL1: Booting BL31 + INFO: Entry point address = 0x100e0000 + INFO: SPSR = 0x3cd + NOTICE: BL31: v2.11.0(release):v2.11.0-187-g0cb1ddc9c-dirty + NOTICE: BL31: Built : 10:56:58, Jul 9 2024 + INFO: rpi3: Checking DTB... + INFO: rpi3: Reserved 0x10000000 - 0x10100000 in DTB + INFO: BL31: Initializing runtime services + INFO: BL31: Preparing for EL3 exit to normal world + INFO: Entry point address = 0x11000000 + INFO: SPSR = 0x3c9 + + + U-Boot 2024.04-g84314330-dirty (Apr 23 2024 - 15:41:54 -0500) + + DRAM: 948 MiB + RPI 3 Model B (0xa02082) + Core: 68 devices, 14 uclasses, devicetree: embed + MMC: mmc@7e202000: 0, mmc@7e300000: 1 + Loading Environment from FAT... OK + In: serial,usbkbd + Out: serial,vidconsole + Err: serial,vidconsole + Net: No ethernet found. + starting USB... + Bus usb@7e980000: USB DWC2 + scanning bus usb@7e980000 for devices... + Error: smsc95xx_eth No valid MAC address found. + 2 USB Device(s) found + scanning usb for storage devices... 0 Storage Device(s) found + Hit any key to stop autoboot: 2 1 0 + Card did not respond to voltage select! : -110 + No EFI system partition + No EFI system partition + Failed to persist EFI variables + No EFI system partition + Failed to persist EFI variables + No EFI system partition + Failed to persist EFI variables + Missing TPMv2 device for EFI_TCG_PROTOCOL + ** Booting bootflow '' with efi_mgr + Loading Boot0000 'mmc 0' failed + EFI boot manager: Cannot load any image + Boot failed (err=-14) + Card did not respond to voltage select! : -110 + No ethernet found. + No ethernet found. + U-Boot> + + +Next steps for Discrete TPM and Measured Boot development +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +In order to automatically validate the workings of the Discrete TPM, the creation +of test cases that compare the eventlog image hashes with what is stored in PCR0 +are a great way to test the core functionality of the Discrete TPM in Measured Boot. + +Development of Discrete TPM drivers such as a reference FIFO |I2C|, MMIO, and CRB +drivers has not started, these drivers will allow a larger number of platform +to use a Discrete TPM in TF-A. + +*Copyright (c) 2025, Arm Limited. All rights reserved.* + +.. _TCG event log: https://trustedcomputinggroup.org/resource/tcg-efi-platform-specification/ +.. _rpi3 pinout: https://www.raspberrypi.com/documentation/computers/raspberry-pi.html#gpio +.. _instructions: https://www.circuitbasics.com/use-putty-to-access-the-raspberry-pi-terminal-from-a-computer/ +.. _workaround: https://github.com/mhomran/u-boot-rpi3-b-plus diff --git a/docs/getting_started/build-options.rst b/docs/getting_started/build-options.rst index 740f3a633..b5814bba7 100644 --- a/docs/getting_started/build-options.rst +++ b/docs/getting_started/build-options.rst @@ -784,6 +784,20 @@ Common build options This option defaults to 0. +- ``DISCRETE_TPM``: Boolean flag to include support for a Discrete TPM. + + This option defaults to 0. + +- ``TPM_INTERFACE``: When ``DISCRETE_TPM=1``, this is a required flag to + select the TPM interface. Currently only one interface is supported: + + :: + + FIFO_SPI + +- ``MBOOT_TPM_HASH_ALG``: Build flag to select the TPM hash algorithm used during + Measured Boot. Currently only accepts ``sha256`` as a valid algorithm. + - ``MARCH_DIRECTIVE``: used to pass a -march option from the platform build options to the compiler. An example usage: diff --git a/docs/global_substitutions.txt b/docs/global_substitutions.txt index 23a91cdcd..ecf6d6396 100644 --- a/docs/global_substitutions.txt +++ b/docs/global_substitutions.txt @@ -6,6 +6,7 @@ .. |BTI| replace:: :term:`BTI` .. |CoT| replace:: :term:`CoT` .. |COT| replace:: :term:`COT` +.. |CRB| replace:: :term:`CRB` .. |CSS| replace:: :term:`CSS` .. |CVE| replace:: :term:`CVE` .. |DICE| replace:: :term:`DICE` @@ -19,11 +20,13 @@ .. |FCONF| replace:: :term:`FCONF` .. |FDT| replace:: :term:`FDT` .. |FF-A| replace:: :term:`FF-A` +.. |FIFO| replace:: :term:`FIFO` .. |FIP| replace:: :term:`FIP` .. |FVP| replace:: :term:`FVP` .. |FWU| replace:: :term:`FWU` .. |GIC| replace:: :term:`GIC` .. |HES| replace:: :term:`HES` +.. |I2C| replace:: :term:`I2C` .. |ISA| replace:: :term:`ISA` .. |Linaro| replace:: :term:`Linaro` .. |MMU| replace:: :term:`MMU` @@ -55,6 +58,7 @@ .. |SP| replace:: :term:`SP` .. |SPD| replace:: :term:`SPD` .. |SPM| replace:: :term:`SPM` +.. |SPI| replace:: :term:`SPI` .. |SRTM| replace:: :term:`SRTM` .. |SSBS| replace:: :term:`SSBS` .. |SVE| replace:: :term:`SVE` diff --git a/docs/glossary.rst b/docs/glossary.rst index 20ad21c14..8bb35bc32 100644 --- a/docs/glossary.rst +++ b/docs/glossary.rst @@ -36,6 +36,9 @@ You can find additional definitions in the `Arm Glossary`_. CSS Compute Sub-System + CRB + Command Response Buffer + CVE Common Vulnerabilities and Exposures. A CVE document is commonly used to describe a publicly-known security vulnerability. @@ -88,6 +91,9 @@ You can find additional definitions in the `Arm Glossary`_. FF-A Firmware Framework for Arm A-profile + FIFO + First In, First Out + FIP Firmware Image Package @@ -103,6 +109,9 @@ You can find additional definitions in the `Arm Glossary`_. HES Arm CCA Hardware Enforced Security + I2C + Inter-Integrated Circuit Protocol + ISA Instruction Set Architecture @@ -211,6 +220,9 @@ You can find additional definitions in the `Arm Glossary`_. SPM Secure Partition Manager + SPI + Serial Peripheral Interface + SRTM Static Root of Trust for Measurement diff --git a/docs/resources/diagrams/rpi3_dtpm_driver.png b/docs/resources/diagrams/rpi3_dtpm_driver.png new file mode 100644 index 000000000..36b18439c Binary files /dev/null and b/docs/resources/diagrams/rpi3_dtpm_driver.png differ diff --git a/docs/threat_model/firmware_threat_model/threat_model.rst b/docs/threat_model/firmware_threat_model/threat_model.rst index ae0219ee3..c0cc3be50 100644 --- a/docs/threat_model/firmware_threat_model/threat_model.rst +++ b/docs/threat_model/firmware_threat_model/threat_model.rst @@ -928,6 +928,12 @@ nonetheless once execution has reached the runtime EL3 firmware. Measured Boot implementation in |TF-A| is that it does not extend the measurements into a |PCR| of a Discrete |TPM|, where measurements would be securely stored and protected against tampering. + - Discrete |TPM|: Implemented in |TF-A| as a proof of concept, the Discrete + |TPM| is used alongside the existing TCG-compliant Event Log. This + Measured Boot implementation extends measurement hashes to a |PCR| in the + |TPM|, which provides a hardware-backed root of trust. The measurements in + the Event Log can now be hashed and compared to the value of the |PCR| to + determine if tampering of the Event Log has taken place. - `CCA Measured Boot`_: Implemented by |TF-M|. Measurements are stored in |HES| secure on-chip memory. |HES| implements protection against tampering its on-chip memory. |HES| interface is available for BL1 and BL2. @@ -942,6 +948,20 @@ nonetheless once execution has reached the runtime EL3 firmware. to protect or threats to defend against that could compromise |TF-A| execution environment's security. + When considering the implementation of Measured Boot using a TCG-compliant + Event Log backed by a discrete TPM, physical vulnerabilities come to mind. + Platforms have many different ways of integrating a discrete TPM, and these + implementations can be susceptible to man-in-the-middle attacks, where the + attacker intercepts the bus traffic between the discrete TPM and the host + machine. This can lead to PCR extend operations being modified, compromising + Measured Boot. This vulnerability requires physical access to the host machine. + + TF-A does not provide any mitigations against these physical vulnerabilities, + it is the responsibility of the platform owners to address this based on their + specific threat model. Mitigation of this can be achieved through dedicated + hardware solutions, such as an encrypted AP/dTPM bus, or software-based + approaches designed to protect sensitive data such as parameter encryption. + There are general security assets and threats associated with remote/delegated attestation. However, these are outside the |TF-A| security boundary and should be dealt with by the appropriate agent in the platform/system. @@ -1192,7 +1212,7 @@ Threats to be Mitigated by an External Agent Outside of TF-A -------------- -*Copyright (c) 2021-2024, Arm Limited. All rights reserved.* +*Copyright (c) 2021-2025, Arm Limited. All rights reserved.* .. _STRIDE threat analysis technique: https://docs.microsoft.com/en-us/azure/security/develop/threat-modeling-tool-threats#stride-model diff --git a/drivers/gpio/gpio_spi.c b/drivers/gpio/gpio_spi.c new file mode 100644 index 000000000..2913b4192 --- /dev/null +++ b/drivers/gpio/gpio_spi.c @@ -0,0 +1,130 @@ +/* + * Copyright (c) 2025, ARM Limited and Contributors. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include +#include + +#include +#include +#include +#include +#include + +static struct spi_plat gpio_spidev; + +static void gpio_spi_delay_us(void) +{ + udelay(gpio_spidev.gpio_data.spi_delay_us); +} + +static int gpio_spi_miso(void) +{ + return gpio_get_value(gpio_spidev.gpio_data.miso_gpio); +} + +static void gpio_spi_sclk(int bit) +{ + gpio_set_value(gpio_spidev.gpio_data.sclk_gpio, bit); +} + +static void gpio_spi_mosi(int bit) +{ + gpio_set_value(gpio_spidev.gpio_data.mosi_gpio, bit); +} + +static void gpio_spi_cs(int bit) +{ + gpio_set_value(gpio_spidev.gpio_data.cs_gpio, bit); +} + +static void gpio_spi_start(void) +{ + gpio_spi_cs(1); + gpio_spi_sclk(0); + gpio_spi_cs(0); +} + +static void gpio_spi_stop(void) +{ + gpio_spi_cs(1); +} + +/* set sclk to a known state (0) before performing any further action */ +static void gpio_spi_get_access(void) +{ + gpio_spi_sclk(0); +} + +static void xfer(unsigned int bytes, const void *out, void *in, int cpol, int cpha) +{ + for (unsigned int j = 0U; j < bytes; j++) { + unsigned char in_byte = 0U; + unsigned char out_byte = (out != NULL) ? *(const uint8_t *)out++ : 0xFF; + + for (int i = 7; i >= 0; i--) { + if (cpha) { + gpio_spi_sclk(!cpol); + } + + gpio_spi_mosi(!!(out_byte & (1 << i))); + + gpio_spi_delay_us(); + gpio_spi_sclk(cpha ? cpol : !cpol); + gpio_spi_delay_us(); + + in_byte |= gpio_spi_miso() << i; + + if (!cpha) { + gpio_spi_sclk(cpol); + } + } + + if (in != NULL) { + *(uint8_t *)in++ = in_byte; + } + } +} + +static int gpio_spi_xfer(unsigned int bytes, const void *out, void *in) +{ + if ((out == NULL) && (in == NULL)) { + return -1; + } + + switch (gpio_spidev.gpio_data.spi_mode) { + case 0: + xfer(bytes, out, in, 0, 0); + break; + case 1: + xfer(bytes, out, in, 0, 1); + break; + case 2: + xfer(bytes, out, in, 1, 0); + break; + case 3: + xfer(bytes, out, in, 1, 1); + break; + default: + return -1; + } + + return 0; +} + +struct spi_ops gpio_spidev_ops = { + .get_access = gpio_spi_get_access, + .start = gpio_spi_start, + .stop = gpio_spi_stop, + .xfer = gpio_spi_xfer, +}; + +struct spi_plat *gpio_spi_init(struct gpio_spi_data *gpio_spi_data) +{ + gpio_spidev.gpio_data = *gpio_spi_data; + gpio_spidev.ops = &gpio_spidev_ops; + + return &gpio_spidev; +} diff --git a/drivers/measured_boot/event_log/event_log.mk b/drivers/measured_boot/event_log/event_log.mk index 5ea4c554a..9e0d6c4f6 100644 --- a/drivers/measured_boot/event_log/event_log.mk +++ b/drivers/measured_boot/event_log/event_log.mk @@ -1,5 +1,5 @@ # -# Copyright (c) 2020-2022, Arm Limited. All rights reserved. +# Copyright (c) 2020-2025, Arm Limited. All rights reserved. # # SPDX-License-Identifier: BSD-3-Clause # @@ -7,15 +7,20 @@ # Default log level to dump the event log (LOG_LEVEL_INFO) EVENT_LOG_LEVEL ?= 40 -# Measured Boot hash algorithm. -# SHA-256 (or stronger) is required for all devices that are TPM 2.0 compliant. -ifdef TPM_HASH_ALG - $(warning "TPM_HASH_ALG is deprecated. Please use MBOOT_EL_HASH_ALG instead.") - MBOOT_EL_HASH_ALG := ${TPM_HASH_ALG} +# When using a TPM, adopt the TPM's hash algorithm for +# measurements through the Event Log mechanism, ensuring +# the TPM uses the same algorithm for measurements and +# extends the PCR accordingly, allowing for comparison +# between PCR value and Event Log measurements required +# for attestation. +ifdef MBOOT_TPM_HASH_ALG + MBOOT_EL_HASH_ALG := ${MBOOT_TPM_HASH_ALG} else MBOOT_EL_HASH_ALG := sha256 endif +# Measured Boot hash algorithm. +# SHA-256 (or stronger) is required for all devices that are TPM 2.0 compliant. ifeq (${MBOOT_EL_HASH_ALG}, sha512) TPM_ALG_ID := TPM_ALG_SHA512 TCG_DIGEST_SIZE := 64U diff --git a/drivers/tpm/tpm2.mk b/drivers/tpm/tpm2.mk new file mode 100644 index 000000000..4418b97a9 --- /dev/null +++ b/drivers/tpm/tpm2.mk @@ -0,0 +1,26 @@ +# +# Copyright (c) 2025, Arm Limited. All rights reserved. +# +# SPDX-License-Identifier: BSD-3-Clause +# + +TPM2_SRC_DIR := drivers/tpm/ + +TPM2_SOURCES := ${TPM2_SRC_DIR}tpm2_cmds.c \ + ${TPM2_SRC_DIR}tpm2_chip.c + +# TPM Hash algorithm, used during Measured Boot +# currently only accepts SHA-256 +ifeq (${MBOOT_TPM_HASH_ALG}, sha256) + TPM_ALG_ID := TPM_ALG_SHA256 + TCG_DIGEST_SIZE := 32U +else + $(error "The selected MBOOT_TPM_HASH_ALG is invalid.") +endif #MBOOT_TPM_HASH_ALG + +ifeq (${TPM_INTERFACE}, FIFO_SPI) + TPM2_SOURCES += ${TPM2_SRC_DIR}tpm2_fifo.c \ + ${TPM2_SRC_DIR}tpm2_fifo_spi.c +else + $(error "The selected TPM_INTERFACE is invalid.") +endif #TPM_INTERFACE diff --git a/drivers/tpm/tpm2_chip.c b/drivers/tpm/tpm2_chip.c new file mode 100644 index 000000000..537ce92a9 --- /dev/null +++ b/drivers/tpm/tpm2_chip.c @@ -0,0 +1,21 @@ + +/* + * Copyright (c) 2025, Arm Limited. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include + +/* + * TPM timeout values + * Reference: TCG PC Client Platform TPM Profile (PTP) Specification v1.05 + */ +struct tpm_chip_data tpm_chip_data = { + .locality = -1, + .timeout_msec_a = 750, + .timeout_msec_b = 2000, + .timeout_msec_c = 200, + .timeout_msec_d = 30, + .address = 0, +}; diff --git a/drivers/tpm/tpm2_cmds.c b/drivers/tpm/tpm2_cmds.c new file mode 100644 index 000000000..b6422a8db --- /dev/null +++ b/drivers/tpm/tpm2_cmds.c @@ -0,0 +1,222 @@ +/* + * Copyright (c) 2025, Arm Limited. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ +#include + +#include +#include +#include +#include + +#define CMD_SIZE_OFFSET 6 + +#define SINGLE_BYTE 1 +#define TWO_BYTES 2 +#define FOUR_BYTES 4 + +static struct interface_ops *interface; + +static int tpm_xfer(struct tpm_chip_data *chip_data, const tpm_cmd *send, tpm_cmd *receive) +{ + int ret; + + ret = interface->send(chip_data, send); + if (ret < 0) { + return ret; + } + + ret = interface->receive(chip_data, receive); + if (ret < 0) { + return ret; + } + + return TPM_SUCCESS; +} + +int tpm_interface_init(struct tpm_chip_data *chip_data, uint8_t locality) +{ + int err; + + interface = tpm_interface_getops(chip_data, locality); + + err = interface->request_access(chip_data, locality); + if (err != 0) { + return err; + } + + return interface->get_info(chip_data, locality); +} + +int tpm_interface_close(struct tpm_chip_data *chip_data, uint8_t locality) +{ + return interface->release_locality(chip_data, locality); +} + +static int tpm_update_buffer(tpm_cmd *buf, uint32_t new_data, size_t new_len) +{ + int i, j, start; + uint32_t command_size; + + union { + uint8_t var8; + uint16_t var16; + uint32_t var32; + uint8_t array[4]; + } tpm_new_data; + + command_size = be32toh(buf->header.cmd_size); + + if (command_size + new_len > MAX_SIZE_CMDBUF) { + ERROR("%s: buf size exceeded, increase MAX_SIZE_CMDBUF\n", + __func__); + return TPM_INVALID_PARAM; + } + /* + * Subtract the cmd header size from the current command size + * so the data buffer is written to starting at index 0. + */ + start = command_size - TPM_HEADER_SIZE; + + /* + * The TPM, according to the TCG spec, processes data in BE byte order, + * in the case where the Host is LE, htobe correctly handles the byte order. + * When updating the buffer, keep in mind to only pass sizeof(new_data) or + * the variable type size for the new_len function parameter. This ensures + * there is only the possiblility of writing 1, 2, or 4 bytes to the buffer, + * and that the correct number of bytes are written to data[i]. + */ + if (new_len == SINGLE_BYTE) { + tpm_new_data.var8 = new_data & 0xFF; + } else if (new_len == TWO_BYTES) { + tpm_new_data.var16 = htobe16(new_data & 0xFFFF); + } else if (new_len == FOUR_BYTES) { + tpm_new_data.var32 = htobe32(new_data); + } else { + ERROR("%s: Invalid data length\n", __func__); + return TPM_INVALID_PARAM; + } + + for (i = start, j = 0; i < start + new_len; i++, j++) { + buf->data[i] = tpm_new_data.array[j]; + } + buf->header.cmd_size = htobe32(command_size + new_len); + + return TPM_SUCCESS; +} + + +int tpm_startup(struct tpm_chip_data *chip_data, uint16_t mode) +{ + tpm_cmd startup_cmd, startup_response; + uint32_t tpm_rc; + int ret; + + memset(&startup_cmd, 0, sizeof(startup_cmd)); + memset(&startup_response, 0, sizeof(startup_response)); + + startup_cmd.header.tag = htobe16(TPM_ST_NO_SESSIONS); + startup_cmd.header.cmd_size = htobe32(sizeof(tpm_cmd_hdr)); + startup_cmd.header.cmd_code = htobe32(TPM_CMD_STARTUP); + + ret = tpm_update_buffer(&startup_cmd, mode, sizeof(mode)); + if (ret < 0) { + return ret; + } + + ret = tpm_xfer(chip_data, &startup_cmd, &startup_response); + if (ret < 0) { + return ret; + } + + tpm_rc = be32toh(startup_response.header.cmd_code); + if (tpm_rc != TPM_RESPONSE_SUCCESS) { + ERROR("%s: response code contains error = %X\n", __func__, tpm_rc); + return TPM_ERR_RESPONSE; + } + + return TPM_SUCCESS; +} + +int tpm_pcr_extend(struct tpm_chip_data *chip_data, uint32_t index, + uint16_t algorithm, const uint8_t *digest, + uint32_t digest_len) +{ + tpm_cmd pcr_extend_cmd, pcr_extend_response; + uint32_t tpm_rc; + int ret; + + memset(&pcr_extend_cmd, 0, sizeof(pcr_extend_cmd)); + memset(&pcr_extend_response, 0, sizeof(pcr_extend_response)); + + if (digest == NULL) { + return TPM_INVALID_PARAM; + } + pcr_extend_cmd.header.tag = htobe16(TPM_ST_SESSIONS); + pcr_extend_cmd.header.cmd_size = htobe32(sizeof(tpm_cmd_hdr)); + pcr_extend_cmd.header.cmd_code = htobe32(TPM_CMD_PCR_EXTEND); + + /* handle (PCR Index)*/ + ret = tpm_update_buffer(&pcr_extend_cmd, index, sizeof(index)); + if (ret < 0) { + return ret; + } + + /* authorization size , session handle, nonce size, attributes*/ + ret = tpm_update_buffer(&pcr_extend_cmd, TPM_MIN_AUTH_SIZE, sizeof(uint32_t)); + if (ret < 0) { + return ret; + } + ret = tpm_update_buffer(&pcr_extend_cmd, TPM_RS_PW, sizeof(uint32_t)); + if (ret < 0) { + return ret; + } + ret = tpm_update_buffer(&pcr_extend_cmd, TPM_ZERO_NONCE_SIZE, sizeof(uint16_t)); + if (ret < 0) { + return ret; + } + ret = tpm_update_buffer(&pcr_extend_cmd, TPM_ATTRIBUTES_DISABLE, sizeof(uint8_t)); + if (ret < 0) { + return ret; + } + + /* hmac/password size */ + ret = tpm_update_buffer(&pcr_extend_cmd, TPM_ZERO_HMAC_SIZE, sizeof(uint16_t)); + if (ret < 0) { + return ret; + } + + /* hashes count */ + ret = tpm_update_buffer(&pcr_extend_cmd, TPM_SINGLE_HASH_COUNT, sizeof(uint32_t)); + if (ret < 0) { + return ret; + } + + /* hash algorithm */ + ret = tpm_update_buffer(&pcr_extend_cmd, algorithm, sizeof(algorithm)); + if (ret < 0) { + return ret; + } + + /* digest */ + for (int i = 0; i < digest_len; i++) { + ret = tpm_update_buffer(&pcr_extend_cmd, digest[i], sizeof(uint8_t)); + if (ret < 0) { + return ret; + } + } + + ret = tpm_xfer(chip_data, &pcr_extend_cmd, &pcr_extend_response); + if (ret < 0) { + return ret; + } + + tpm_rc = be32toh(pcr_extend_response.header.cmd_code); + if (tpm_rc != TPM_RESPONSE_SUCCESS) { + ERROR("%s: response code contains error = %X\n", __func__, tpm_rc); + return TPM_ERR_RESPONSE; + } + + return TPM_SUCCESS; +} diff --git a/drivers/tpm/tpm2_fifo.c b/drivers/tpm/tpm2_fifo.c new file mode 100644 index 000000000..7c4b9d8b4 --- /dev/null +++ b/drivers/tpm/tpm2_fifo.c @@ -0,0 +1,322 @@ +/* + * Copyright (c) 2025, Arm Limited. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ +#include + +#include +#include +#include +#include + +#define LOCALITY_START_ADDRESS(x, y) \ + ((uint16_t)(x->address + (0x1000 * y))) + +static int tpm2_get_info(struct tpm_chip_data *chip_data, uint8_t locality) +{ + uint16_t tpm_base_addr = LOCALITY_START_ADDRESS(chip_data, locality); + uint32_t vid_did; + uint8_t revision; + int err; + + err = tpm2_fifo_read_chunk(tpm_base_addr + TPM_FIFO_REG_VENDID, DWORD, &vid_did); + if (err < 0) { + return err; + } + + err = tpm2_fifo_read_byte(tpm_base_addr + TPM_FIFO_REG_REVID, &revision); + if (err < 0) { + return err; + } + + INFO("TPM Chip: vendor-id 0x%x, device-id 0x%x, revision-id: 0x%x\n", + 0xFFFF & vid_did, vid_did >> 16, revision); + + return TPM_SUCCESS; +} + +static int tpm2_wait_reg_bits(uint16_t reg, uint8_t set, unsigned long timeout, uint8_t *status) +{ + int err; + uint64_t timeout_delay = timeout_init_us(timeout * 1000); + + do { + err = tpm2_fifo_read_byte(reg, status); + if (err < 0) { + return err; + } + if ((*status & set) == set) { + return TPM_SUCCESS; + } + } while (!timeout_elapsed(timeout_delay)); + + return TPM_ERR_TIMEOUT; +} + +static int tpm2_fifo_request_access(struct tpm_chip_data *chip_data, uint8_t locality) +{ + uint16_t tpm_base_addr = LOCALITY_START_ADDRESS(chip_data, locality); + uint8_t status; + int err; + + err = tpm2_fifo_write_byte(tpm_base_addr + TPM_FIFO_REG_ACCESS, TPM_ACCESS_REQUEST_USE); + if (err < 0) { + return err; + } + + err = tpm2_wait_reg_bits(tpm_base_addr + TPM_FIFO_REG_ACCESS, + TPM_ACCESS_ACTIVE_LOCALITY, + chip_data->timeout_msec_a, &status); + if (err == 0) { + chip_data->locality = locality; + return TPM_SUCCESS; + } + + return err; +} + +static int tpm2_fifo_release_locality(struct tpm_chip_data *chip_data, uint8_t locality) +{ + uint16_t tpm_base_addr = LOCALITY_START_ADDRESS(chip_data, locality); + uint8_t buf; + int err; + + err = tpm2_fifo_read_byte(tpm_base_addr + TPM_FIFO_REG_ACCESS, &buf); + if (err < 0) { + return err; + } + + if (buf & (TPM_ACCESS_REQUEST_PENDING | TPM_ACCESS_VALID)) { + return tpm2_fifo_write_byte(tpm_base_addr + TPM_FIFO_REG_ACCESS, + TPM_ACCESS_RELINQUISH_LOCALITY); + } + + ERROR("%s: Unable to release locality\n", __func__); + return TPM_ERR_RESPONSE; +} + +static int tpm2_fifo_prepare(struct tpm_chip_data *chip_data) +{ + uint16_t tpm_base_addr = LOCALITY_START_ADDRESS(chip_data, chip_data->locality); + uint8_t status; + int err; + + err = tpm2_fifo_write_byte(tpm_base_addr + TPM_FIFO_REG_STATUS, TPM_STAT_COMMAND_READY); + if (err < 0) { + return err; + } + + err = tpm2_wait_reg_bits(tpm_base_addr + TPM_FIFO_REG_STATUS, + TPM_STAT_COMMAND_READY, + chip_data->timeout_msec_b, &status); + if (err < 0) { + ERROR("%s: TPM Status Busy\n", __func__); + return err; + } + + return TPM_SUCCESS; +} + +static int tpm2_fifo_get_burstcount(struct tpm_chip_data *chip_data, uint16_t *burstcount) +{ + uint16_t tpm_base_addr = LOCALITY_START_ADDRESS(chip_data, chip_data->locality); + uint64_t timeout_delay = timeout_init_us(chip_data->timeout_msec_a * 1000); + int err; + + if (burstcount == NULL) { + return TPM_INVALID_PARAM; + } + + do { + uint8_t byte0, byte1; + + err = tpm2_fifo_read_byte(tpm_base_addr + TPM_FIFO_REG_BURST_COUNT_LO, &byte0); + if (err < 0) { + return err; + } + + err = tpm2_fifo_read_byte(tpm_base_addr + TPM_FIFO_REG_BURST_COUNT_HI, &byte1); + if (err < 0) { + return err; + } + + *burstcount = (uint16_t)((byte1 << 8) + byte0); + if (*burstcount != 0U) { + return TPM_SUCCESS; + } + } while (!timeout_elapsed(timeout_delay)); + + return TPM_ERR_TIMEOUT; +} + +static int tpm2_fifo_send(struct tpm_chip_data *chip_data, const tpm_cmd *buf) +{ + uint16_t tpm_base_addr = LOCALITY_START_ADDRESS(chip_data, chip_data->locality); + uint8_t status; + uint16_t burstcnt; + int err; + uint32_t len, index; + + if (sizeof(buf->header) != TPM_HEADER_SIZE) { + ERROR("%s: invalid command header size.\n", __func__); + return TPM_INVALID_PARAM; + } + + err = tpm2_fifo_read_byte(tpm_base_addr + TPM_FIFO_REG_STATUS, &status); + if (err < 0) { + return err; + } + + if (!(status & TPM_STAT_COMMAND_READY)) { + err = tpm2_fifo_prepare(chip_data); + if (err < 0) { + return err; + } + } + + /* write the command header to the TPM first */ + const uint8_t *header_data = (const uint8_t *)&buf->header; + + for (index = 0; index < TPM_HEADER_SIZE; index++) { + err = tpm2_fifo_write_byte(tpm_base_addr + TPM_FIFO_REG_DATA_FIFO, + header_data[index]); + if (err < 0) { + return err; + } + } + + len = be32toh(buf->header.cmd_size); + + while (index < len) { + err = tpm2_fifo_get_burstcount(chip_data, &burstcnt); + if (err < 0) { + return err; + } + + for (; burstcnt > 0U && index < len; burstcnt--) { + err = tpm2_fifo_write_byte(tpm_base_addr + TPM_FIFO_REG_DATA_FIFO, + buf->data[index - TPM_HEADER_SIZE]); + if (err < 0) { + return err; + } + index++; + } + } + + err = tpm2_wait_reg_bits(tpm_base_addr + TPM_FIFO_REG_STATUS, + TPM_STAT_VALID, + chip_data->timeout_msec_c, + &status); + if (err < 0) { + return err; + } + + if (status & TPM_STAT_EXPECT) { + ERROR("%s: TPM is still expecting data after command buffer is sent\n", __func__); + return TPM_ERR_TRANSFER; + } + + err = tpm2_fifo_write_byte(tpm_base_addr + TPM_FIFO_REG_STATUS, TPM_STAT_GO); + if (err < 0) { + return err; + } + + return TPM_SUCCESS; +} + +static int tpm2_fifo_read_data(struct tpm_chip_data *chip_data, tpm_cmd *buf, + uint16_t tpm_base_addr, uint8_t *status, int *size, int bytes_expected) +{ + int err, read_size, loop_index; + uint16_t burstcnt; + uint8_t *read_data; + + if (bytes_expected == TPM_READ_HEADER) { + /* read the response header from the TPM first */ + read_data = (uint8_t *)&buf->header; + read_size = TPM_HEADER_SIZE; + loop_index = *size; + } else { + /* process the rest of the mssg with bytes_expected */ + read_data = buf->data; + read_size = bytes_expected; + loop_index = *size - TPM_HEADER_SIZE; + } + + err = tpm2_wait_reg_bits(tpm_base_addr + TPM_FIFO_REG_STATUS, + TPM_STAT_AVAIL, + chip_data->timeout_msec_c, + status); + if (err < 0) { + return err; + } + + while (*size < read_size) { + err = tpm2_fifo_get_burstcount(chip_data, &burstcnt); + if (err < 0) { + ERROR("%s: TPM burst count error\n", __func__); + return err; + } + + for (; burstcnt > 0U && loop_index < read_size; + burstcnt--, loop_index++, (*size)++) { + err = tpm2_fifo_read_byte( + tpm_base_addr + TPM_FIFO_REG_DATA_FIFO, + (void *)&read_data[loop_index]); + if (err < 0) { + return err; + } + } + } + + return TPM_SUCCESS; +} + +static int tpm2_fifo_receive(struct tpm_chip_data *chip_data, tpm_cmd *buf) +{ + uint16_t tpm_base_addr = LOCALITY_START_ADDRESS(chip_data, chip_data->locality); + int size = 0, bytes_expected, err; + uint8_t status; + + err = tpm2_fifo_read_data(chip_data, buf, tpm_base_addr, &status, &size, TPM_READ_HEADER); + if (err < 0) { + return err; + } + + bytes_expected = be32toh(buf->header.cmd_size); + if (bytes_expected > sizeof(*buf)) { + ERROR("%s: tpm response buffer cannot store expected response\n", __func__); + return TPM_INVALID_PARAM; + } + + if (size == bytes_expected) { + return size; + } + + err = tpm2_fifo_read_data(chip_data, buf, tpm_base_addr, &status, &size, bytes_expected); + if (err < 0) { + return err; + } + + if (size < bytes_expected) { + ERROR("%s: response buffer size is less than expected\n", __func__); + return TPM_ERR_RESPONSE; + } + + return TPM_SUCCESS; +} + +static interface_ops_t fifo_ops = { + .get_info = tpm2_get_info, + .send = tpm2_fifo_send, + .receive = tpm2_fifo_receive, + .request_access = tpm2_fifo_request_access, + .release_locality = tpm2_fifo_release_locality, +}; + +struct interface_ops * +tpm_interface_getops(struct tpm_chip_data *chip_data, uint8_t locality) +{ + return &fifo_ops; +} diff --git a/drivers/tpm/tpm2_fifo_spi.c b/drivers/tpm/tpm2_fifo_spi.c new file mode 100644 index 000000000..16d87d918 --- /dev/null +++ b/drivers/tpm/tpm2_fifo_spi.c @@ -0,0 +1,161 @@ +/* + * Copyright (c) 2025, Arm Limited. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include +#include +#include +#include + +#include +#include +#include +#include + +#define ENCODE_LIMIT 128 +#define CS_ASSERT_OFFSET 0xD4 +#define RETRY_COUNT 50 + +#define TPM_READ false +#define TPM_WRITE true + +extern struct spi_plat *spidev; + +static int tpm2_spi_transfer(const void *data_out, void *data_in, uint8_t len) +{ + return spidev->ops->xfer(len, data_out, data_in); +} + +/* + * Reference: TCG PC Client Platform TPM Profile (PTP) Specification v1.05 + */ +static int tpm2_spi_start_transaction(uint16_t tpm_reg, bool write, uint8_t len) +{ + int rc; + uint8_t header[4]; + uint8_t header_response[4]; + uint8_t zero = 0, byte; + int retries; + + /* check to make sure len does not exceed the encoding limit */ + if (len > ENCODE_LIMIT) { + return TPM_INVALID_PARAM; + } + + /* + * 7.4.6 TPM SPI Bit protocol calls for the following header + * to be sent to the TPM at the start of every attempted read/write. + */ + + /* header[0] contains the r/w and the xfer size, if the msb is not + * set, the operation is write, if it is set then it is read. + * The size of the transfer is encoded, and must not overwrite + * the msb, therefore an ENCODE LIMIT of 128 is present. + */ + header[0] = ((write) ? 0x00 : 0x80) | (len - 1); + + /* + * header[1] contains the address offset 0xD4_xxxx as defined + * in the TPM spec, since the CS# is asserted. + */ + header[1] = CS_ASSERT_OFFSET; + + /* + * header[2] and header[3] contain the address of the register + * to be read/written. + */ + header[2] = tpm_reg >> 8; + header[3] = tpm_reg; + + rc = tpm2_spi_transfer(header, header_response, 4); + if (rc != 0) { + return TPM_ERR_TRANSFER; + } + + /* + * 7.4.5 Flow Control defines a wait state in order to accommodate + * the TPM in case it needs to free its buffer. + */ + if ((header_response[3] & 0x01) != 0U) { + return TPM_SUCCESS; + } + + /* + * if the wait state over bit is not set in the initial header_response, + * poll for the wait state over by sending a zeroed byte, if the + * RETRY_COUNT is exceeded the transfer fails. + */ + for (retries = RETRY_COUNT; retries > 0; retries--) { + rc = tpm2_spi_transfer(&zero, &byte, 1); + if (rc != 0) { + return TPM_ERR_TRANSFER; + } + if ((byte & 0x01) != 0U) { + return TPM_SUCCESS; + } + } + + if (retries == 0) { + ERROR("%s: TPM Timeout\n", __func__); + return TPM_ERR_TIMEOUT; + } + + return TPM_SUCCESS; +} + +static void tpm2_spi_end_transaction(void) +{ + spidev->ops->stop(); +} + +static void tpm2_spi_init(void) +{ + spidev->ops->get_access(); + spidev->ops->start(); +} + +static int tpm2_fifo_io(uint16_t tpm_reg, bool is_write, uint8_t len, void *val) +{ + int rc; + + tpm2_spi_init(); + rc = tpm2_spi_start_transaction(tpm_reg, is_write, len); + if (rc != 0) { + tpm2_spi_end_transaction(); + return rc; + } + + rc = tpm2_spi_transfer( + is_write ? val : NULL, + is_write ? NULL : val, + len); + if (rc != 0) { + tpm2_spi_end_transaction(); + return rc; + } + + tpm2_spi_end_transaction(); + + return TPM_SUCCESS; +} + +int tpm2_fifo_write_byte(uint16_t tpm_reg, uint8_t val) +{ + return tpm2_fifo_io(tpm_reg, TPM_WRITE, BYTE, &val); +} + +int tpm2_fifo_read_byte(uint16_t tpm_reg, uint8_t *val) +{ + return tpm2_fifo_io(tpm_reg, TPM_READ, BYTE, val); +} + +int tpm2_fifo_read_chunk(uint16_t tpm_reg, uint8_t len, void *val) +{ + if ((len != BYTE) && (len != WORD) && (len != DWORD)) { + return TPM_INVALID_PARAM; + } + + return tpm2_fifo_io(tpm_reg, TPM_READ, len, val); +} diff --git a/drivers/tpm/tpm2_slb9670/slb9670_gpio.c b/drivers/tpm/tpm2_slb9670/slb9670_gpio.c new file mode 100644 index 000000000..993387da4 --- /dev/null +++ b/drivers/tpm/tpm2_slb9670/slb9670_gpio.c @@ -0,0 +1,75 @@ +/* + * Copyright (c) 2025, Arm Limited. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include +#include +#include + +/* + * Infineon SLB9670 Chip Reset Parameters + */ +#define t_WRST 2 /* Warm Reset Time (us) */ +#define t_RSTIN 60 /* Reset Inactive Time (ms) */ + +/* + * RPi3 GPIO pin configuration for TPM via bit-bang SPI + * References: https://pinout.xyz/pinout/spi + * - docs/design_documents/measured_boot_dtpm_poc.rst + */ +const struct gpio_spi_data tpm_rpi3_gpio_data = { + .cs_gpio = 7, + .sclk_gpio = 11, + .mosi_gpio = 10, + .miso_gpio = 9, + .reset_gpio = 24, + .spi_delay_us = 0, + .spi_mode = 0 +}; + +/* + * When RST is asserted at certain points in time, then this + * triggers the TPM's security functions, in the case where + * multiple resets need to be asserted, there must be a wait + * of at least t_RSTIN between the resets + * + * In most cases this is not needed since RST is only being asserted + * once, ie for TPM initialization at the beginning of TFA. + */ +void tpm2_slb9670_reset_chip(struct gpio_spi_data *tpm_gpio_data) +{ + /* + * since we don't know the value of the pin before it was init to 1 + * it is best to assume the state was 0, and account for that by + * adding an initial RST inactive delay + */ + mdelay(t_RSTIN); + /* pull #RST pin to active low for 2us */ + gpio_set_value(tpm_gpio_data->reset_gpio, 0); + udelay(t_WRST); + /* wait 60ms after warm reset before sending TPM commands */ + gpio_set_value(tpm_gpio_data->reset_gpio, 1); + mdelay(t_RSTIN); +} + +/* + * init GPIO pins for the Infineon slb9670 TPM + */ +void tpm2_slb9670_gpio_init(struct gpio_spi_data *tpm_gpio_data) +{ + gpio_set_value(tpm_gpio_data->cs_gpio, 1); + gpio_set_direction(tpm_gpio_data->cs_gpio, GPIO_DIR_OUT); + + gpio_set_value(tpm_gpio_data->sclk_gpio, 0); + gpio_set_direction(tpm_gpio_data->sclk_gpio, GPIO_DIR_OUT); + + gpio_set_value(tpm_gpio_data->mosi_gpio, 1); + gpio_set_direction(tpm_gpio_data->mosi_gpio, GPIO_DIR_OUT); + + gpio_set_direction(tpm_gpio_data->miso_gpio, GPIO_DIR_IN); + + gpio_set_value(tpm_gpio_data->reset_gpio, 1); + gpio_set_direction(tpm_gpio_data->reset_gpio, GPIO_DIR_OUT); +} diff --git a/include/drivers/gpio_spi.h b/include/drivers/gpio_spi.h new file mode 100644 index 000000000..a92655384 --- /dev/null +++ b/include/drivers/gpio_spi.h @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2025, Arm Limited. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef GPIO_SPI_H +#define GPIO_SPI_H + +#include + +struct gpio_spi_data { + uint8_t cs_gpio, sclk_gpio, mosi_gpio, miso_gpio, reset_gpio; + uint32_t spi_delay_us; + unsigned int spi_mode; +}; + +struct spi_ops { + void (*get_access)(void); + void (*start)(void); + void (*stop)(void); + int (*xfer)(unsigned int bitlen, const void *dout, void *din); +}; + +struct spi_plat { + struct gpio_spi_data gpio_data; + const struct spi_ops *ops; +}; + +struct spi_plat *gpio_spi_init(struct gpio_spi_data *gpio_spi_data); + +#endif /* GPIO_SPI_H */ diff --git a/include/drivers/tpm/tpm2.h b/include/drivers/tpm/tpm2.h new file mode 100644 index 000000000..c91acf8e9 --- /dev/null +++ b/include/drivers/tpm/tpm2.h @@ -0,0 +1,103 @@ +/* + * Copyright (c) 2025, Arm Limited. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef TPM2_H +#define TPM2_H + +#include +#include +#include +#include + +#include + +/* Return values */ +enum tpm_ret_value { + TPM_SUCCESS = 0, + TPM_ERR_RESPONSE = -1, + TPM_INVALID_PARAM = -2, + TPM_ERR_TIMEOUT = -3, + TPM_ERR_TRANSFER = -4, +}; + +/* + * TPM FIFO register space address offsets + */ +#define TPM_FIFO_REG_ACCESS 0x00 +#define TPM_FIFO_REG_INTR_ENABLE 0x08 +#define TPM_FIFO_REG_INTR_VECTOR 0x0C +#define TPM_FIFO_REG_INTR_STS 0x10 +#define TPM_FIFO_REG_INTF_CAPS 0x14 +#define TPM_FIFO_REG_STATUS 0x18 +#define TPM_FIFO_REG_BURST_COUNT_LO 0x19 +#define TPM_FIFO_REG_BURST_COUNT_HI 0x20 +#define TPM_FIFO_REG_DATA_FIFO 0x24 +#define TPM_FIFO_REG_VENDID 0xF00 +#define TPM_FIFO_REG_DEVID 0xF02 +#define TPM_FIFO_REG_REVID 0xF04 + +#define TPM_ST_NO_SESSIONS U(0x8001) +#define TPM_ST_SESSIONS U(0x8002) + +#define TPM_SU_CLEAR U(0x0000) +#define TPM_SU_STATE U(0x0001) + +#define TPM_MIN_AUTH_SIZE 9 +#define TPM_RS_PW 0x40000009 +#define TPM_ZERO_NONCE_SIZE 0 +#define TPM_ATTRIBUTES_DISABLE 0 +#define TPM_ZERO_HMAC_SIZE 0 +#define TPM_SINGLE_HASH_COUNT 1 + + +#define TPM_CMD_STARTUP U(0x0144) +#define TPM_CMD_PCR_READ U(0x017E) +#define TPM_CMD_PCR_EXTEND U(0x0182) + +#define TPM_RESPONSE_SUCCESS U(0x0000) + +#define TPM_ACCESS_ACTIVE_LOCALITY U(1 << 5) +#define TPM_ACCESS_VALID U(1 << 7) +#define TPM_ACCESS_RELINQUISH_LOCALITY U(1 << 5) +#define TPM_ACCESS_REQUEST_USE U(1 << 1) +#define TPM_ACCESS_REQUEST_PENDING U(1 << 2) + +#define TPM_STAT_VALID U(1 << 7) +#define TPM_STAT_COMMAND_READY U(1 << 6) +#define TPM_STAT_GO U(1 << 5) +#define TPM_STAT_AVAIL U(1 << 4) +#define TPM_STAT_EXPECT U(1 << 3) + +#define TPM_READ_HEADER -1 + +#define TPM_HEADER_SIZE 10 +#define MAX_SIZE_CMDBUF 256 +#define MAX_CMD_DATA (MAX_SIZE_CMDBUF - TPM_HEADER_SIZE) + +#pragma pack(1) +typedef struct tpm_cmd_hdr { + uint16_t tag; + uint32_t cmd_size; + uint32_t cmd_code; +} tpm_cmd_hdr; + +typedef struct tpm_cmd { + tpm_cmd_hdr header; + uint8_t data[MAX_CMD_DATA]; +} tpm_cmd; +#pragma pack() + +int tpm_interface_init(struct tpm_chip_data *chip_data, uint8_t locality); + +int tpm_interface_close(struct tpm_chip_data *chip_data, uint8_t locality); + +int tpm_startup(struct tpm_chip_data *chip_data, uint16_t mode); + +int tpm_pcr_extend(struct tpm_chip_data *chip_data, uint32_t index, + uint16_t algorithm, const uint8_t *digest, + uint32_t digest_len); + +#endif /* TPM2_H */ diff --git a/include/drivers/tpm/tpm2_chip.h b/include/drivers/tpm/tpm2_chip.h new file mode 100644 index 000000000..ce052ad98 --- /dev/null +++ b/include/drivers/tpm/tpm2_chip.h @@ -0,0 +1,24 @@ +/* + * Copyright (c) 2025, Arm Limited. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include +#include + +#ifndef TPM2_CHIP_H +#define TPM2_CHIP_H + +#define BYTE U(0x1) +#define WORD U(0x2) +#define DWORD U(0x4) + +struct tpm_chip_data { + uint8_t locality; + unsigned long timeout_msec_a, timeout_msec_b; + unsigned long timeout_msec_c, timeout_msec_d; + uint16_t address; +}; + +#endif /* TPM2_CHIP_H */ diff --git a/include/drivers/tpm/tpm2_interface.h b/include/drivers/tpm/tpm2_interface.h new file mode 100644 index 000000000..6bfbf6ced --- /dev/null +++ b/include/drivers/tpm/tpm2_interface.h @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2025, Arm Limited. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef TPM2_INTERFACE_H +#define TPM2_INTERFACE_H + +#include "tpm2_chip.h" + +typedef struct interface_ops { + int (*get_info)(struct tpm_chip_data *chip_data, uint8_t locality); + int (*send)(struct tpm_chip_data *chip_data, const tpm_cmd *buf); + int (*receive)(struct tpm_chip_data *chip_data, tpm_cmd *buf); + int (*request_access)(struct tpm_chip_data *chip_data, uint8_t locality); + int (*release_locality)(struct tpm_chip_data *chip_data, uint8_t locality); +} interface_ops_t; + +struct interface_ops *tpm_interface_getops(struct tpm_chip_data *chip_data, uint8_t locality); + +int tpm2_fifo_write_byte(uint16_t tpm_reg, uint8_t val); + +int tpm2_fifo_read_byte(uint16_t tpm_reg, uint8_t *val); + +int tpm2_fifo_read_chunk(uint16_t tpm_reg, uint8_t len, void *val); + +#endif /* TPM2_INTERFACE_H */ diff --git a/include/drivers/tpm/tpm2_slb9670/slb9670_gpio.h b/include/drivers/tpm/tpm2_slb9670/slb9670_gpio.h new file mode 100644 index 000000000..59ae125ad --- /dev/null +++ b/include/drivers/tpm/tpm2_slb9670/slb9670_gpio.h @@ -0,0 +1,16 @@ +/* + * Copyright (c) 2025, Arm Limited. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include "drivers/gpio_spi.h" + +#ifndef SLB9670_GPIO_H +#define SLB9670_GPIO_H + +void tpm2_slb9670_reset_chip(struct gpio_spi_data *tpm_gpio_data); + +void tpm2_slb9670_gpio_init(struct gpio_spi_data *tpm_gpio_data); + +#endif /* SLB9670_GPIO_H */ diff --git a/make_helpers/defaults.mk b/make_helpers/defaults.mk index ec2aa1bfb..906e5d706 100644 --- a/make_helpers/defaults.mk +++ b/make_helpers/defaults.mk @@ -192,6 +192,9 @@ endif # Option to build TF with Measured Boot support MEASURED_BOOT := 0 +# Option to build TF with Discrete TPM support +DISCRETE_TPM := 0 + # Option to enable the DICE Protection Environmnet as a Measured Boot backend DICE_PROTECTION_ENVIRONMENT :=0 diff --git a/plat/rpi/common/rpi3_common.c b/plat/rpi/common/rpi3_common.c index 4e3c9f233..490835402 100644 --- a/plat/rpi/common/rpi3_common.c +++ b/plat/rpi/common/rpi3_common.c @@ -75,6 +75,9 @@ static const mmap_region_t plat_rpi3_mmap[] = { #endif MAP_DEVICE0, MAP_FIP, +#if MEASURED_BOOT + RPI3_MAP_BL1_RW, +#endif MAP_NS_DRAM0, #ifdef BL32_BASE MAP_BL32_MEM, diff --git a/plat/rpi/rpi3/include/platform_def.h b/plat/rpi/rpi3/include/platform_def.h index 757c64ad9..eb2914ab4 100644 --- a/plat/rpi/rpi3/include/platform_def.h +++ b/plat/rpi/rpi3/include/platform_def.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015-2024, Arm Limited and Contributors. All rights reserved. + * Copyright (c) 2015-2025, Arm Limited and Contributors. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -171,6 +171,14 @@ #define BL1_RW_BASE (BL1_RW_LIMIT - PLAT_MAX_BL1_RW_SIZE) #define BL1_RW_LIMIT (BL_RAM_BASE + BL_RAM_SIZE) +/* + * In order to access the TCG Event Log in BL2, we need to expose the BL1_RW region + * where the log resides. + */ +#define RPI3_MAP_BL1_RW MAP_REGION_FLAT(BL1_RW_BASE, \ + BL1_RW_LIMIT - BL1_RW_BASE, \ + MT_MEMORY | MT_RW | MT_SECURE) + /* * BL2 specific defines. * @@ -261,4 +269,15 @@ */ #define SYS_COUNTER_FREQ_IN_TICKS ULL(19200000) +/* + * TCG Event Log + */ +#define PLAT_ARM_EVENT_LOG_MAX_SIZE UL(0x400) + +/* + * NT_FW_CONFIG magic dram addr and max size + */ +#define PLAT_RPI3_DTO_BASE ULL(0x11530000) +#define PLAT_RPI3_DTO_MAX_SIZE ULL(0x001000) + #endif /* PLATFORM_DEF_H */ diff --git a/plat/rpi/rpi3/include/rpi3_measured_boot.h b/plat/rpi/rpi3/include/rpi3_measured_boot.h new file mode 100644 index 000000000..67ad47947 --- /dev/null +++ b/plat/rpi/rpi3/include/rpi3_measured_boot.h @@ -0,0 +1,18 @@ +/* + * Copyright (c) 2025, Arm Limited and Contributors. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef RPI3_MEASURED_BOOT_H +#define RPI3_MEASURED_BOOT_H + +#include + +#include + +void rpi3_mboot_fetch_eventlog_info(uint8_t **eventlog_addr, size_t *eventlog_size); + +int rpi3_set_nt_fw_info(size_t log_size, uintptr_t *ns_log_addr); + +#endif /* RPI3_MEASURED_BOOT_H */ diff --git a/plat/rpi/rpi3/platform.mk b/plat/rpi/rpi3/platform.mk index fc51bec67..5297177b4 100644 --- a/plat/rpi/rpi3/platform.mk +++ b/plat/rpi/rpi3/platform.mk @@ -1,5 +1,5 @@ # -# Copyright (c) 2013-2024, Arm Limited and Contributors. All rights reserved. +# Copyright (c) 2013-2025, Arm Limited and Contributors. All rights reserved. # # SPDX-License-Identifier: BSD-3-Clause # @@ -8,7 +8,8 @@ include lib/libfdt/libfdt.mk include lib/xlat_tables_v2/xlat_tables.mk PLAT_INCLUDES := -Iplat/rpi/common/include \ - -Iplat/rpi/rpi3/include + -Iplat/rpi/rpi3/include \ + -Iinclude/lib/libfdt PLAT_BL_COMMON_SOURCES := drivers/ti/uart/aarch64/16550_console.S \ drivers/arm/pl011/aarch64/pl011_console.S \ @@ -20,6 +21,40 @@ PLAT_BL_COMMON_SOURCES := drivers/ti/uart/aarch64/16550_console.S \ plat/rpi/common/rpi3_console_dual.c \ ${XLAT_TABLES_LIB_SRCS} +ifeq (${DISCRETE_TPM},1) +TPM2_MK := drivers/tpm/tpm2.mk +$(info Including ${TPM2_MK}) +include ${TPM2_MK} +endif + +ifeq (${TPM_INTERFACE},FIFO_SPI) +PLAT_BL_COMMON_SOURCES += drivers/gpio/gpio_spi.c \ + drivers/tpm/tpm2_slb9670/slb9670_gpio.c +endif + +ifeq (${MEASURED_BOOT},1) +MEASURED_BOOT_MK := drivers/measured_boot/event_log/event_log.mk +$(info Including ${MEASURED_BOOT_MK}) +include ${MEASURED_BOOT_MK} + +PLAT_BL_COMMON_SOURCES += $(TPM2_SOURCES) \ + ${EVENT_LOG_SOURCES} + +BL1_SOURCES += plat/rpi/rpi3/rpi3_bl1_mboot.c +BL2_SOURCES += plat/rpi/rpi3/rpi3_bl2_mboot.c \ + plat/rpi/rpi3/rpi3_dyn_cfg_helpers.c \ + common/fdt_wrappers.c \ + common/fdt_fixup.c + +CRYPTO_SOURCES := drivers/auth/crypto_mod.c + +BL1_SOURCES += ${CRYPTO_SOURCES} +BL2_SOURCES += ${CRYPTO_SOURCES} + +include drivers/auth/mbedtls/mbedtls_crypto.mk + +endif + BL1_SOURCES += drivers/io/io_fip.c \ drivers/io/io_memmap.c \ drivers/io/io_storage.c \ diff --git a/plat/rpi/rpi3/rpi3_bl1_mboot.c b/plat/rpi/rpi3/rpi3_bl1_mboot.c new file mode 100644 index 000000000..42943658e --- /dev/null +++ b/plat/rpi/rpi3/rpi3_bl1_mboot.c @@ -0,0 +1,137 @@ +/* + * Copyright (c) 2025, Arm Limited. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +/* Event Log data */ +uint8_t event_log[PLAT_ARM_EVENT_LOG_MAX_SIZE]; + +/* RPI3 table with platform specific image IDs, names and PCRs */ +const event_log_metadata_t rpi3_event_log_metadata[] = { + { FW_CONFIG_ID, MBOOT_FW_CONFIG_STRING, PCR_0 }, + { TB_FW_CONFIG_ID, MBOOT_TB_FW_CONFIG_STRING, PCR_0 }, + { BL2_IMAGE_ID, MBOOT_BL2_IMAGE_STRING, PCR_0 }, + + { EVLOG_INVALID_ID, NULL, (unsigned int)(-1) } /* Terminator */ +}; + +#if DISCRETE_TPM +extern struct tpm_chip_data tpm_chip_data; +#if (TPM_INTERFACE == FIFO_SPI) +extern struct gpio_spi_data tpm_rpi3_gpio_data; +struct spi_plat *spidev; +#endif + +static void rpi3_bl1_tpm_early_interface_setup(void) +{ +#if (TPM_INTERFACE == FIFO_SPI) + tpm2_slb9670_gpio_init(&tpm_rpi3_gpio_data); + + tpm2_slb9670_reset_chip(&tpm_rpi3_gpio_data); + + spidev = gpio_spi_init(&tpm_rpi3_gpio_data); +#endif +} +#endif + +void bl1_plat_mboot_init(void) +{ +#if DISCRETE_TPM + int rc; + + rpi3_bl1_tpm_early_interface_setup(); + rc = tpm_interface_init(&tpm_chip_data, 0); + if (rc != 0) { + ERROR("BL1: TPM interface init failed\n"); + panic(); + } + rc = tpm_startup(&tpm_chip_data, TPM_SU_CLEAR); + if (rc != 0) { + ERROR("BL1: TPM Startup failed\n"); + panic(); + } +#endif + + event_log_init(event_log, event_log + sizeof(event_log)); + event_log_write_header(); +} + +void bl1_plat_mboot_finish(void) +{ + size_t event_log_cur_size; + image_desc_t *image_desc; + entry_point_info_t *ep_info; + + event_log_cur_size = event_log_get_cur_size(event_log); + image_desc = bl1_plat_get_image_desc(BL2_IMAGE_ID); + assert(image_desc != NULL); + + /* Get the entry point info */ + ep_info = &image_desc->ep_info; + ep_info->args.arg2 = (uint64_t) event_log; + ep_info->args.arg3 = (uint32_t) event_log_cur_size; + +#if DISCRETE_TPM + int rc; + + /* relinquish control of TPM locality 0 and close interface */ + rc = tpm_interface_close(&tpm_chip_data, 0); + if (rc != 0) { + ERROR("BL1: TPM interface close failed\n"); + panic(); + } +#endif +} + +int plat_mboot_measure_image(unsigned int image_id, image_info_t *image_data) +{ + int rc = 0; + unsigned char hash_data[CRYPTO_MD_MAX_SIZE]; + const event_log_metadata_t *metadata_ptr = rpi3_event_log_metadata; + + rc = event_log_measure(image_data->image_base, image_data->image_size, hash_data); + if (rc != 0) { + return rc; + } + +#if DISCRETE_TPM + rc = tpm_pcr_extend(&tpm_chip_data, 0, TPM_ALG_ID, hash_data, TCG_DIGEST_SIZE); + if (rc != 0) { + ERROR("BL1: TPM PCR-0 extend failed\n"); + panic(); + } +#endif + + while ((metadata_ptr->id != EVLOG_INVALID_ID) && + (metadata_ptr->id != image_id)) { + metadata_ptr++; + } + assert(metadata_ptr->id != EVLOG_INVALID_ID); + + event_log_record(hash_data, EV_POST_CODE, metadata_ptr); + + /* Dump Event Log for user view */ + dump_event_log((uint8_t *)event_log, event_log_get_cur_size(event_log)); + + return rc; +} diff --git a/plat/rpi/rpi3/rpi3_bl2_mboot.c b/plat/rpi/rpi3/rpi3_bl2_mboot.c new file mode 100644 index 000000000..55c69232e --- /dev/null +++ b/plat/rpi/rpi3/rpi3_bl2_mboot.c @@ -0,0 +1,151 @@ +/* + * Copyright (c) 2025, Arm Limited. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include +#include +#include + +#include "./include/rpi3_measured_boot.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* RPI3 table with platform specific image IDs, names and PCRs */ +const event_log_metadata_t rpi3_event_log_metadata[] = { + { BL31_IMAGE_ID, MBOOT_BL31_IMAGE_STRING, PCR_0 }, + { BL33_IMAGE_ID, MBOOT_BL33_IMAGE_STRING, PCR_0 }, + { NT_FW_CONFIG_ID, MBOOT_NT_FW_CONFIG_STRING, PCR_0 }, + + { EVLOG_INVALID_ID, NULL, (unsigned int)(-1) } /* Terminator */ +}; + +#if DISCRETE_TPM +extern struct tpm_chip_data tpm_chip_data; +#if (TPM_INTERFACE == FIFO_SPI) +extern struct gpio_spi_data tpm_rpi3_gpio_data; +struct spi_plat *spidev; +#endif + +static void rpi3_bl2_tpm_early_interface_setup(void) +{ +#if (TPM_INTERFACE == FIFO_SPI) + tpm2_slb9670_gpio_init(&tpm_rpi3_gpio_data); + + spidev = gpio_spi_init(&tpm_rpi3_gpio_data); +#endif +} +#endif + +static uint8_t *event_log_start; +static size_t event_log_size; + +void bl2_plat_mboot_init(void) +{ + uint8_t *bl2_event_log_start; + uint8_t *bl2_event_log_finish; + +#if DISCRETE_TPM + int rc; + + rpi3_bl2_tpm_early_interface_setup(); + rc = tpm_interface_init(&tpm_chip_data, 0); + if (rc != 0) { + ERROR("BL2: TPM interface init failed\n"); + panic(); + } +#endif + + rpi3_mboot_fetch_eventlog_info(&event_log_start, &event_log_size); + bl2_event_log_start = event_log_start + event_log_size; + bl2_event_log_finish = event_log_start + PLAT_ARM_EVENT_LOG_MAX_SIZE; + event_log_init(bl2_event_log_start, bl2_event_log_finish); +} + +void bl2_plat_mboot_finish(void) +{ + int rc; + + /* Event Log address in Non-Secure memory */ + uintptr_t ns_log_addr; + + /* Event Log filled size */ + size_t event_log_cur_size; + + event_log_cur_size = event_log_get_cur_size((uint8_t *)event_log_start); + + /* write the eventlog addr and size to NT_FW_CONFIG TPM entry */ + rc = rpi3_set_nt_fw_info(event_log_cur_size, &ns_log_addr); + if (rc != 0) { + ERROR("%s(): Unable to update %s_FW_CONFIG\n", + __func__, "NT"); + /* + * fatal error due to Bl33 maintaining the assumption + * that the eventlog is successfully passed via + * NT_FW_CONFIG. + */ + panic(); + } + + /* Copy Event Log to Non-secure memory */ + (void)memcpy((void *)ns_log_addr, (const void *)event_log_start, + event_log_cur_size); + + /* Ensure that the Event Log is visible in Non-secure memory */ + flush_dcache_range(ns_log_addr, event_log_cur_size); + + /* Dump Event Log for user view */ + dump_event_log((uint8_t *)event_log_start, event_log_cur_size); + +#if DISCRETE_TPM + /* relinquish control of TPM locality 0 and close interface */ + rc = tpm_interface_close(&tpm_chip_data, 0); + if (rc != 0) { + ERROR("BL2: TPM interface close failed\n"); + panic(); + } +#endif +} + +int plat_mboot_measure_image(unsigned int image_id, image_info_t *image_data) +{ + int rc = 0; + + unsigned char hash_data[CRYPTO_MD_MAX_SIZE]; + const event_log_metadata_t *metadata_ptr = rpi3_event_log_metadata; + + /* Measure the payload with algorithm selected by EventLog driver */ + rc = event_log_measure(image_data->image_base, image_data->image_size, hash_data); + if (rc != 0) { + return rc; + } + +#if DISCRETE_TPM + rc = tpm_pcr_extend(&tpm_chip_data, 0, TPM_ALG_ID, hash_data, TCG_DIGEST_SIZE); + if (rc != 0) { + ERROR("BL2: TPM PCR-0 extend failed\n"); + panic(); + } +#endif + + while ((metadata_ptr->id != EVLOG_INVALID_ID) && + (metadata_ptr->id != image_id)) { + metadata_ptr++; + } + assert(metadata_ptr->id != EVLOG_INVALID_ID); + + event_log_record(hash_data, EV_POST_CODE, metadata_ptr); + + return rc; +} diff --git a/plat/rpi/rpi3/rpi3_bl2_setup.c b/plat/rpi/rpi3/rpi3_bl2_setup.c index 80e4d8d8d..2f57b32a2 100644 --- a/plat/rpi/rpi3/rpi3_bl2_setup.c +++ b/plat/rpi/rpi3/rpi3_bl2_setup.c @@ -1,12 +1,12 @@ /* - * Copyright (c) 2015-2019, ARM Limited and Contributors. All rights reserved. + * Copyright (c) 2015-2025, ARM Limited and Contributors. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ #include -#include +#include "./include/rpi3_measured_boot.h" #include #include @@ -18,6 +18,7 @@ #include #include #include +#include #include @@ -27,6 +28,10 @@ static meminfo_t bl2_tzram_layout __aligned(CACHE_WRITEBACK_GRANULE); /* Data structure which holds the MMC info */ static struct mmc_device_info mmc_info; +/* Variables that hold the eventlog addr and size for use in BL2 Measured Boot */ +static uint8_t *event_log_start; +static size_t event_log_size; + static void rpi3_sdhost_setup(void) { struct rpi3_sdhost_params params; @@ -41,6 +46,12 @@ static void rpi3_sdhost_setup(void) rpi3_sdhost_init(¶ms, &mmc_info); } +void rpi3_mboot_fetch_eventlog_info(uint8_t **eventlog_addr, size_t *eventlog_size) +{ + *eventlog_addr = event_log_start; + *eventlog_size = event_log_size; +} + /******************************************************************************* * BL1 has passed the extents of the trusted SRAM that should be visible to BL2 * in x0. This memory layout is sitting at the base of the free trusted SRAM. @@ -67,6 +78,10 @@ void bl2_early_platform_setup2(u_register_t arg0, u_register_t arg1, /* Setup SDHost driver */ rpi3_sdhost_setup(); + /* populate eventlog addr and size for use in bl2 mboot */ + event_log_start = (uint8_t *)(uintptr_t)arg2; + event_log_size = arg3; + plat_rpi3_io_setup(); } diff --git a/plat/rpi/rpi3/rpi3_dyn_cfg_helpers.c b/plat/rpi/rpi3/rpi3_dyn_cfg_helpers.c new file mode 100644 index 000000000..7c2e6e737 --- /dev/null +++ b/plat/rpi/rpi3/rpi3_dyn_cfg_helpers.c @@ -0,0 +1,198 @@ +/* + * Copyright (c) 2025, ARM Limited and Contributors. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include + +#include +#include +#include +#include +#include + +#define DTB_PROP_HW_LOG_ADDR "tpm_event_log_addr" +#define DTB_PROP_HW_LOG_SIZE "tpm_event_log_size" + +static int rpi3_event_log_fdt_init_overlay(uintptr_t dt_base, int dt_size) +{ + int ret; + int offset; + void *dtb = (void *)dt_base; + + ret = fdt_create_empty_tree(dtb, dt_size); + if (ret < 0) { + ERROR("cannot create empty dtb tree: %s\n", + fdt_strerror(ret)); + return ret; + } + + offset = fdt_path_offset(dtb, "/"); + if (offset < 0) { + ERROR("cannot find root of the tree: %s\n", + fdt_strerror(offset)); + return offset; + } + + offset = fdt_add_subnode(dtb, offset, "fragment@0"); + if (offset < 0) { + ERROR("cannot add fragment node: %s\n", + fdt_strerror(offset)); + return offset; + } + + ret = fdt_setprop_string(dtb, offset, "target-path", "/"); + if (ret < 0) { + ERROR("cannot set target-path property: %s\n", + fdt_strerror(ret)); + return ret; + } + + offset = fdt_add_subnode(dtb, offset, "__overlay__"); + if (offset < 0) { + ERROR("cannot add __overlay__ node: %s\n", + fdt_strerror(offset)); + return ret; + } + + offset = fdt_add_subnode(dtb, offset, "tpm_event_log"); + if (offset < 0) { + ERROR("cannot add tpm_event_log node: %s\n", + fdt_strerror(offset)); + return offset; + } + + ret = fdt_setprop_string(dtb, offset, "compatible", + "arm,tpm_event_log"); + if (ret < 0) { + ERROR("cannot set compatible property: %s\n", + fdt_strerror(ret)); + return ret; + } + + ret = fdt_setprop_u64(dtb, offset, "tpm_event_log_addr", 0); + if (ret < 0) { + ERROR("cannot set tpm_event_log_addr property: %s\n", + fdt_strerror(ret)); + return ret; + } + + ret = fdt_setprop_u32(dtb, offset, "tpm_event_log_size", 0); + if (ret < 0) { + ERROR("cannot set tpm_event_log_size property: %s\n", + fdt_strerror(ret)); + return ret; + } + + return ret; +} + +/* + * Write the Event Log address and its size in the DTB. + * + * This function is supposed to be called only by BL2. + * + * Returns: + * 0 = success + * < 0 = error + */ +static int rpi3_set_event_log_info(uintptr_t config_base, + uintptr_t log_addr, size_t log_size) +{ + /* As libfdt uses void *, we can't avoid this cast */ + void *dtb = (void *)config_base; + /* compatible is set based on the following tpm_tis_spi guidelines from + * https://www.kernel.org/doc/Documentation/devicetree/bindings + * /security/tpm/tpm_tis_spi.txt + */ + const char *compatible_tpm = "arm,tpm_event_log"; + uint64_t base = cpu_to_fdt64(log_addr); + uint32_t sz = cpu_to_fdt32(log_size); + int err, node; + + err = fdt_open_into(dtb, dtb, PLAT_RPI3_DTO_MAX_SIZE); + if (err < 0) { + ERROR("Invalid Device Tree at %p: error %d\n", dtb, err); + return err; + } + + /* + * Verify that the DTB is valid, before attempting to write to it, + * and get the DTB root node. + */ + + /* Check if the pointer to DT is correct */ + err = fdt_check_header(dtb); + if (err < 0) { + WARN("Invalid DTB file passed\n"); + return err; + } + + /* + * Find the TPM node in device tree. + */ + node = fdt_node_offset_by_compatible(dtb, -1, compatible_tpm); + if (node < 0) { + ERROR("The compatible property '%s' not%s", compatible_tpm, + " found in the config\n"); + return node; + } + + err = fdt_setprop(dtb, node, DTB_PROP_HW_LOG_ADDR, &base, 8); + if (err < 0) { + ERROR("Failed to add log addr err %d\n", err); + return err; + } + + err = fdt_setprop(dtb, node, DTB_PROP_HW_LOG_SIZE, &sz, 4); + if (err < 0) { + ERROR("Failed to add log size err %d\n", err); + return err; + } + + err = fdt_pack(dtb); + if (err < 0) { + ERROR("Failed to pack Device Tree at %p: error %d\n", dtb, err); + return err; + } + + /* + * Ensure that the info written to the DTB is visible + * to other images. + */ + flush_dcache_range(config_base, fdt_totalsize(dtb)); + + return err; +} + +/* + * This function writes the Event Log address and its size + * in the RPi3 DTB. + * + * This function is supposed to be called only by BL2. + * + * Returns: + * 0 = success + * < 0 = error + */ +int rpi3_set_nt_fw_info(size_t log_size, uintptr_t *ns_log_addr) +{ + uintptr_t ns_addr; + int err; + + assert(ns_log_addr != NULL); + + ns_addr = PLAT_RPI3_DTO_BASE + PLAT_RPI3_DTO_MAX_SIZE; + + rpi3_event_log_fdt_init_overlay(PLAT_RPI3_DTO_BASE, + PLAT_RPI3_DTO_MAX_SIZE); + + /* Write the Event Log address and its size in the DTB */ + err = rpi3_set_event_log_info(PLAT_RPI3_DTO_BASE, + ns_addr, log_size); + + /* Return Event Log address in Non-secure memory */ + *ns_log_addr = (err < 0) ? 0UL : ns_addr; + return err; +}