diff options
Diffstat (limited to 'drivers/char/tpm/tpm_crb_ffa.c')
-rw-r--r-- | drivers/char/tpm/tpm_crb_ffa.c | 136 |
1 files changed, 101 insertions, 35 deletions
diff --git a/drivers/char/tpm/tpm_crb_ffa.c b/drivers/char/tpm/tpm_crb_ffa.c index 3169a87a56b6..755b77b32ea4 100644 --- a/drivers/char/tpm/tpm_crb_ffa.c +++ b/drivers/char/tpm/tpm_crb_ffa.c @@ -10,8 +10,16 @@ #define pr_fmt(fmt) "CRB_FFA: " fmt #include <linux/arm_ffa.h> +#include <linux/delay.h> +#include <linux/moduleparam.h> #include "tpm_crb_ffa.h" +static unsigned int busy_timeout_ms = 2000; + +module_param(busy_timeout_ms, uint, 0644); +MODULE_PARM_DESC(busy_timeout_ms, + "Maximum time in ms to retry before giving up on busy"); + /* TPM service function status codes */ #define CRB_FFA_OK 0x05000001 #define CRB_FFA_OK_RESULTS_RETURNED 0x05000002 @@ -38,9 +46,11 @@ * messages. * * All requests with FFA_MSG_SEND_DIRECT_REQ and FFA_MSG_SEND_DIRECT_RESP - * are using the AArch32 SMC calling convention with register usage as - * defined in FF-A specification: - * w0: Function ID (0x8400006F or 0x84000070) + * are using the AArch32 or AArch64 SMC calling convention with register usage + * as defined in FF-A specification: + * w0: Function ID + * -for 32-bit: 0x8400006F or 0x84000070 + * -for 64-bit: 0xC400006F or 0xC4000070 * w1: Source/Destination IDs * w2: Reserved (MBZ) * w3-w7: Implementation defined, free to be used below @@ -68,7 +78,8 @@ #define CRB_FFA_GET_INTERFACE_VERSION 0x0f000001 /* - * Return information on a given feature of the TPM service + * Notifies the TPM service that a TPM command or TPM locality request is + * ready to be processed, and allows the TPM service to process it. * Call register usage: * w3: Not used (MBZ) * w4: TPM service function ID, CRB_FFA_START @@ -105,10 +116,14 @@ struct tpm_crb_ffa { u16 minor_version; /* lock to protect sending of FF-A messages: */ struct mutex msg_data_lock; - struct ffa_send_direct_data direct_msg_data; + union { + struct ffa_send_direct_data direct_msg_data; + struct ffa_send_direct_data2 direct_msg_data2; + }; }; static struct tpm_crb_ffa *tpm_crb_ffa; +static struct ffa_driver tpm_crb_ffa_driver; static int tpm_crb_ffa_to_linux_errno(int errno) { @@ -162,41 +177,85 @@ static int tpm_crb_ffa_to_linux_errno(int errno) */ int tpm_crb_ffa_init(void) { + int ret = 0; + + if (!IS_MODULE(CONFIG_TCG_ARM_CRB_FFA)) { + ret = ffa_register(&tpm_crb_ffa_driver); + if (ret) { + tpm_crb_ffa = ERR_PTR(-ENODEV); + return ret; + } + } + if (!tpm_crb_ffa) - return -ENOENT; + ret = -ENOENT; if (IS_ERR_VALUE(tpm_crb_ffa)) - return -ENODEV; + ret = -ENODEV; - return 0; + return ret; } EXPORT_SYMBOL_GPL(tpm_crb_ffa_init); -static int __tpm_crb_ffa_send_recieve(unsigned long func_id, - unsigned long a0, - unsigned long a1, - unsigned long a2) +static int __tpm_crb_ffa_try_send_receive(unsigned long func_id, + unsigned long a0, unsigned long a1, + unsigned long a2) { const struct ffa_msg_ops *msg_ops; int ret; + msg_ops = tpm_crb_ffa->ffa_dev->ops->msg_ops; + + if (ffa_partition_supports_direct_req2_recv(tpm_crb_ffa->ffa_dev)) { + tpm_crb_ffa->direct_msg_data2 = (struct ffa_send_direct_data2){ + .data = { func_id, a0, a1, a2 }, + }; + + ret = msg_ops->sync_send_receive2(tpm_crb_ffa->ffa_dev, + &tpm_crb_ffa->direct_msg_data2); + if (!ret) + ret = tpm_crb_ffa_to_linux_errno(tpm_crb_ffa->direct_msg_data2.data[0]); + } else { + tpm_crb_ffa->direct_msg_data = (struct ffa_send_direct_data){ + .data1 = func_id, + .data2 = a0, + .data3 = a1, + .data4 = a2, + }; + + ret = msg_ops->sync_send_receive(tpm_crb_ffa->ffa_dev, + &tpm_crb_ffa->direct_msg_data); + if (!ret) + ret = tpm_crb_ffa_to_linux_errno(tpm_crb_ffa->direct_msg_data.data1); + } + + return ret; +} + +static int __tpm_crb_ffa_send_receive(unsigned long func_id, unsigned long a0, + unsigned long a1, unsigned long a2) +{ + ktime_t start, stop; + int ret; + if (!tpm_crb_ffa) return -ENOENT; - msg_ops = tpm_crb_ffa->ffa_dev->ops->msg_ops; + start = ktime_get(); + stop = ktime_add(start, ms_to_ktime(busy_timeout_ms)); - memset(&tpm_crb_ffa->direct_msg_data, 0x00, - sizeof(struct ffa_send_direct_data)); + for (;;) { + ret = __tpm_crb_ffa_try_send_receive(func_id, a0, a1, a2); + if (ret != -EBUSY) + break; - tpm_crb_ffa->direct_msg_data.data1 = func_id; - tpm_crb_ffa->direct_msg_data.data2 = a0; - tpm_crb_ffa->direct_msg_data.data3 = a1; - tpm_crb_ffa->direct_msg_data.data4 = a2; - - ret = msg_ops->sync_send_receive(tpm_crb_ffa->ffa_dev, - &tpm_crb_ffa->direct_msg_data); - if (!ret) - ret = tpm_crb_ffa_to_linux_errno(tpm_crb_ffa->direct_msg_data.data1); + usleep_range(50, 100); + if (ktime_after(ktime_get(), stop)) { + dev_warn(&tpm_crb_ffa->ffa_dev->dev, + "Busy retry timed out\n"); + break; + } + } return ret; } @@ -214,7 +273,7 @@ static int __tpm_crb_ffa_send_recieve(unsigned long func_id, * * Return: 0 on success, negative error code on failure. */ -int tpm_crb_ffa_get_interface_version(u16 *major, u16 *minor) +static int tpm_crb_ffa_get_interface_version(u16 *major, u16 *minor) { int rc; @@ -229,15 +288,19 @@ int tpm_crb_ffa_get_interface_version(u16 *major, u16 *minor) guard(mutex)(&tpm_crb_ffa->msg_data_lock); - rc = __tpm_crb_ffa_send_recieve(CRB_FFA_GET_INTERFACE_VERSION, 0x00, 0x00, 0x00); + rc = __tpm_crb_ffa_send_receive(CRB_FFA_GET_INTERFACE_VERSION, 0x00, 0x00, 0x00); if (!rc) { - *major = CRB_FFA_MAJOR_VERSION(tpm_crb_ffa->direct_msg_data.data2); - *minor = CRB_FFA_MINOR_VERSION(tpm_crb_ffa->direct_msg_data.data2); + if (ffa_partition_supports_direct_req2_recv(tpm_crb_ffa->ffa_dev)) { + *major = CRB_FFA_MAJOR_VERSION(tpm_crb_ffa->direct_msg_data2.data[1]); + *minor = CRB_FFA_MINOR_VERSION(tpm_crb_ffa->direct_msg_data2.data[1]); + } else { + *major = CRB_FFA_MAJOR_VERSION(tpm_crb_ffa->direct_msg_data.data2); + *minor = CRB_FFA_MINOR_VERSION(tpm_crb_ffa->direct_msg_data.data2); + } } return rc; } -EXPORT_SYMBOL_GPL(tpm_crb_ffa_get_interface_version); /** * tpm_crb_ffa_start() - signals the TPM that a field has changed in the CRB @@ -262,7 +325,7 @@ int tpm_crb_ffa_start(int request_type, int locality) guard(mutex)(&tpm_crb_ffa->msg_data_lock); - return __tpm_crb_ffa_send_recieve(CRB_FFA_START, request_type, locality, 0x00); + return __tpm_crb_ffa_send_receive(CRB_FFA_START, request_type, locality, 0x00); } EXPORT_SYMBOL_GPL(tpm_crb_ffa_start); @@ -277,8 +340,9 @@ static int tpm_crb_ffa_probe(struct ffa_device *ffa_dev) tpm_crb_ffa = ERR_PTR(-ENODEV); // set tpm_crb_ffa so we can detect probe failure - if (!ffa_partition_supports_direct_recv(ffa_dev)) { - pr_err("TPM partition doesn't support direct message receive.\n"); + if (!ffa_partition_supports_direct_recv(ffa_dev) && + !ffa_partition_supports_direct_req2_recv(ffa_dev)) { + dev_warn(&ffa_dev->dev, "partition doesn't support direct message receive.\n"); return -EINVAL; } @@ -299,17 +363,17 @@ static int tpm_crb_ffa_probe(struct ffa_device *ffa_dev) rc = tpm_crb_ffa_get_interface_version(&tpm_crb_ffa->major_version, &tpm_crb_ffa->minor_version); if (rc) { - pr_err("failed to get crb interface version. rc:%d", rc); + dev_err(&ffa_dev->dev, "failed to get crb interface version. rc:%d\n", rc); goto out; } - pr_info("ABI version %u.%u", tpm_crb_ffa->major_version, + dev_info(&ffa_dev->dev, "ABI version %u.%u\n", tpm_crb_ffa->major_version, tpm_crb_ffa->minor_version); if (tpm_crb_ffa->major_version != CRB_FFA_VERSION_MAJOR || (tpm_crb_ffa->minor_version > 0 && tpm_crb_ffa->minor_version < CRB_FFA_VERSION_MINOR)) { - pr_err("Incompatible ABI version"); + dev_warn(&ffa_dev->dev, "Incompatible ABI version\n"); goto out; } @@ -341,7 +405,9 @@ static struct ffa_driver tpm_crb_ffa_driver = { .id_table = tpm_crb_ffa_device_id, }; +#ifdef MODULE module_ffa_driver(tpm_crb_ffa_driver); +#endif MODULE_AUTHOR("Arm"); MODULE_DESCRIPTION("TPM CRB FFA driver"); |