From: niuyongwen <niuyongwen(a)hygon.cn>
TDM(Trusted Dynamic Measurement) is a module designed and implemented by
HYGON in its X86 CPU's embeded secure processor, providing dynamical
measurement service to X86 side aiming at memory that needs to be protected,
e.g. the memory area kernel code resides. With this new feature, the goal
of protecting any specified memory dynamically in the runtime can be achieved.
When the protected memory is modified illegally, TDM will detect the event
immediately and give an alarm in the form of an exception, meantime, the
abnormal information is recorded inside the TDM for subsequent audit or remote
attestation.
The TDM driver mainly implements the following functions:
(1) Send the required memory block information and configuration information
to TDM device for protection;
(2) Manage the further distribution of exceptions when TDM detects illegal
memory modification and an exception is triggered.
(3) Record abnormal information for subsequent audit or attestation.
Signed-off-by: niuyongwen <niuyongwen(a)hygon.cn>
Change-Id: I5d86931a6cced6653e3fc4b1976be0d2e1f5a7f5
---
drivers/crypto/ccp/Kconfig | 8 +
drivers/crypto/ccp/Makefile | 2 +
drivers/crypto/ccp/tdm_hygon.c | 1004 ++++++++++++++++++++++++++++++++
drivers/crypto/ccp/tdm_hygon.h | 111 ++++
4 files changed, 1125 insertions(+)
create mode 100644 drivers/crypto/ccp/tdm_hygon.c
create mode 100644 drivers/crypto/ccp/tdm_hygon.h
diff --git a/drivers/crypto/ccp/Kconfig b/drivers/crypto/ccp/Kconfig
index fc9cdfc72706..74a48b453bf7 100644
--- a/drivers/crypto/ccp/Kconfig
+++ b/drivers/crypto/ccp/Kconfig
@@ -52,3 +52,11 @@ config HYGON_PSP2CPU_CMD
depends on CRYPTO_DEV_SP_PSP
help
Hygon PSP2CPU Command Support
+
+config TDM_HYGON
+ tristate "Hygon TDM Interface"
+ default y
+ depends on CRYPTO_DEV_CCP_DD
+ depends on HYGON_PSP2CPU_CMD
+ help
+ Hygon TDM driver
diff --git a/drivers/crypto/ccp/Makefile b/drivers/crypto/ccp/Makefile
index 51d1c0cf66c7..0fdfd913272c 100644
--- a/drivers/crypto/ccp/Makefile
+++ b/drivers/crypto/ccp/Makefile
@@ -19,3 +19,5 @@ ccp-crypto-objs := ccp-crypto-main.o \
ccp-crypto-des3.o \
ccp-crypto-rsa.o \
ccp-crypto-sha.o
+
+obj-$(CONFIG_TDM_HYGON) += tdm_hygon.o
diff --git a/drivers/crypto/ccp/tdm_hygon.c b/drivers/crypto/ccp/tdm_hygon.c
new file mode 100644
index 000000000000..fda129cd7353
--- /dev/null
+++ b/drivers/crypto/ccp/tdm_hygon.c
@@ -0,0 +1,1004 @@
+/*
+ * The Hygon TDM CPU-to-PSP communication driver
+ *
+ * Copyright (C) 2018 Hygon Info Technologies Ltd.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+#include <linux/module.h>
+#include <linux/psp-sev.h>
+#include <linux/spinlock.h>
+#include <linux/rwlock.h>
+#include <linux/err.h>
+#include <asm/current.h>
+#include <linux/kthread.h>
+#include <linux/list.h>
+#include <linux/delay.h>
+#include <linux/kfifo.h>
+#include <linux/scatterlist.h>
+#include <crypto/hash.h>
+#include "tdm_hygon.h"
+
+#define TDM_CMD_ID_MAX 16
+#define TDM2PSP_CMD(id) (0x110 | (id))
+#define TDM_INT_CMD_ID 1
+#define TDM_KFIFO_SIZE 1024
+
+struct context_message {
+ uint32_t context_flag;
+ uint32_t pid;
+ uint8_t comm[16];
+ uint8_t module_name[64];
+};
+
+typedef struct tdm_task_head {
+ struct list_head head;
+ rwlock_t lock;
+} tdm_task_head_t;
+
+typedef struct dynamic_task_node {
+ uint32_t task_id;
+ uint32_t context_flag;
+ measure_exception_handler_t handler;
+ struct list_head list;
+} dynamic_task_node_t;
+
+static tdm_task_head_t dyn_head;
+static unsigned int cmd_id = TDM_INT_CMD_ID;
+static struct task_struct *kthread;
+static DECLARE_KFIFO(kfifo_error_task, unsigned char, TDM_KFIFO_SIZE);
+static spinlock_t kfifo_lock;
+
+#ifdef SEV_API_ADDR_CALL
+typedef int (*psp_do_cmd_t)(int cmd, void *data, int *psp_ret);
+/*(psp_do_cmd_t)0xffffffffc054c940;*/
+psp_do_cmd_t psp_do_cmd;
+static unsigned long psp_do_cmd_addr;
+module_param(psp_do_cmd_addr, ulong, 0444);
+MODULE_PARM_DESC(psp_do_cmd_addr, "Addr of unexported kernel API
psp_do_cmd");
+#endif
+
+enum X86_TO_PSP_CMD {
+ TASK_CREATE = 0x0,
+ TASK_VERIFY_AUTH,
+ TASK_QUERY,
+ TASK_DESTROY,
+ TASK_UPDATE,
+ TASK_STOP,
+ TASK_START,
+ TDM_VERSION
+};
+
+static int list_check_exist(uint32_t task_id)
+{
+ int found = 0;
+ struct list_head *head = NULL;
+ rwlock_t *lock = NULL;
+ dynamic_task_node_t *task_node = NULL, *tmp_node = NULL;
+
+ head = &dyn_head.head;
+ lock = &dyn_head.lock;
+
+ read_lock(lock);
+ list_for_each_entry_safe(task_node, tmp_node, head, list) {
+ if (task_node->task_id == task_id) {
+ found = 1;
+ break;
+ }
+ }
+ read_unlock(lock);
+
+ return found;
+}
+
+static int list_enqueue(void *entry)
+{
+ struct list_head *head, *entry_list = NULL;
+ rwlock_t *lock = NULL;
+
+ if (!entry) {
+ pr_err("Null pointer\n");
+ return -DYN_NULL_POINTER;
+ }
+
+ head = &dyn_head.head;
+ lock = &dyn_head.lock;
+ entry_list = &(((dynamic_task_node_t *)entry)->list);
+
+ write_lock(lock);
+ if (entry_list)
+ list_add_tail(entry_list, head);
+ write_unlock(lock);
+
+ return 0;
+}
+
+static __maybe_unused int list_print(void)
+{
+ struct list_head *head = NULL;
+ rwlock_t *lock = NULL;
+ dynamic_task_node_t *task_node = NULL, *tmp_node = NULL;
+
+ head = &dyn_head.head;
+ lock = &dyn_head.lock;
+
+ read_lock(lock);
+ list_for_each_entry_safe(task_node, tmp_node, head, list) {
+ pr_info("id: %d ", task_node->task_id);
+ }
+ read_unlock(lock);
+ pr_info("\n");
+
+ return 0;
+}
+
+static void __maybe_unused print_buf(uint8_t *buf, int len)
+{
+ int i = 0;
+
+ for (i = 0; i < len; i++) {
+ pr_info("0x%02x ", buf[i]);
+ if ((i + 1)%16 == 0)
+ pr_info("\n");
+ }
+ pr_info("\n");
+}
+
+static int measure_exception_handling_thread(void *data)
+{
+ int ret = 0;
+ int copied = 0;
+ uint32_t error_task_id = 0xffffffff;
+ struct measure_status task_measure_status;
+ struct list_head *head = NULL;
+ rwlock_t *lock = NULL;
+ dynamic_task_node_t *task_node = NULL, *tmp_node = NULL;
+
+ head = &dyn_head.head;
+ lock = &dyn_head.lock;
+
+ pr_info("Thread started for measurement exception handler
dispatching...\n");
+ while (!kthread_should_stop()) {
+ set_current_state(TASK_INTERRUPTIBLE);
+ schedule();
+
+ while (!kfifo_is_empty(&kfifo_error_task)) {
+ copied = kfifo_out_spinlocked(&kfifo_error_task, (unsigned char
*)&error_task_id, sizeof(uint32_t), &kfifo_lock);
+ if (copied != sizeof(uint32_t)) {
+ ret = -DYN_ERR_API;
+ pr_err("kfifio_out exception,return\n");
+ goto end;
+ }
+
+ read_lock(lock);
+ list_for_each_entry_safe(task_node, tmp_node, head, list) {
+ if (task_node->task_id == error_task_id)
+ break;
+ }
+ read_unlock(lock);
+
+ if (!task_node) {
+ ret = -DYN_NULL_POINTER;
+ pr_err("task_node is null,return\n");
+ goto end;
+ }
+
+ if (task_node->task_id == error_task_id) {
+ if (task_node->handler) {
+ pr_info("-----Measurement exception handler dispatching
thread------\n");
+ pr_info("Measurement exception received for task %d\n", error_task_id);
+ pr_info("Step1: Query PSP for task %d status to confirm the error.\n",
error_task_id);
+ pr_info("Step2: Error confirmed, CALL measurement exception
handler.\n");
+ ret = psp_query_measure_status(error_task_id, &task_measure_status);
+ if (ret) {
+ pr_err("task_id %d status query failed\n", error_task_id);
+ goto end;
+ }
+
+ if (task_measure_status.error == MER_ERR) { //error--1 normal--0
+ pr_info("Error detected for task %d, action TODO!\n", error_task_id);
+ pr_info("-------Measurement exception handler----------\n");
+ task_node->handler(error_task_id);
+ pr_info("Exit measurement exception handler.\n");
+ } else {
+ pr_info("No error detected for task %d, please check it again!\n",
error_task_id);
+ }
+ } else {
+ pr_err("task %d's callback function is not registered, please check
it\n", error_task_id);
+ }
+ }
+ }
+ }
+end:
+ return ret;
+}
+
+static int tdm_interrupt_handler(uint32_t id, uint64_t data)
+{
+ if (kthread) {
+ kfifo_in_spinlocked(&kfifo_error_task, (unsigned char *)&data,
sizeof(uint32_t), &kfifo_lock);
+ wake_up_process(kthread);
+ }
+
+ return 0;
+}
+
+static int tdm_do_cmd(unsigned int cmd_id, void *cmd_data, int *error)
+{
+ if (cmd_id >= TDM_CMD_ID_MAX) {
+ pr_err("%s cmd_id %u beyond limit\n", __func__, cmd_id);
+ return -DYN_BEYOND_MAX;
+ }
+
+ return psp_do_cmd(TDM2PSP_CMD(cmd_id), cmd_data, error);
+}
+
+static int calc_task_context_hash(struct context_message context_msg, uint8_t *hash)
+{
+ int ret = 0;
+ struct crypto_shash *shash = NULL;
+
+ if (!hash) {
+ pr_err("%s invalid address\n", __func__);
+ return -DYN_NULL_POINTER;
+ }
+
+ shash = crypto_alloc_shash("sha256", 0, 0);
+ if (IS_ERR(shash)) {
+ pr_err("can't alloc hash\n");
+ return -DYN_ERR_API;
+ }
+
+ {
+ SHASH_DESC_ON_STACK(sdesc, shash);
+
+ sdesc->tfm = shash;
+ sdesc->flags = 0;
+
+ ret = crypto_shash_init(sdesc);
+ if (ret) {
+ pr_err("crypto_shash_init failed\n");
+ ret = -DYN_ERR_API;
+ goto out;
+ }
+
+ if (context_msg.context_flag & CONTEXT_CHECK_PID) {
+ ret = crypto_shash_update(sdesc, (uint8_t *)&context_msg.pid,
sizeof(context_msg.pid));
+ if (ret) {
+ pr_err("crypto_shash_update failed\n");
+ ret = -DYN_ERR_API;
+ goto out;
+ }
+ }
+
+ if (context_msg.context_flag & CONTEXT_CHECK_COMM) {
+ ret = crypto_shash_update(sdesc, context_msg.comm, strlen(context_msg.comm));
+ if (ret) {
+ pr_err("crypto_shash_update failed\n");
+ ret = -DYN_ERR_API;
+ goto out;
+ }
+ }
+
+ if (context_msg.context_flag & CONTEXT_CHECK_MODNAME) {
+ ret = crypto_shash_update(sdesc, context_msg.module_name,
strlen(context_msg.module_name));
+ if (ret) {
+ pr_err("crypto_shash_update failed\n");
+ ret = -DYN_ERR_API;
+ goto out;
+ }
+ }
+
+ ret = crypto_shash_final(sdesc, hash);
+ if (ret) {
+ pr_err("crypto_shash_final failed\n");
+ ret = -DYN_ERR_API;
+ goto out;
+ }
+ }
+
+out:
+ crypto_free_shash(shash);
+ return ret;
+}
+
+int psp_get_fw_info(struct tdm_version *version)
+{
+ int ret = 0;
+ unsigned char *cmd_data;
+ unsigned char *p;
+ int error;
+ uint32_t x86_cmd_type = 0;
+
+ if (!version) {
+ pr_err("version is null pointer\n");
+ return -DYN_NULL_POINTER;
+ }
+
+ cmd_data = kzalloc(PAGE_SIZE, GFP_KERNEL);
+ if (!cmd_data) {
+ pr_err("%s kzalloc for size %ld failed\n", __func__, PAGE_SIZE);
+ return -DYN_ERR_MEM;
+ }
+
+ /*0--create 1--verify_auth 2--query 3--destroy 4--update 5--stop 6-start*/
+ x86_cmd_type = TDM_VERSION;
+ p = cmd_data;
+ memcpy(p, &x86_cmd_type, sizeof(x86_cmd_type));
+
+ ret = tdm_do_cmd(cmd_id, cmd_data, &error);
+ if (ret && ret != -EIO) {
+ pr_err("psp_do_cmd failed cmd 0x%x\n", TDM2PSP_CMD(cmd_id));
+ goto end;
+ }
+
+ if (error) {
+ pr_err("get_fw_info exception error: 0x%x\n", error);
+ ret = -error;
+ goto end;
+ }
+
+ //1B--type 4B--task-id 2B--measure status
+ p = cmd_data;
+ memcpy(version, p, sizeof(struct tdm_version));
+
+end:
+ kfree(cmd_data);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(psp_get_fw_info);
+
+int psp_create_measure_task(struct addr_range_info *range, struct measure_data *data,
uint32_t context_check_flag, struct authcode_2b *code)
+{
+ int ret = 0;
+ unsigned char *cmd_data;
+ unsigned char *p;
+ int error;
+ uint32_t new_task_id = 0;
+ uint32_t addr_range_info_len = 0;
+ uint32_t x86_cmd_type = 0;
+ uint16_t authcode_len = 0;
+ uint8_t context_hash[32] = {0};
+ struct context_message context_msg;
+ struct list_head *head = NULL;
+ dynamic_task_node_t *task_node = NULL;
+ unsigned long return_address = 0;
+ struct module *p_module = NULL;
+
+ if ((!range) || (!data) || (!code)) {
+ pr_err("%s invalid address\n", __func__);
+ return -DYN_NULL_POINTER;
+ }
+
+ if (range->count > RANGE_CNT_MAX) {
+ pr_err("%s range->count %d is beyond RANGE_CNT_MAX %d\n ", __func__,
range->count, RANGE_CNT_MAX);
+ return -DYN_BEYOND_MAX;
+ }
+
+ /*addr_range_info_len--4B addr_info measure_data--40B*/
+ /*authcode_len--2B context_hash--32B*/
+ cmd_data = kzalloc(PAGE_SIZE, GFP_KERNEL);
+ if (!cmd_data) {
+ pr_err("%s kzalloc for size %ld failed\n", __func__, PAGE_SIZE);
+ return -DYN_ERR_MEM;
+ }
+
+ addr_range_info_len = range->count*sizeof(struct addr_info) + sizeof(struct
addr_range_info);
+
+ /*0--create 1--verify_auth 2--query 3--destroy 4--update 5--stop 6-start*/
+ x86_cmd_type = TASK_CREATE;
+ p = cmd_data;
+ memcpy(p, &x86_cmd_type, sizeof(x86_cmd_type));
+ p += sizeof(x86_cmd_type);
+ memcpy(p, range, addr_range_info_len);
+ p += addr_range_info_len;
+ memcpy(p, data, sizeof(struct measure_data));
+ p += sizeof(struct measure_data);
+ authcode_len = code->len > AUTHCODE_MAX ? AUTHCODE_MAX : code->len;
+ memcpy(p, &authcode_len, sizeof(authcode_len));
+ p += sizeof(authcode_len);
+
+ context_msg.context_flag = context_check_flag;
+ context_msg.pid = current->pid;
+ memcpy(context_msg.comm, current->comm, sizeof(current->comm));
+ return_address = (unsigned long)__builtin_return_address(0);
+ p_module = __module_address(return_address);
+ //module
+ if (p_module)
+ memcpy(context_msg.module_name, p_module->name, sizeof(p_module->name));
+ //build-in
+ else
+ memset(context_msg.module_name, 0, sizeof(context_msg.module_name));
+
+ ret = calc_task_context_hash(context_msg, context_hash);
+ if (ret) {
+ pr_err("calc_task_context_hash failed\n");
+ goto end;
+ }
+
+ memcpy(p, context_hash, sizeof(context_hash));
+
+ ret = tdm_do_cmd(cmd_id, cmd_data, &error);
+ if (ret && ret != -EIO) {
+ pr_err("psp_do_cmd failed cmd 0x%x\n", TDM2PSP_CMD(cmd_id));
+ goto end;
+ }
+
+ if (error) {
+ pr_err("create_measure_task exception error: 0x%x\n", error);
+ ret = -error;
+ goto end;
+ }
+
+ //get new assigned id from cmd_data
+ p = cmd_data;
+ memcpy(&x86_cmd_type, p, sizeof(x86_cmd_type));
+ p += sizeof(x86_cmd_type);
+ memcpy(&new_task_id, p, sizeof(new_task_id));
+ p += sizeof(new_task_id);
+ memcpy(&code->len, p, sizeof(code->len));
+ p += sizeof(code->len);
+ code->len = code->len > AUTHCODE_MAX ? AUTHCODE_MAX : code->len;
+ memcpy(&code->val[0], p, code->len);
+
+ head = &dyn_head.head;
+ task_node = kzalloc(sizeof(dynamic_task_node_t), GFP_KERNEL);
+ if (!task_node) {
+ pr_err("%s kzalloc for size %ld failed\n", __func__,
sizeof(dynamic_task_node_t));
+ kfree(cmd_data);
+ return -DYN_ERR_MEM;
+ }
+
+ task_node->task_id = new_task_id;
+ task_node->handler = NULL;
+ task_node->context_flag = context_check_flag;
+
+ ret = list_enqueue(task_node);
+ if (ret) {
+ pr_err("task %d enqueue failed!!!\n", new_task_id);
+ kfree(task_node);
+ goto end;
+ }
+
+ kfree(cmd_data);
+
+ return new_task_id;
+
+end:
+ kfree(cmd_data);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(psp_create_measure_task);
+
+int psp_query_measure_status(uint32_t task_id, struct measure_status *status)
+{
+ int ret = 0;
+ unsigned char *cmd_data;
+ unsigned char *p;
+ int error;
+ uint32_t x86_cmd_type = 0;
+
+ if (!status) {
+ pr_err("status is null pointer\n");
+ return -DYN_NULL_POINTER;
+ }
+
+ if (!list_check_exist(task_id)) {
+ pr_err("task %d isn't created\n", task_id);
+ return -DYN_NOT_EXIST;
+ }
+
+ cmd_data = kzalloc(PAGE_SIZE, GFP_KERNEL);
+ if (!cmd_data) {
+ pr_err("%s kzalloc for size %ld failed\n", __func__, PAGE_SIZE);
+ return -DYN_ERR_MEM;
+ }
+
+ /*0--create 1--verify_auth 2--query 3--destroy 4--update 5--stop 6-start*/
+ x86_cmd_type = TASK_QUERY;
+ p = cmd_data;
+ memcpy(p, &x86_cmd_type, sizeof(x86_cmd_type));
+ p += sizeof(x86_cmd_type);
+ memcpy(p, &task_id, sizeof(task_id));
+
+ ret = tdm_do_cmd(cmd_id, cmd_data, &error);
+ if (ret && ret != -EIO) {
+ pr_err("psp_do_cmd failed cmd 0x%x\n", TDM2PSP_CMD(cmd_id));
+ goto end;
+ }
+
+ if (error) {
+ pr_err("query_measure_task exception error: 0x%x\n", error);
+ ret = -error;
+ goto end;
+ }
+
+ //1B--type 4B--task-id 2B--measure status
+ p = cmd_data;
+ p += sizeof(x86_cmd_type) + sizeof(task_id);
+ memcpy(status, p, sizeof(struct measure_status));
+
+end:
+ kfree(cmd_data);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(psp_query_measure_status);
+
+int psp_register_measure_exception_handler(uint32_t task_id, struct authcode_2b *code,
measure_exception_handler_t handler)
+{
+ int ret = 0;
+ unsigned char *cmd_data;
+ unsigned char *p;
+ int error;
+ uint32_t x86_cmd_type = 0;
+ uint8_t context_hash[32] = {0};
+ struct context_message context_msg;
+ struct list_head *head = NULL;
+ rwlock_t *lock = NULL;
+ dynamic_task_node_t *task_node = NULL, *tmp_node = NULL;
+ unsigned long return_address = 0;
+ struct module *p_module = NULL;
+
+ if (!code) {
+ pr_err("%s invalid authcode\n", __func__);
+ return -DYN_NULL_POINTER;
+ }
+
+ if (code->len > AUTHCODE_MAX) {
+ pr_err("%s authcode len %d is beyond AUTHCODE_MAX %d\n", __func__,
code->len, AUTHCODE_MAX);
+ return -DYN_BEYOND_MAX;
+ }
+
+ if (!list_check_exist(task_id)) {
+ pr_err("task %d isn't created\n", task_id);
+ return -DYN_NOT_EXIST;
+ }
+ //check if task_id is registered already
+ head = &dyn_head.head;
+ lock = &dyn_head.lock;
+
+ read_lock(lock);
+ list_for_each_entry_safe(task_node, tmp_node, head, list) {
+ if (task_node->task_id == task_id) {
+ if ((handler && task_node->handler)) {
+ pr_err("task %d is registered already\n", task_id);
+ read_unlock(lock);
+ return -DYN_EEXIST;
+ }
+ break;//task_node will be used for next context
+ }
+ }
+ read_unlock(lock);
+
+ cmd_data = kzalloc(PAGE_SIZE, GFP_KERNEL);
+ if (!cmd_data) {
+ pr_err("%s kzalloc for size %ld failed\n", __func__, PAGE_SIZE);
+ return -DYN_ERR_MEM;
+ }
+
+ /*0--create 1--verify_auth 2--query 3--destroy 4--update 5--stop 6-start*/
+ x86_cmd_type = TASK_VERIFY_AUTH;
+ p = cmd_data;
+ memcpy(p, &x86_cmd_type, sizeof(x86_cmd_type));
+ p += sizeof(x86_cmd_type);
+ memcpy(p, &task_id, sizeof(task_id));
+ p += sizeof(task_id);
+ memcpy(p, &code->len, sizeof(code->len));
+ p += sizeof(code->len);
+ memcpy(p, code->val, code->len);
+ p += AUTHCODE_MAX;
+
+ context_msg.context_flag = task_node->context_flag;
+ context_msg.pid = current->pid;
+ memcpy(context_msg.comm, current->comm, sizeof(current->comm));
+ return_address = (unsigned long)__builtin_return_address(0);
+
+ p_module = __module_address(return_address);
+ //module
+ if (p_module)
+ memcpy(context_msg.module_name, p_module->name, sizeof(p_module->name));
+ //build-in
+ else
+ memset(context_msg.module_name, 0, sizeof(context_msg.module_name));
+
+ ret = calc_task_context_hash(context_msg, context_hash);
+ if (ret) {
+ pr_err("calc_task_context_hash failed\n");
+ goto end;
+ }
+
+ memcpy(p, context_hash, sizeof(context_hash));
+
+ ret = tdm_do_cmd(cmd_id, cmd_data, &error);
+ if (ret && ret != -EIO) {
+ pr_err("psp_do_cmd failed cmd 0x%x\n", TDM2PSP_CMD(cmd_id));
+ goto end;
+ }
+
+ if (error) {
+ pr_err("register_measure_task exception error: 0x%x\n", error);
+ ret = -error;
+ goto end;
+ }
+
+ write_lock(lock);
+ task_node->handler = handler;
+ write_unlock(lock);
+
+end:
+ kfree(cmd_data);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(psp_register_measure_exception_handler);
+
+int psp_destroy_measure_task(uint32_t task_id, struct authcode_2b *code)
+{
+ int ret = 0;
+ unsigned char *cmd_data;
+ unsigned char *p;
+ int error;
+ uint32_t x86_cmd_type = 0;
+ uint8_t context_hash[32] = {0};
+ struct context_message context_msg;
+ struct list_head *head = NULL;
+ rwlock_t *lock = NULL;
+ dynamic_task_node_t *task_node = NULL, *tmp_node = NULL;
+ unsigned long return_address = 0;
+ struct module *p_module = NULL;
+
+ if (!code) {
+ pr_err("%s invalid authcode\n", __func__);
+ return -DYN_NULL_POINTER;
+ }
+
+ if (code->len > AUTHCODE_MAX) {
+ pr_err("%s authcode len %d is beyond AUTHCODE_MAX %d\n", __func__,
code->len, AUTHCODE_MAX);
+ return -DYN_BEYOND_MAX;
+ }
+
+ if (!list_check_exist(task_id)) {
+ pr_err("task %d isn't created\n", task_id);
+ return -DYN_NOT_EXIST;
+ }
+
+ cmd_data = kzalloc(PAGE_SIZE, GFP_KERNEL);
+ if (!cmd_data) {
+ pr_err("%s kzalloc for size %ld failed\n", __func__, PAGE_SIZE);
+ return -DYN_ERR_MEM;
+ }
+
+ /*0--create 1--verify_auth 2--query 3--destroy 4--update 5--stop 6-start*/
+ x86_cmd_type = TASK_DESTROY;
+ p = cmd_data;
+ memcpy(p, &x86_cmd_type, sizeof(x86_cmd_type));
+ p += sizeof(x86_cmd_type);
+ memcpy(p, &task_id, sizeof(task_id));
+ p += sizeof(task_id);
+ memcpy(p, &code->len, sizeof(code->len));
+ p += sizeof(code->len);
+ memcpy(p, code->val, code->len);
+ p += AUTHCODE_MAX;
+
+ head = &dyn_head.head;
+ lock = &dyn_head.lock;
+
+ read_lock(lock);
+ list_for_each_entry_safe(task_node, tmp_node, head, list) {
+ if (task_node->task_id == task_id)
+ break;
+ }
+ read_unlock(lock);
+
+ context_msg.context_flag = task_node->context_flag;
+ context_msg.pid = current->pid;
+ memcpy(context_msg.comm, current->comm, sizeof(current->comm));
+ return_address = (unsigned long)__builtin_return_address(0);
+
+ p_module = __module_address(return_address);
+ if (p_module)
+ memcpy(context_msg.module_name, p_module->name, sizeof(p_module->name));
+ else
+ memset(context_msg.module_name, 0, sizeof(context_msg.module_name));
+
+ ret = calc_task_context_hash(context_msg, context_hash);
+ if (ret) {
+ pr_err("calc_task_context_hash failed\n");
+ goto end;
+ }
+
+ memcpy(p, context_hash, sizeof(context_hash));
+
+ ret = tdm_do_cmd(cmd_id, cmd_data, &error);
+ if (ret && ret != -EIO) {
+ pr_err("psp_do_cmd failed cmd 0x%x\n", TDM2PSP_CMD(cmd_id));
+ goto end;
+ }
+
+ if (error) {
+ pr_err("destroy_measure_task exception error: 0x%x\n", error);
+ ret = -error;
+ goto end;
+ }
+
+ if (task_node->handler) {
+ write_lock(lock);
+ task_node->handler = NULL;
+ write_unlock(lock);
+ }
+
+ write_lock(lock);
+ list_del(&task_node->list);
+ write_unlock(lock);
+
+ kfree(task_node);
+
+end:
+ kfree(cmd_data);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(psp_destroy_measure_task);
+
+int psp_update_measure_task(uint32_t task_id, struct authcode_2b *code, struct
measure_update_data *data)
+{
+ int ret = 0;
+ unsigned char *cmd_data;
+ unsigned char *p;
+ int error;
+ uint32_t x86_cmd_type = 0;
+ uint8_t context_hash[32] = {0};
+ struct context_message context_msg;
+ struct list_head *head = NULL;
+ rwlock_t *lock = NULL;
+ dynamic_task_node_t *task_node = NULL, *tmp_node = NULL;
+ unsigned long return_address = 0;
+ struct module *p_module = NULL;
+
+ if ((!data) || (!code)) {
+ pr_err("%s invalid address\n", __func__);
+ return -DYN_NULL_POINTER;
+ }
+
+ if (code->len > AUTHCODE_MAX) {
+ pr_err("%s authcode len %d is beyond AUTHCODE_MAX %d\n", __func__,
code->len, AUTHCODE_MAX);
+ return -DYN_BEYOND_MAX;
+ }
+
+ if (!list_check_exist(task_id)) {
+ pr_err("task %d isn't created\n", task_id);
+ return -DYN_NOT_EXIST;
+ }
+
+ cmd_data = kzalloc(PAGE_SIZE, GFP_KERNEL);
+ if (!cmd_data) {
+ pr_err("%s kzalloc for size %ld failed\n", __func__, PAGE_SIZE);
+ return -DYN_ERR_MEM;
+ }
+
+ /*0--create 1--verify_auth 2--query 3--destroy 4--update 5--stop 6-start*/
+ x86_cmd_type = TASK_UPDATE;
+ p = cmd_data;
+ memcpy(p, &x86_cmd_type, sizeof(x86_cmd_type));
+ p += sizeof(x86_cmd_type);
+ memcpy(p, &task_id, sizeof(task_id));
+ p += sizeof(task_id);
+ memcpy(p, &code->len, sizeof(code->len));
+ p += sizeof(code->len);
+ memcpy(p, code->val, code->len);
+ p += AUTHCODE_MAX;//Fixed allocation of 16 bytes of memory for authcode
+
+ head = &dyn_head.head;
+ lock = &dyn_head.lock;
+
+ read_lock(lock);
+ list_for_each_entry_safe(task_node, tmp_node, head, list) {
+ if (task_node->task_id == task_id)
+ break;
+ }
+ read_unlock(lock);
+
+ context_msg.context_flag = task_node->context_flag;
+ context_msg.pid = current->pid;
+ memcpy(context_msg.comm, current->comm, sizeof(current->comm));
+ return_address = (unsigned long)__builtin_return_address(0);
+
+ p_module = __module_address(return_address);
+ if (p_module)
+ memcpy(context_msg.module_name, p_module->name, sizeof(p_module->name));
+ else
+ memset(context_msg.module_name, 0, sizeof(context_msg.module_name));
+
+ ret = calc_task_context_hash(context_msg, context_hash);
+ if (ret) {
+ pr_err("calc_task_context_hash failed\n");
+ goto end;
+ }
+
+ memcpy(p, context_hash, sizeof(context_hash));
+ p += sizeof(context_hash);
+ memcpy(p, data, sizeof(struct measure_update_data));
+
+ ret = tdm_do_cmd(cmd_id, cmd_data, &error);
+ if (ret && ret != -EIO) {
+ pr_err("psp_do_cmd failed cmd 0x%x\n", TDM2PSP_CMD(cmd_id));
+ goto end;
+ }
+
+ if (error) {
+ pr_err("update_measure_task exception error: 0x%x\n", error);
+ ret = -error;
+ goto end;
+ }
+
+end:
+ kfree(cmd_data);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(psp_update_measure_task);
+
+int psp_startstop_measure_task(uint32_t task_id, struct authcode_2b *code, bool start)
+{
+ int ret = 0;
+ unsigned char *cmd_data;
+ unsigned char *p;
+ int error;
+ uint32_t x86_cmd_type = 0;
+ uint8_t context_hash[32] = {0};
+ struct context_message context_msg;
+ struct list_head *head = NULL;
+ rwlock_t *lock = NULL;
+ dynamic_task_node_t *task_node = NULL, *tmp_node = NULL;
+ unsigned long return_address = 0;
+ struct module *p_module = NULL;
+ struct measure_status receive_measure_status;
+
+ if (!code) {
+ pr_err("%s invalid authcode\n", __func__);
+ return -DYN_NULL_POINTER;
+ }
+
+ if (code->len > AUTHCODE_MAX) {
+ pr_err("%s authcode len %d is beyond AUTHCODE_MAX %d\n", __func__,
code->len, AUTHCODE_MAX);
+ return -DYN_BEYOND_MAX;
+ }
+
+ if (!list_check_exist(task_id)) {
+ pr_err("task %d isn't created\n", task_id);
+ return -DYN_NOT_EXIST;
+ }
+
+ cmd_data = kzalloc(PAGE_SIZE, GFP_KERNEL);
+ if (!cmd_data) {
+ pr_err("%s kzalloc for size %ld failed\n", __func__, PAGE_SIZE);
+ return -DYN_ERR_MEM;
+ }
+
+ /*0--create 1--verify_auth 2--query 3--destroy 4--update 5--stop 6-start*/
+ x86_cmd_type = start ? TASK_START : TASK_STOP;
+ p = cmd_data;
+ memcpy(p, &x86_cmd_type, sizeof(x86_cmd_type));
+ p += sizeof(x86_cmd_type);
+ memcpy(p, &task_id, sizeof(task_id));
+ p += sizeof(task_id);
+ memcpy(p, &code->len, sizeof(code->len));
+ p += sizeof(code->len);
+ memcpy(p, code->val, code->len);
+ p += AUTHCODE_MAX;
+
+ head = &dyn_head.head;
+ lock = &dyn_head.lock;
+
+ read_lock(lock);
+ list_for_each_entry_safe(task_node, tmp_node, head, list) {
+ if (task_node->task_id == task_id)
+ break;
+ }
+ read_unlock(lock);
+
+ context_msg.context_flag = task_node->context_flag;
+ context_msg.pid = current->pid;
+ memcpy(context_msg.comm, current->comm, sizeof(current->comm));
+ return_address = (unsigned long)__builtin_return_address(0);
+
+ p_module = __module_address(return_address);
+ if (p_module)
+ memcpy(context_msg.module_name, p_module->name, sizeof(p_module->name));
+ else
+ memset(context_msg.module_name, 0, sizeof(context_msg.module_name));
+
+ ret = calc_task_context_hash(context_msg, context_hash);
+ if (ret) {
+ pr_err("calc_task_context_hash failed\n");
+ goto end;
+ }
+
+ memcpy(p, context_hash, sizeof(context_hash));
+
+ ret = tdm_do_cmd(cmd_id, cmd_data, &error);
+ if (ret && ret != -EIO) {
+ pr_err("psp_do_cmd failed cmd 0x%x\n", TDM2PSP_CMD(cmd_id));
+ goto end;
+ }
+
+ if (error) {
+ pr_err("startstop_measure_task exception error: 0x%x\n", error);
+ ret = -error;
+ goto end;
+ }
+
+ p = cmd_data;
+ p += sizeof(x86_cmd_type) + sizeof(task_id);
+ memcpy(&receive_measure_status, p, sizeof(struct measure_status));
+
+ kfree(cmd_data);
+
+ return receive_measure_status.status;
+
+end:
+ kfree(cmd_data);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(psp_startstop_measure_task);
+
+static int __init _module_init(void)
+{
+ int ret = 0;
+
+ INIT_KFIFO(kfifo_error_task);
+ INIT_LIST_HEAD(&dyn_head.head);
+ rwlock_init(&dyn_head.lock);
+ spin_lock_init(&kfifo_lock);
+
+#ifdef SEV_API_ADDR_CALL
+ pr_info("psp_do_cmd_addr 0x%lx\n", psp_do_cmd_addr);
+ if (!psp_do_cmd_addr) {
+ pr_err("Please specify param: psp_do_cmd_addr\n");
+ return -DYN_NULL_POINTER;
+ }
+
+ psp_do_cmd = (psp_do_cmd_t)psp_do_cmd_addr;
+#endif
+
+ ret = psp_register_cmd_notifier(cmd_id, tdm_interrupt_handler);
+ if (ret) {
+ pr_err("notifier function registration failed\n");
+ return ret;
+ }
+
+ kthread = kthread_create(measure_exception_handling_thread, NULL,
"measure_exception_handling_thread");
+ if (IS_ERR(kthread)) {
+ pr_err("kthread_create fail\n");
+ ret = PTR_ERR(kthread);
+ return ret;
+ }
+
+ wake_up_process(kthread);
+
+ return 0;
+}
+
+static void __exit _module_exit(void)
+{
+ if (kthread) {
+ kthread_stop(kthread);
+ kthread = NULL;
+ }
+
+ psp_unregister_cmd_notifier(cmd_id, tdm_interrupt_handler);
+}
+
+MODULE_AUTHOR("niuyongwen(a)hygon.cn").cn");
+MODULE_LICENSE("GPL");
+MODULE_VERSION("0.1");
+MODULE_DESCRIPTION("The dynamic measure driver");
+
+module_init(_module_init);
+module_exit(_module_exit);
diff --git a/drivers/crypto/ccp/tdm_hygon.h b/drivers/crypto/ccp/tdm_hygon.h
new file mode 100644
index 000000000000..80d9c34e0fd9
--- /dev/null
+++ b/drivers/crypto/ccp/tdm_hygon.h
@@ -0,0 +1,111 @@
+/*
+ * The Hygon TDM CPU-to-PSP communication driver
+ *
+ * Copyright (C) 2018 Hygon Info Technologies Ltd.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+#ifndef __TDM_HYGON_H__
+#define __TDM_HYGON_H__
+
+#include <linux/sched.h>
+
+#define RANGE_CNT_MAX 0x80
+#define MEASURE_TASK_MAX 100
+#define AUTHCODE_MAX 16
+#define AUTH_TRY_DELAY 1
+
+#define HASH_ALGO_SM3 0
+#define HASH_ALGO_SHA1 1
+#define HASH_ALGO_SHA256 2
+#define HASH_ALGO_SHA384 3
+
+#define CONTEXT_CHECK_PID 0x1
+#define CONTEXT_CHECK_COMM 0x2
+#define CONTEXT_CHECK_MODNAME 0x4
+
+#define MEASURE_UPDATE_ALGO 0x1
+#define MEASURE_UPDATE_EXPECTED_MEASUREMENT 0x2
+#define MEASURE_UPDATE_PERIOD 0x4
+
+enum TDM_TASK_STATUS {
+ DYN_INIT = 0x0,
+ DYN_TO_RUN,
+ DYN_RUN,
+ DYN_TO_STOP,
+ DYN_STOP
+};
+
+enum TDM_MEASURE_STATUS {
+ MER_NORMAL = 0x0,
+ MER_ERR
+};
+
+enum DYN_ERROR_TYPE {
+ DYN_NORMAL = 0x0,
+ DYN_NOT_EXIST,
+ DYN_AUTH_FAIL,
+ DYN_STATUS_NOT_SUIT,
+ DYN_BEYOND_MAX,
+ DYN_DA_PERIOD,
+ DYN_NULL_POINTER,
+ DYN_ERR_API,
+ DYN_EEXIST,
+ DYN_ERR_MEM,
+ DYN_ERR_AUTH_LEN
+};
+
+struct addr_info {
+ uint64_t phy_addr;
+ uint64_t length;
+} __attribute__((packed));
+
+struct addr_range_info {
+ uint32_t count;
+ struct addr_info addr[0];
+} __attribute__((packed));
+
+struct measure_data {
+ uint32_t hash_algo;
+ uint8_t expected_measurement[32];
+ uint32_t period_ms;
+} __attribute__((packed));
+
+struct authcode_2b {
+ uint16_t len;
+ uint8_t val[0];
+} __attribute__((packed));
+
+struct measure_status {
+ uint8_t status;
+ uint8_t error;
+ uint8_t count;
+} __attribute__((packed));
+
+struct measure_update_data {
+ uint32_t update_flag;
+ uint32_t algo;
+ uint8_t expected_measurement[32];
+ uint32_t period_ms;
+} __attribute__((packed));
+
+struct tdm_version {
+ uint8_t api_major;
+ uint8_t api_minor;
+ uint32_t buildId;
+ uint32_t task_max;
+ uint32_t range_max_per_task;
+} __attribute__((packed));
+
+typedef int (*measure_exception_handler_t)(uint32_t task_id);
+
+int psp_get_fw_info(struct tdm_version *version);
+int psp_create_measure_task(struct addr_range_info *range, struct measure_data *data,
uint32_t context_check_flag, struct authcode_2b *code);
+int psp_query_measure_status(uint32_t task_id, struct measure_status *status);
+int psp_register_measure_exception_handler(uint32_t task_id, struct authcode_2b *code,
measure_exception_handler_t handler);
+int psp_destroy_measure_task(uint32_t task_id, struct authcode_2b *code);
+int psp_update_measure_task(uint32_t task_id, struct authcode_2b *code, struct
measure_update_data *data);
+int psp_startstop_measure_task(uint32_t task_id, struct authcode_2b *code, bool start);
+#endif /* __TDM_HYGON_H__*/
--
2.17.1