LAN966x TFA_CTL

LAN966x TFA_CTL

SoC Resources

Using the PSCI/SMCCC interface available in Linux along with the mchp_tfa_ctl driver, it is possible to control the following features of TFA firmware:

  • Secure JTAG

  • Firmware encryption with BSSK key

Kernel configurations

Following kernel config options should be enabled to use TFA_CTL interface

  • CONFIG_MCHP_TFA_CTL - tfa_ctl driver config option.

The option is enabled by default.

The option will select these related kernel options

  • CONFIG_ARM_PSCI

  • CONFIG_CRYPTO_SHA256

Devicetree Configuration

There is no device tree node for the driver itself, but it requires that the psci node is present.

/ {
	...
	psci {
		compatible = "arm,psci-1.0";
		method = "smc";
	};
	...
};

Using the TFA_CTL driver

First, you should ensure you have booted a Linux kernel with the mchp_tfa_ctl driver. You can do so by grepping the dmesg kernel log and/or querying sysfs:

# dmesg | grep tfa_ctl
[    0.148936] tfa_ctl: PSCI driver v.0.0.0
# ls -l /sys/class/misc/tfa_ctl/
total 0
-r--r--r--    1 root     root          4096 Jan  1 00:01 dev
-rw-r--r--    1 root     root             0 Jan  1 00:01 fw_bind
--w-------    1 root     root             0 Jan  1 00:01 fw_bind_trigger
drwxr-xr-x    2 root     root             0 Jan  1 00:01 power
--w-------    1 root     root            32 Jan  1 00:01 sjtag_key
-r--r--r--    1 root     root             8 Jan  1 00:01 sjtag_status
--w-------    1 root     root             0 Jan  1 00:01 sjtag_unlock
lrwxrwxrwx    1 root     root             0 Jan  1 00:01 subsystem -> ../../../../class/misc
-rw-r--r--    1 root     root          4096 Jan  1 00:01 uevent

Controlling SJTAG

A good place to start is to ensure that SJTAG is enabled. You can do so by reading the sjtag_status file. It contains the two SJTAG registers: CTL and INT_STATUS:

# od -t x4 /sys/class/misc/tfa_ctl/sjtag_status
0000000 0000024d 00000000
0000010

Here, the 0x24d value (b'001001001101') is a typical value for when SJTAG is enabled in mode 1. Refer to the register list for details.

To do a proper unlock, you must provide the (binary) 32-byte SJTAG key in the write-only file sjtag_key. The default value is all zeroes.

After writing the key, you can trigger an unlock by writing (anything) to the sjtag_unlock file, as below. The return value from the write signal the result of the unlock operation.

# cd /tmp
# dd if=/dev/zero of=key count=1 bs=32
1+0 records in
1+0 records out
# dd if=key of=/sys/class/misc/tfa_ctl/sjtag_key
0+1 records in
0+1 records out
# echo 1 > /sys/class/misc/tfa_ctl/sjtag_unlock
#
# od -t x4 /sys/class/misc/tfa_ctl/sjtag_status
0000000 00000847 00000000
0000010

After unlocking, the value 0x847 indicates that the SJTAG is now unlocked. You should now be able to attach a JTAG interface.

Firmware Binding

In this context Firmware Binding descibes the operation of taking a firmware image encrypted with the SSK key, decrypting and re-encrypting with the BSSK instead. As the BSSK is a device-local, unique key, this ensures that the firmware cannot be reproduced. (Note that the BSSK is derived from the HUK key).

The firmware binding process is able to use OTP emulation data, if this is deployed. If not, the binding will use the OTP data directly.

Firmware binding requires the SSK and BSSK for decryption and encryption. These may be any binary value (32 bytes long).

The firmware binding is done using a sysfs file as holding bay for the image, and a sysfs to trigger the actual bind operation.

The commands below illustrate taking an SSK encrypted image off the MMC and re-encrypting with the BSSK key.

# cd /tmp
# dd if=/dev/mmcblk0p1 of=data
sha256sum data
dd if=data of=/sys/class/misc/tfa_ctl/fw_bind bs=1k
echo 1 > /sys/class/misc/tfa_ctl/fw_bind_trigger
dd if=/sys/class/misc/tfa_ctl/fw_bind of=bound bs=1k
2048+0 records in
2048+0 records out
# sha256sum data
sha256sum bound
559a603550297e47a83fa2a9a42c4cb3216e8588d68ae488dc511abc6025ca88  data
# dd if=data of=/sys/class/misc/tfa_ctl/fw_bind bs=1k
1024+0 records in
1024+0 records out
# echo 1 > /sys/class/misc/tfa_ctl/fw_bind_trigger
# dd if=/sys/class/misc/tfa_ctl/fw_bind of=bound bs=1k
1024+0 records in
1024+0 records out
# sha256sum bound
41d6f577e343c81cbd05552c8bc2408e99815d41683e98873acedbe7b8f197ca  bound

As can be seen, the firmware binding will not change the size of the image, but it will change the image data.

After a successful operation, the image may now be written to the boot media of choice.

If the operation fails, the fw_bind_trigger write will fail. The kernel log will provide information about the problem.

# echo 1 > /sys/class/misc/tfa_ctl/fw_bind_trigger
sh: write error: Input/output error
# dmesg | tail -1
[ 3357.425863] misc tfa_ctl: tfa_ctl: Bind failed: 0xffffffec

The error codes are described here:

0xfffffff0

FW_FIP_HDR

Header check of FIP failed

0xffffffef

FW_FIP_ALIGN

FIP needs to be produced with FIP_ALIGN

0xffffffee

FW_FIP_INCOMPLETE

FIP is incomplete (truncated, garbled)

0xffffffed

FW_TOC_TERM_MISSING

FIP does not have a ToC terminator entry

0xffffffec

FW_NOT_SSK_ENCRYPTED

FIP must be encrypted with SSK

0xffffffeb

FW_SSK_FAILURE

Failed to obtain SSK key

0xffffffea

FW_DECRYPT

Failed to decrypt FIP image

0xffffffe9

FW_BSSK_FAILURE

Failed to obtain BSSK key

0xffffffe8

FW_ENCRYPT

Failed to encrypt FIP image

The firmware image use memory resources until purged from the fw_bind sysfs file. The purge is done by writing an empty image to the file.
# dd if=/dev/null of=/sys/class/misc/tfa_ctl/fw_bind
0+0 records in
0+0 records out