diff --git a/drivers/crypto/montage/tsse/Makefile b/drivers/crypto/montage/tsse/Makefile index d67ffde3a5b046c114fb6953bb042265539fb07d..daa61ea213819a6717c5bb213e3d1387a9146e0e 100644 --- a/drivers/crypto/montage/tsse/Makefile +++ b/drivers/crypto/montage/tsse/Makefile @@ -8,6 +8,12 @@ obj-m += tsse.o tsse-objs := tsse_dev_mgr.o \ tsse_ipc.o \ + tsse_ipc_epid.o \ + tsse_ipc_api.o \ + tsse_ipc_setup.o \ + tsse_ipc_drv.o \ + tsse_ipc_service.o \ + tsse_ipc_hash.o \ tsse_fw_service.o \ tsse_service.o \ tsse_irq.o \ diff --git a/drivers/crypto/montage/tsse/tsse_dev.h b/drivers/crypto/montage/tsse/tsse_dev.h index c16d2ae7c414088aa585c698b776f2e9bb4acf03..0a331867ae7eec60c06e47f2c67719398034cedc 100644 --- a/drivers/crypto/montage/tsse/tsse_dev.h +++ b/drivers/crypto/montage/tsse/tsse_dev.h @@ -2,7 +2,7 @@ /* * This file is part of tsse driver for Linux * - * Copyright © 2023 Montage Technology. All rights reserved. + * Copyright © 2023-2024 Montage Technology. All rights reserved. */ #ifndef __TSSE_DEV_H__ @@ -14,7 +14,7 @@ #include #include #include -#include "tsse_ipc.h" +#include "tsse_ipc_setup.h" #define TSSE_PCI_MAX_BARS 4 #define TSSE_FW_VERSION_LEN 32 @@ -37,8 +37,6 @@ enum tsse_dev_status_bit { struct tsse_qpairs_bank { struct tsse_dev *tsse_dev; void __iomem *reg_base; - - u32 num_qparis; u32 irq_vec; }; struct tsse_dev { @@ -57,10 +55,12 @@ struct tsse_dev { struct tsse_ipc *ipc; void *adi; void *mbx_hw; + void *fw_data; const struct firmware *fw; char fw_version[TSSE_FW_VERSION_LEN]; bool fw_version_exist; }; + #define TSSEDEV_TO_DEV(tssedev) (&((tssedev)->tsse_pci_dev.pci_dev->dev)) #define TSSE_DEV_BARS(tssedev) ((tssedev)->tsse_pci_dev.bars) @@ -74,6 +74,10 @@ int tsse_devmgr_add_dev(struct tsse_dev *tsse_dev); void tsse_devmgr_rm_dev(struct tsse_dev *tdev); int tsse_prepare_restart_dev(struct tsse_dev *tdev); int tsse_start_dev(struct tsse_dev *tdev); +struct tsse_dev *tsse_get_dev_by_handle(int handle); + +typedef int (*tsse_dev_process_func)(struct tsse_dev *tdev); +int tsse_process_for_all(tsse_dev_process_func func); static inline struct tsse_dev *pci_to_tsse_dev(struct pci_dev *pci_dev) { @@ -99,4 +103,19 @@ static inline int tsse_dev_in_use(struct tsse_dev *tdev) { return atomic_read(&tdev->ref_count) != 0; } + +static inline void tsse_list_del(struct list_head *entry) +{ + WRITE_ONCE(entry->next->prev, entry->prev); + WRITE_ONCE(entry->prev->next, entry->next); +} +static inline void tsse_list_add(struct list_head *new, struct list_head *prev, + struct list_head *next) +{ + WRITE_ONCE(new->next, next); + WRITE_ONCE(new->prev, prev); + mb(); /* Make sure new node updates first */ + WRITE_ONCE(next->prev, new); + WRITE_ONCE(prev->next, new); +} #endif diff --git a/drivers/crypto/montage/tsse/tsse_dev_drv.c b/drivers/crypto/montage/tsse/tsse_dev_drv.c index 81cbf74ec91ff798d7d12c9e68dfb8eaf63000ee..9ec036df7955e13cc244c0a710d13d4ccd8d9afd 100644 --- a/drivers/crypto/montage/tsse/tsse_dev_drv.c +++ b/drivers/crypto/montage/tsse/tsse_dev_drv.c @@ -2,7 +2,7 @@ /* * This file is part of tsse driver for Linux * - * Copyright © 2023 Montage Technology. All rights reserved. + * Copyright © 2023-2024 Montage Technology. All rights reserved. */ #include @@ -15,7 +15,7 @@ #include "tsse_dev_drv.h" #include "tsse_vuart.h" -#include "tsse_ipc.h" +#include "tsse_ipc_setup.h" #include "tsse_fw_service.h" #define CLUSTER_SLOT_CONFIG_OFFSET 0x5780000 @@ -87,22 +87,20 @@ static int tsse_sriov_configure(struct pci_dev *pdev, int num_vfs_param) if (tdev->num_vfs > 0) { tdev->num_irqs = TSSE_SRIOV_PF_MAX_IRQ_NUM; - tdev->qpairs_bank.num_qparis = TSSE_SRIOV_PF_MAX_QPAIR_NUM; } else { tdev->num_irqs = TSSE_PF_MAX_IRQ_NUM; - tdev->qpairs_bank.num_qparis = TSSE_PF_MAX_QPAIR_NUM; } tsse_dev_info( tdev, - "num_irqs:%u num_qparis:%u qpairs' start irq vector index:%u qpairs' reg base:0x%lx\n", - tdev->num_irqs, tdev->qpairs_bank.num_qparis, + "num_irqs:%u, qpair start irq vector index:%u, qpair reg base:0x%lx\n", + tdev->num_irqs, tdev->qpairs_bank.irq_vec, (ulong)tdev->qpairs_bank.reg_base); ret = tsse_start_dev(tdev); if (ret) { - dev_err(&pdev->dev, "%s %d: failed to start the device\n", - __func__, __LINE__); + dev_err(&pdev->dev, "%s %d: failed to start the device: %d\n", + __func__, __LINE__, ret); return ret; } @@ -123,7 +121,7 @@ static int tsse_sriov_configure(struct pci_dev *pdev, int num_vfs_param) * @buf: string that user writes * @count: string length that user writes * Return: the number of bytes used from the buffer, here it is just the count argument. -*/ + */ static ssize_t tsse_image_load_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { @@ -168,15 +166,31 @@ static int device_probe(struct pci_dev *pdev, const struct pci_device_id *id) return -EINVAL; } + /* Disable ASPM completely as that cause device performence low */ + status = pci_disable_link_state(pdev, PCIE_LINK_STATE_L0S | + PCIE_LINK_STATE_L1); + if (status) { + dev_info(&pdev->dev, + "%s %d: Disable ASPM failed(%d), may cause device performence low.\n", + __func__, __LINE__, status); + } + tdev = kzalloc_node(sizeof(*tdev), GFP_KERNEL, dev_to_node(&pdev->dev)); if (!tdev) return -ENOMEM; + tdev->fw_data = kzalloc_node(TSSE_FIRMWARE_MAX_LENGTH, GFP_KERNEL, dev_to_node(&pdev->dev)); + + if (!tdev->fw_data) { + kfree(tdev); + return -ENOMEM; + } + status = pcim_enable_device(pdev); if (status) { - dev_err(&pdev->dev, "pcim_enable_device failed\n"); + dev_err(&pdev->dev, "pcim_enable_device failed: %d\n", status); goto out_err; } @@ -200,7 +214,7 @@ static int device_probe(struct pci_dev *pdev, const struct pci_device_id *id) status = pcim_iomap_regions(pdev, BIT(0) | BIT(2), TSSE_DEV_NAME); if (status) { - dev_err(&pdev->dev, "I/O memory remapping failed\n"); + dev_err(&pdev->dev, "I/O memory remapping failed: %d\n", status); goto out_err; } @@ -232,7 +246,6 @@ static int device_probe(struct pci_dev *pdev, const struct pci_device_id *id) pci_set_drvdata(pdev, tdev); tdev->num_irqs = TSSE_PF_MAX_IRQ_NUM; - tdev->qpairs_bank.num_qparis = TSSE_PF_MAX_QPAIR_NUM; tdev->qpairs_bank.irq_vec = TSSE_PF_QPAIR_START_IRQ_VECTOR; tdev->qpairs_bank.reg_base = TSSE_DEV_BARS(tdev)[2].virt_addr + TSSE_PF_QPAIR_REG_BASE; @@ -241,8 +254,8 @@ static int device_probe(struct pci_dev *pdev, const struct pci_device_id *id) tsse_dev_info( tdev, - "num_irqs:%u num_qparis:%u qpairs' start irq vector index:%u qpairs' reg base:0x%lx\n", - tdev->num_irqs, tdev->qpairs_bank.num_qparis, + "num_irqs:%u, qpair start irq vector index:%u, qpair reg base:0x%lx\n", + tdev->num_irqs, tdev->qpairs_bank.irq_vec, (ulong)tdev->qpairs_bank.reg_base); if (tsse_devmgr_add_dev(tdev)) { @@ -268,37 +281,41 @@ static int device_probe(struct pci_dev *pdev, const struct pci_device_id *id) tdev->fw_version_exist = true; } - if (tsse_ipc_init(pdev)) { - dev_err(&pdev->dev, - "%s %d: tsse_ipc_init failed to tsse_ipc.\n", __func__, - __LINE__); - status = -EFAULT; - goto out_err_ipc; - } - if (sysfs_create_file(&pdev->dev.kobj, &dev_attr_tsse_image_load.attr)) { dev_err(&pdev->dev, "%s %d: sysfs_create_file failed for tsse image load.\n", __func__, __LINE__); status = -EFAULT; - goto out_err_image_load; + goto out_err_sysfs; } + if (tsse_ipc_init(pdev)) { + dev_err(&pdev->dev, + "%s %d: tsse_ipc_init failed.\n", __func__, + __LINE__); + status = -EFAULT; + goto out_err_ipc; + } tsse_dev_info(tdev, "successful\n"); pci_read_config_dword(pdev, 0x720, &tmp_val); tsse_dev_dbg(tdev, "the value of FILTER_MASK_2_REG is 0x%x\n", tmp_val); return 0; -out_err_image_load: - tsse_ipc_deinit(tdev); out_err_ipc: + if (tdev->fw) { + release_firmware(tdev->fw); + tdev->fw = NULL; + } + sysfs_remove_file(&pdev->dev.kobj, &dev_attr_tsse_image_load.attr); +out_err_sysfs: vuart_uninit_port(pdev); out_err_port_init: tsse_devmgr_rm_dev(tdev); out_err_ida_free: ida_free(&tsse_ida, tdev->id); out_err: + kfree(tdev->fw_data); kfree(tdev); return status; } @@ -311,12 +328,13 @@ static void device_remove(struct pci_dev *pdev) (ulong)pdev, (ulong)tdev); tsse_sriov_disable(tdev); + tsse_ipc_deinit(tdev); if (tdev->fw) { release_firmware(tdev->fw); tdev->fw = NULL; } sysfs_remove_file(&pdev->dev.kobj, &dev_attr_tsse_image_load.attr); - tsse_ipc_deinit(tdev); + kfree(tdev->fw_data); vuart_uninit_port(pdev); tsse_devmgr_rm_dev(tdev); ida_free(&tsse_ida, tdev->id); @@ -378,6 +396,6 @@ module_exit(tsse_exit); MODULE_AUTHOR("montage-tech.com"); MODULE_DESCRIPTION("TSSE device driver"); -MODULE_VERSION("1.0.0"); +MODULE_VERSION("1.1.2"); MODULE_LICENSE("GPL"); MODULE_FIRMWARE(TSSE_FIRMWARE); diff --git a/drivers/crypto/montage/tsse/tsse_dev_mgr.c b/drivers/crypto/montage/tsse/tsse_dev_mgr.c index 39553eb96832380480a138593941c3c0f8bf26fa..91394b9eabd691f8c70f36f22e3019a7562f0827 100644 --- a/drivers/crypto/montage/tsse/tsse_dev_mgr.c +++ b/drivers/crypto/montage/tsse/tsse_dev_mgr.c @@ -2,7 +2,7 @@ /* * This file is part of tsse driver for Linux * - * Copyright © 2023 Montage Technology. All rights reserved. + * Copyright © 2023-2024 Montage Technology. All rights reserved. */ #include @@ -14,26 +14,12 @@ #include #include "tsse_dev.h" #include "tsse_irq.h" +#include "tsse_handle.h" static DEFINE_MUTEX(tsse_dev_table_lock); static LIST_HEAD(tsse_dev_table); static DEFINE_MUTEX(algs_lock); -static inline void tsse_list_del(struct list_head *entry) -{ - WRITE_ONCE(entry->next->prev, entry->prev); - WRITE_ONCE(entry->prev->next, entry->next); -} -static inline void tsse_list_add(struct list_head *new, struct list_head *prev, - struct list_head *next) -{ - WRITE_ONCE(new->next, next); - WRITE_ONCE(new->prev, prev); - mb(); /* Make sure new node updates first */ - WRITE_ONCE(next->prev, new); - WRITE_ONCE(prev->next, new); -} - static int tsse_dev_pf_get(struct tsse_dev *vf_tsse_dev) { int ret = 0; @@ -116,12 +102,10 @@ static int tsse_stop_dev(struct tsse_dev *tdev, bool busy_exit) if (busy_exit) return -EBUSY; } - if (tdev->qpairs_bank.num_qparis != 0) { - mutex_lock(&tsse_dev_table_lock); - tsse_list_del(&tdev->list); - mutex_unlock(&tsse_dev_table_lock); - tsse_dev_info(tdev, "removed from active dev table list\n"); - } + mutex_lock(&tsse_dev_table_lock); + tsse_list_del(&tdev->list); + mutex_unlock(&tsse_dev_table_lock); + tsse_dev_info(tdev, "removed from active dev table list\n"); tsse_dev_info(tdev, "device stopped\n"); @@ -134,12 +118,6 @@ int tsse_start_dev(struct tsse_dev *tdev) struct list_head *prev_node = &tsse_dev_table; int ret = 0; - if (tdev->qpairs_bank.num_qparis == 0) { - set_bit(TSSE_DEV_STATUS_STARTED, &tdev->status); - tsse_dev_info(tdev, "device started\n"); - return 0; - } - set_bit(TSSE_DEV_STATUS_STARTING, &tdev->status); mutex_lock(&tsse_dev_table_lock); @@ -148,7 +126,7 @@ int tsse_start_dev(struct tsse_dev *tdev) if (tmp_dev == tdev) { ret = -EEXIST; tsse_dev_err(tdev, - "The device cannot be added repeatedly\n"); + "The device cannot be added repeatedly\n"); goto clear_status; } } @@ -156,9 +134,9 @@ int tsse_start_dev(struct tsse_dev *tdev) set_bit(TSSE_DEV_STATUS_STARTED, &tdev->status); tsse_list_add(&tdev->list, prev_node, prev_node->next); - tsse_dev_info(tdev, "device started\n"); mutex_unlock(&tsse_dev_table_lock); + tsse_dev_info(tdev, "device started\n"); return 0; clear_status: mutex_unlock(&tsse_dev_table_lock); @@ -199,3 +177,94 @@ struct list_head *tsse_devmgr_get_head(void) { return &tsse_dev_table; } + +/** + * tsse_get_dev_by_handle() - Get TSSE device by its handle + * @handle: handle to TSSE device + * Return: pointer to TSSE device structure if found, otherwise NULL + */ +struct tsse_dev *tsse_get_dev_by_handle(int handle) +{ + struct list_head *itr = NULL; + struct tsse_dev *ptr = NULL; + struct tsse_dev *tdev = NULL; + + mutex_lock(&tsse_dev_table_lock); + list_for_each(itr, &tsse_dev_table) { + ptr = list_entry(itr, struct tsse_dev, list); + if (handle == ptr->id) { + tdev = ptr; + break; + } + } + mutex_unlock(&tsse_dev_table_lock); + + if (!tdev) { + pr_err("%s %d: no such device: %d\n", __func__, __LINE__, handle); + return NULL; + } + return tdev; +} + +/** + * tsse_get_available_handle() - get handle from available device. + * Return: -1 if no available device, otherwise the handle id. + */ +int tsse_get_available_handle(void) +{ + struct list_head *itr = NULL; + struct tsse_dev *tdev = NULL; + + mutex_lock(&tsse_dev_table_lock); + list_for_each(itr, &tsse_dev_table) { + tdev = list_entry(itr, struct tsse_dev, list); + break; + } + mutex_unlock(&tsse_dev_table_lock); + + if (!tdev) { + pr_err("%s(): device not ready\n", __func__); + return -1; + } + return tdev->id; +} +EXPORT_SYMBOL_GPL(tsse_get_available_handle); + +/** + * tsse_get_domain_by_handle() - get IOMMU domain from the handle of device. + * @handle: handle of a TSSE device + * Return: pointer to IOMMU domain of the device if the handle is correct + * and IOMMU enabled, otherwise NULL. + */ +struct iommu_domain *tsse_get_domain_by_handle(int handle) +{ + struct tsse_dev *tdev; + struct pci_dev *pdev; + + if (!iommu_present(&pci_bus_type)) { + pr_err("%s(): IOMMU is not enabled\n", __func__); + return NULL; + } + tdev = tsse_get_dev_by_handle(handle); + if (!tdev) + return NULL; + + pdev = tdev->tsse_pci_dev.pci_dev; + return iommu_get_domain_for_dev(&pdev->dev); +} +EXPORT_SYMBOL_GPL(tsse_get_domain_by_handle); + +int tsse_process_for_all(tsse_dev_process_func func) +{ + struct list_head *itr = NULL; + struct tsse_dev *tdev = NULL; + int rc = 0; + + list_for_each(itr, &tsse_dev_table) { + tdev = list_entry(itr, struct tsse_dev, list); + rc = func(tdev); + if (rc) + break; + } + return rc; +} diff --git a/drivers/crypto/montage/tsse/tsse_fw_service.c b/drivers/crypto/montage/tsse/tsse_fw_service.c index aeada6ec7fa0c04ad0d499db28f262611664bd45..4917955ca13df2d8503d4313b5aec7b026ed096f 100644 --- a/drivers/crypto/montage/tsse/tsse_fw_service.c +++ b/drivers/crypto/montage/tsse/tsse_fw_service.c @@ -2,7 +2,7 @@ /* * This file is part of tsse driver for Linux * - * Copyright © 2023 Montage Technology. All rights reserved. + * Copyright © 2023-2024 Montage Technology. All rights reserved. */ #include @@ -18,37 +18,19 @@ #include #include "tsse_dev.h" +#include "tsse_ipc.h" #include "tsse_service.h" +#include "tsse_fw_service.h" #define SEARCH_PATTERN "MT_CFG_BUILD_VERSION_DETAIL" #define SPACE_CH ' ' -static int fw_send_msg(struct tsse_ipc *tsseipc, struct ipc_msg *msg) +static int fw_send_msg(struct tsse_dev *tdev, struct fw_load *fw_task) { - u8 *h2d; - u32 int_reg; + struct tsse_ipc *tsseipc = tdev->ipc; - mutex_lock(&tsseipc->list_lock); - - int_reg = readl(tsseipc->virt_addr + HOST2MAIN_INTR_SET_OFFSET); - if ((int_reg & IPC_REGISTER_INT_SET) != 0) { - mutex_unlock(&tsseipc->list_lock); - return -EFAULT; - } - if (msg->header.i_len < sizeof(struct ipc_header) + - sizeof(struct msg_info) + sizeof(struct fw_load)) { - dev_err(tsseipc->dev, "msg format error\n"); - return -EFAULT; - } - h2d = (u8 *)(tsseipc->virt_addr + HOST2MAIN_IPC_OFFSET); - memcpy_toio(h2d, msg, sizeof(struct ipc_header)); - memcpy_toio(h2d + sizeof(struct ipc_header), (u8 *)msg->i_data, - msg->header.i_len - sizeof(struct ipc_header)); - writel(0x1, tsseipc->virt_addr + HOST2MAIN_INTR_SET_OFFSET); - - dev_info(tsseipc->dev, "notify device to get firmware\n"); - mutex_unlock(&tsseipc->list_lock); - return 0; + dev_dbg(tsseipc->dev, "notify device\n"); + return ipc_h2d_msg_send_legacy(tdev->id, IPC_MESSAGE_BOOT, fw_task, sizeof(struct fw_load)); } /** @@ -56,7 +38,7 @@ static int fw_send_msg(struct tsse_ipc *tsseipc, struct ipc_msg *msg) * @fw: firmware pointer * @fw_version_out: firmware version string output * Return: 0 on success, error code otherwise -*/ + */ int get_firmware_version(const struct firmware *fw, char *fw_version_out) { const char *pattern = SEARCH_PATTERN; @@ -103,42 +85,46 @@ int get_firmware_version(const struct firmware *fw, char *fw_version_out) } /** - * fw_service() - Firmware service to handle IPC message from mainCPU. + * fw_service() - Firmware service to handle IPC message from device. * It will write init or manual load firmware to PCIe BAR and send message back. - * @tsseipc_t: pointer to a structure used for IPC - * @msg_t: pointer to IPC message -*/ -void fw_service(void *tsseipc_t, void *msg_t) + * @handle: handle to TSSE device + * @msg_payload: pointer to IPC message payload + * @length: length of the msg_payload + * Return: 0 on success, error code otherwise + */ +int fw_service(int handle, void *msg_payload, uint32_t length) { void __iomem *fw; - uint32_t size; - uint32_t task_offset; - struct fw_load *fw_task; struct tsse_dev *tdev; - struct tsse_ipc *tsseipc = (struct tsse_ipc *)tsseipc_t; - struct ipc_msg *msg = (struct ipc_msg *)msg_t; + struct tsse_ipc *tsseipc; + struct fw_load *fw_task; - task_offset = sizeof(struct msg_info); - fw_task = (struct fw_load *)((uint8_t *)msg->i_data + task_offset); - tdev = pci_to_tsse_dev(tsseipc->pdev); + if (!msg_payload || !length) { + pr_err("%s %d: invalid input parameter\n", __func__, __LINE__); + return -EINVAL; + } + tdev = tsse_get_dev_by_handle(handle); + if (!tdev) + return -ENODEV; - if (!tdev || !tdev->fw) { + tsseipc = tdev->ipc; + fw_task = (struct fw_load *) msg_payload; + if (!tdev->fw) { fw_task->result = 1; fw_task->size = 0; dev_info(tsseipc->dev, "firmware loading failed\n"); - if (fw_send_msg(tsseipc, msg)) + if (fw_send_msg(tdev, fw_task)) dev_err(tsseipc->dev, "notify device failed\n"); - return; + return -ENOENT; } fw_task->result = 0; fw_task->size = tdev->fw->size; - size = tdev->fw->size; fw = tsseipc->virt_addr + fw_task->offset + FW_BASE; - memcpy_toio((u8 *)fw, tdev->fw->data, size); + memcpy_toio((u8 *)fw, tdev->fw->data, tdev->fw->size); dev_info(tsseipc->dev, "firmware loading done\n"); - if (fw_send_msg(tsseipc, msg)) + if (fw_send_msg(tdev, fw_task)) dev_err(tsseipc->dev, "notify device failed\n"); if (tdev->fw_version_exist) @@ -150,6 +136,7 @@ void fw_service(void *tsseipc_t, void *msg_t) memset(tdev->fw_version, 0, TSSE_FW_VERSION_LEN); tdev->fw_version_exist = false; } + return 0; } /** @@ -158,13 +145,15 @@ void fw_service(void *tsseipc_t, void *msg_t) * @name: firmware file name * @fw: pointer to firmware pointer * Return: 0 on success, error code otherwise -*/ + */ int tsse_fw_load(struct pci_dev *pdev, const char *name, const struct firmware **fw) { int result; + struct tsse_dev *tdev = pci_to_tsse_dev(pdev); - result = request_firmware(fw, name, &pdev->dev); + result = request_firmware_into_buf(fw, name, &pdev->dev, + tdev->fw_data, TSSE_FIRMWARE_MAX_LENGTH); if (result) - dev_err(&pdev->dev, "%s failed for %s\n", __func__, name); + dev_err(&pdev->dev, "%s failed for %s: %d\n", __func__, name, result); return result; } diff --git a/drivers/crypto/montage/tsse/tsse_fw_service.h b/drivers/crypto/montage/tsse/tsse_fw_service.h index 706ea6d297696cf1e49e51bc1150f63a141af1e0..24a209350710e52219465db532c4f1c623824a7c 100644 --- a/drivers/crypto/montage/tsse/tsse_fw_service.h +++ b/drivers/crypto/montage/tsse/tsse_fw_service.h @@ -2,7 +2,7 @@ /* * This file is part of tsse driver for Linux * - * Copyright © 2023 Montage Technology. All rights reserved. + * Copyright © 2023-2024 Montage Technology. All rights reserved. */ #ifndef __TSSE_FW_SERVICE_H__ @@ -12,8 +12,9 @@ #define FW_BASE 0x7000000 #define TSSE_FIRMWARE "tsse_firmware.bin" +#define TSSE_FIRMWARE_MAX_LENGTH (1024 * 1024) -void fw_service(void *tsseipc_t, void *msg_t); +int fw_service(int handle, void *msg_payload, uint32_t length); int tsse_fw_load(struct pci_dev *pdev, const char *name, const struct firmware **fw); int get_firmware_version(const struct firmware *fw, char *fw_version_out); #endif diff --git a/drivers/crypto/montage/tsse/tsse_handle.h b/drivers/crypto/montage/tsse/tsse_handle.h new file mode 100644 index 0000000000000000000000000000000000000000..d18cc5bf0e0add4b1f295783698835d5247e90b9 --- /dev/null +++ b/drivers/crypto/montage/tsse/tsse_handle.h @@ -0,0 +1,16 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * This file is part of tsse driver for Linux + * + * Copyright © 2023-2024 Montage Technology. All rights reserved. + */ + +#ifndef __TSSE_HDNDLE_H__ +#define __TSSE_HDNDLE_H__ + +#include +#include + +int tsse_get_available_handle(void); +struct iommu_domain *tsse_get_domain_by_handle(int handle); +#endif diff --git a/drivers/crypto/montage/tsse/tsse_ipc.c b/drivers/crypto/montage/tsse/tsse_ipc.c index c03d5209551b3972f56062ae17f194af4e9a5dfa..a34330489237951323118d214315d4a72f51f2be 100644 --- a/drivers/crypto/montage/tsse/tsse_ipc.c +++ b/drivers/crypto/montage/tsse/tsse_ipc.c @@ -2,7 +2,7 @@ /* * This file is part of tsse driver for Linux * - * Copyright © 2023 Montage Technology. All rights reserved. + * Copyright © 2023-2024 Montage Technology. All rights reserved. */ #include @@ -11,6 +11,8 @@ #include #include "tsse_ipc.h" +#include "tsse_ipc_setup.h" +#include "tsse_ipc_drv.h" #include "tsse_dev.h" #include "tsse_service.h" @@ -19,7 +21,7 @@ * Return the pointer to ipc_msg, the caller is responsible for free it. * @d2h: device2host memory pointer * Return: new ipc_msg pointer, which points to message read from device -*/ + */ static struct ipc_msg *get_msginf(void __iomem *d2h) { uint32_t u_len = 0; @@ -29,15 +31,15 @@ static struct ipc_msg *get_msginf(void __iomem *d2h) struct ipc_header *ipc_info = (struct ipc_header *)d2h; // The memory layout in d2h should at least contains: - // ipc_header, msg_info and fw_load (message body) + // ipc_header, msg_info if (ipc_info->i_len < sizeof(struct ipc_header) + - sizeof(struct msg_info) + sizeof(struct fw_load)) { + sizeof(struct msg_info)) { pr_info("%s(): msg format error\n", __func__); return NULL; } u_len = ipc_info->i_len - sizeof(struct ipc_header); msg = (struct ipc_msg *)(kzalloc(sizeof(struct ipc_msg) + u_len, - GFP_ATOMIC)); + GFP_ATOMIC)); if (!msg) { pr_info("%s(): ipc_msg kzalloc failed\n", __func__); return NULL; @@ -53,164 +55,96 @@ static struct ipc_msg *get_msginf(void __iomem *d2h) return msg; } -static irqreturn_t tsse_ipc_d2h_irqhandler(int irq, void *dev_id) -{ - struct tsse_ipc *tsseipc = (struct tsse_ipc *)dev_id; - - writel(0x0, tsseipc->virt_addr + MAIN2HOST_INTR_SET_OFFSET); - tasklet_hi_schedule(&tsseipc->ipc_handle); - dev_err(tsseipc->dev, "irq%d\n", irq); - return IRQ_HANDLED; -} - -bool check_send_enbit(struct tsse_ipc *tsseipc) -{ - u32 int_reg; - - int_reg = readl(tsseipc->virt_addr + HOST2MAIN_INTR_SET_OFFSET); - if ((int_reg & IPC_REGISTER_INT_SET) == 0) - return true; - else - return false; -} - -void notify_device(struct tsse_ipc *tsseipc) -{ - writel(0x1, tsseipc->virt_addr + HOST2MAIN_INTR_SET_OFFSET); - return; - -} - /** - * ipc_hw_init()- Enable main2host interrupt, cleanup interrupt - * set value in host2main and main2host. - * @hw_ipc: pointer to a structure used for IPC -*/ -static void ipc_hw_init(struct tsse_ipc *hw_ipc) -{ - writel(0x1, hw_ipc->virt_addr + MAIN2HOST_INTR_ENABLE_OFFSET); - writel(0x0, hw_ipc->virt_addr + HOST2MAIN_INTR_SET_OFFSET); - writel(0x0, hw_ipc->virt_addr + MAIN2HOST_INTR_SET_OFFSET); -} - -static int ipc_init_msg(struct tsse_ipc *tsseipc) + * tsse_write_msg() - do write msg from host to device + * @tsseipc: pointer to structure used for IPC in current device + * @msg_class: type for the IPC message + * @msg_payload: pointer to actual content that caller wants to send + * @payload_length: length of msg_payload + * Return: 0 on success, error code otherwise + */ +static int tsse_write_msg(struct tsse_ipc *tsseipc, uint32_t msg_class, + void *msg_payload, uint32_t payload_length) { u8 *h2d; u32 int_reg; - u32 cmd_len; - u32 i_len; + u32 comm_msg_length; struct ipc_msg *msg; struct msg_info *info_msg; - cmd_len = sizeof(uint32_t); - i_len = sizeof(struct ipc_header) + sizeof(struct msg_info) + cmd_len; - msg = (struct ipc_msg *)(kzalloc(i_len, GFP_ATOMIC)); + comm_msg_length = sizeof(struct ipc_header) + sizeof(struct msg_info); + msg = (struct ipc_msg *)(kzalloc(comm_msg_length, GFP_ATOMIC)); if (!msg) { pr_info("%s(): msg kzalloc failed\n", __func__); - return -EFAULT; + return -ENOMEM; } - msg->header.i_len = i_len; + msg->header.i_len = comm_msg_length + payload_length; info_msg = (struct msg_info *)msg->i_data; - info_msg->msg_class = IPC_MESSAGE_BASIC; - *(uint32_t *)((uint8_t *)msg->i_data + sizeof(struct msg_info)) = IPC_BASIC_CMD_HOST_INIT; + info_msg->msg_class = msg_class; mutex_lock(&tsseipc->list_lock); int_reg = readl(tsseipc->virt_addr + HOST2MAIN_INTR_SET_OFFSET); if ((int_reg & IPC_REGISTER_INT_SET) != 0) { mutex_unlock(&tsseipc->list_lock); kfree(msg); - return -EFAULT; + return -EAGAIN; } h2d = (u8 *)(tsseipc->virt_addr + HOST2MAIN_IPC_OFFSET); - memcpy_toio(h2d, msg, sizeof(struct ipc_header)); - memcpy_toio(h2d + sizeof(struct ipc_header), (u8 *)msg->i_data, - sizeof(struct msg_info) + sizeof(uint32_t)); + ipc_memcpy_to_io(h2d, (u8 *)msg, comm_msg_length); + ipc_memcpy_to_io(h2d + comm_msg_length, (u8 *)msg_payload, payload_length); writel(0x1, tsseipc->virt_addr + HOST2MAIN_INTR_SET_OFFSET); mutex_unlock(&tsseipc->list_lock); kfree(msg); - return 0; } -static void tsse_ipc_bh_handler(unsigned long data) +int ipc_d2h_legacy_msg_process(struct tsse_ipc *tsseipc, void __iomem *d2h_msg) { - struct tsse_ipc *tsseipc = (struct tsse_ipc *)data; - - void __iomem *d2h_payload = tsseipc->virt_addr + MAIN2HOST_IPC_OFFSET; - struct ipc_msg *msg = get_msginf(d2h_payload); + int ret; + struct ipc_msg *msg = get_msginf(d2h_msg); if (!msg) { dev_err(tsseipc->dev, "get_msginf is NULL\n"); - return; - } - if (service_rout(tsseipc, msg)) - dev_err(tsseipc->dev, "illegal message class\n"); - kfree(msg); -} - -int tsse_ipc_init(struct pci_dev *pdev) -{ - struct tsse_dev *tdev = pci_to_tsse_dev(pdev); - struct tsse_ipc *ipc; - int rc; - - ipc = devm_kzalloc(&pdev->dev, sizeof(*ipc), GFP_KERNEL); - if (ipc == NULL) return -ENOMEM; - tdev->ipc = ipc; - ipc->pdev = pdev; - ipc->dev = &pdev->dev; - ipc->virt_addr = TSSE_DEV_BARS(tdev)[2].virt_addr; - - mutex_init(&ipc->list_lock); - tasklet_init(&(ipc->ipc_handle), tsse_ipc_bh_handler, - (ulong)(ipc)); - - rc = request_threaded_irq(pci_irq_vector(pdev, 0), NULL, - tsse_ipc_d2h_irqhandler, IRQF_SHARED, - "pf-ipc", ipc); - if (rc) { - dev_err(&pdev->dev, "request_threaded_irq failed\n"); - return rc; - } - ipc_hw_init(ipc); - rc = ipc_init_msg(ipc); - if (rc) { - dev_err(&pdev->dev, "ipc_init_msg failed\n"); - tsse_ipc_deinit(tdev); } - return rc; + ret = service_rout(tsseipc, msg); + kfree(msg); + return ret; } -void tsse_ipc_deinit(void *tdev_t) +/** + * ipc_h2d_msg_send_legacy() - send message from host to device + * @handle: handle to TSSE device + * @msg_class: type for the IPC message + * @msg_payload: pointer to actual content that caller wants to send + * @length: length of msg_payload + * Return: 0 on success, error code otherwise + */ +int ipc_h2d_msg_send_legacy(int handle, uint32_t msg_class, + void *msg_payload, uint32_t length) { - struct tsse_ipc *tsseipc; - struct pci_dev *pdev; struct tsse_dev *tdev; + struct tsse_ipc *tsseipc; + tsse_d2h_ipc_handler ipc_handler; - tdev = tdev_t; - tsseipc = tdev->ipc; - pdev = tsseipc->pdev; - if (tsseipc) { - free_irq(pci_irq_vector(pdev, 0), tdev->ipc); - tdev->ipc = NULL; + tdev = tsse_get_dev_by_handle(handle); + if (!tdev) + return -ENODEV; + + if (!msg_payload || !length) { + pr_err("%s %d: invalid msg payload\n", __func__, __LINE__); + return -EINVAL; } -} -int tsse_fw_manual_load_ipc(struct pci_dev *pdev) -{ - struct tsse_dev *tdev = pci_to_tsse_dev(pdev); - struct tsse_ipc *ipc = tdev->ipc; - int rc = -EFAULT; - - if (ipc) { - ipc_hw_init(ipc); - rc = ipc_init_msg(ipc); - if (rc) - dev_err(&pdev->dev, "ipc_init_msg failed\n"); + tsseipc = tdev->ipc; + ipc_handler = tsseipc->d2h_handlers[msg_class]; + if ((msg_class >= IPC_MESSAGE_CLASS_NUM) || + (msg_class != IPC_MESSAGE_BASIC && !ipc_handler)) { + pr_err("%s %d: invalid msg class\n", __func__, __LINE__); + return -EINVAL; } - return rc; + return tsse_write_msg(tsseipc, msg_class, msg_payload, length); } diff --git a/drivers/crypto/montage/tsse/tsse_ipc.h b/drivers/crypto/montage/tsse/tsse_ipc.h index 82f8df71c98371de379f57b3cba1b936309fbec7..0f247333cf84c98d8d389bf854903437712ea73c 100644 --- a/drivers/crypto/montage/tsse/tsse_ipc.h +++ b/drivers/crypto/montage/tsse/tsse_ipc.h @@ -2,7 +2,7 @@ /* * This file is part of tsse driver for Linux * - * Copyright © 2023 Montage Technology. All rights reserved. + * Copyright © 2023-2024 Montage Technology. All rights reserved. */ #ifndef __TM_HOST_IPC_H__ @@ -11,45 +11,7 @@ #include #include #include - -#define TSSE_PASID_SVA - -#define HOST2MAIN_INTR_SET_OFFSET 0x2000 -#define HOST2MAIN_INTR_ENABLE_OFFSET 0x2004 -#define HOST2MAIN_ACK_INTR_CLR_OFFSET 0x2008 -#define HOST2MAIN_ACK_INTR_ENABLE_OFFSET 0x200c -#define HOST2MAIN_VLD_INTR_STATUS_OFFSET 0x2010 -#define HOST2MAIN_ACK_INTR_STATUS_OFFSET 0x2014 -#define MSIX_MASK_EN_REG_OFFSET 0x2020 -#define INTR_MASK_BIT_OFFSET 0x2024 -#define INTR_PENDING_BIT_OFFSET 0x2028 -#define HOST2MAIN_IPC_OFFSET 0x2400 - -#define MAIN2HOST_INTR_SET_OFFSET 0x3000 -#define MAIN2HOST_INTR_ENABLE_OFFSET 0x3004 -#define MAIN2HOST_ACK_INTR_CLR_OFFSET 0x3008 -#define MAIN2HOST_ACK_INTR_ENABLE_OFFSET 0x300c -#define MAIN2HOST_VEN_MSI_FUNC_NUM_OFFSET 0x3010 -#define MAIN2HOST_VEN_MSI_VFUNC_ACTIVE_OFFSET 0x3014 -#define MAIN2HOST_IPC_OFFSET 0x3400 - -#define IPC_REGISTER_INT_SET BIT(0) -#define IPC_REGISTER_INT_MASK BIT(1) - -enum IPC_BASIC_CMD { - IPC_BASIC_CMD_HOST_INIT = 0x1, - IPC_BASIC_CMD_PING = 0x2 -}; - -enum IPC_BOOT_CMD { - IPC_BOOT_CMD_GET_FIRMWARE = 0x1 -}; - -enum IPC_MESSAGE_CLASS { - IPC_MESSAGE_BASIC = 1, - IPC_MESSAGE_BOOT, - IPC_MESSAGE_CLASS_NUM, -}; +#include "tsse_ipc_setup.h" struct ipc_header { uint32_t inst_id; @@ -87,17 +49,7 @@ struct ipc_layout { struct msg_info info; }; -struct tsse_ipc { - struct device *dev; - struct pci_dev *pdev; - void __iomem *virt_addr; - struct mutex list_lock; - struct tasklet_struct ipc_handle; -}; +int ipc_h2d_msg_send_legacy(int handle, uint32_t msg_class, void *msg_payload, uint32_t length); +int ipc_d2h_legacy_msg_process(struct tsse_ipc *tsseipc, void *msg); -int tsse_ipc_init(struct pci_dev *pdev); -void tsse_ipc_deinit(void *tdev); -int tsse_fw_manual_load_ipc(struct pci_dev *pdev); -bool check_send_enbit(struct tsse_ipc *tsseipc); -void notify_device(struct tsse_ipc *tsseipc); #endif diff --git a/drivers/crypto/montage/tsse/tsse_ipc_api.c b/drivers/crypto/montage/tsse/tsse_ipc_api.c new file mode 100644 index 0000000000000000000000000000000000000000..1c3eadbbd02939b4246baefc11a2200b933fe036 --- /dev/null +++ b/drivers/crypto/montage/tsse/tsse_ipc_api.c @@ -0,0 +1,111 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * This file is part of tsse driver for Linux + * + * Copyright © 2023-2024 Montage Technology. All rights reserved. + */ +#include +#include +#include "tsse_ipc_msg.h" +#include "tsse_dev.h" +#include "tsse_ipc_hash.h" +#include "tsse_ipc_service.h" + +/** + * tsse_im_startup() - Startup TSSE IPC Message, will skip the device if it is already started. + * Return: 0 if startup successfully, other values for failure. + */ +int tsse_im_startup(void) +{ + return tsse_process_for_all(tsse_im_startup_for_dev); +} +EXPORT_SYMBOL_GPL(tsse_im_startup); + +/** + * tsse_im_service_exist() - Check if the specific IPC Message service exists. + * @name: IPC Message service name + * Return: 0 if the service exists, otherwise -EINVAL. + */ +int tsse_im_service_exist(const char *name) +{ + struct service_info_entry *entry; + + entry = tsse_service_info_hash_get(name); + if (!entry) { + pr_err("%s(): service: %s not exist\n", __func__, name); + return -EINVAL; + } + return 0; +} +EXPORT_SYMBOL_GPL(tsse_im_service_exist); + +/** + * tsse_im_service_handle_alloc() - Allocate IPC Message service handle for specific service. + * @name: IPC Message service name + * @cb: request callback for the service + * @handle: function output for the service handle + * Return: 0 if allocated successfully, other values for failure + */ +int tsse_im_service_handle_alloc( + const char *name, + tsse_im_cb_func cb, + tsse_im_service_handle *handle) +{ + struct tsse_service_instance *service_instance; + int ret; + + service_instance = kzalloc(sizeof(struct tsse_service_instance), GFP_ATOMIC); + if (!service_instance) + return -ENOMEM; + service_instance->service_opened = 0; + service_instance->cb = cb; + strscpy(service_instance->service_name, name, TSSE_IM_SERVICE_NAME_LEN); + + ret = tsse_schedule_device_handle(service_instance); + if (ret) { + kfree(service_instance); + return ret; + } + + ret = tsse_service_open(service_instance); + if (ret) { + pr_err("%s(): open service: %s failed: %d\n", + __func__, service_instance->service_name, ret); + kfree(service_instance); + return ret; + } + *handle = service_instance; + return 0; +} +EXPORT_SYMBOL_GPL(tsse_im_service_handle_alloc); + +/** + * tsse_im_service_handle_free() - Free IPC Message service handle + * @handle: service handle to free + * Return: 0 if free successfully, other values for failure + */ +int tsse_im_service_handle_free(tsse_im_service_handle handle) +{ + int ret = 0; + + if (handle) { + ret = tsse_service_close(handle); + kfree((void *)handle); + } + return ret; +} +EXPORT_SYMBOL_GPL(tsse_im_service_handle_free); + +/** + * tsse_im_service_msg_h2d() - Send message from host to device + * @handle: service handle + * @msg_payload: the message payload to send + * @payload_length: length of msg_payload + */ +int tsse_im_service_msg_h2d(tsse_im_service_handle handle, void *msg_payload, u32 payload_length) +{ + if (!handle || !msg_payload || !payload_length) + return -EINVAL; + return tsse_service_msg_send(handle, TSSE_SERVICE_CMD_APP_MSG, msg_payload, payload_length); +} +EXPORT_SYMBOL_GPL(tsse_im_service_msg_h2d); diff --git a/drivers/crypto/montage/tsse/tsse_ipc_drv.c b/drivers/crypto/montage/tsse/tsse_ipc_drv.c new file mode 100644 index 0000000000000000000000000000000000000000..dbc91d3894ba7bef1beea15a272ce784c1377e5d --- /dev/null +++ b/drivers/crypto/montage/tsse/tsse_ipc_drv.c @@ -0,0 +1,137 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * This file is part of tsse driver for Linux + * + * Copyright © 2023-2024 Montage Technology. All rights reserved. + */ + +#include +#include +#include + +#include "tsse_ipc.h" +#include "tsse_ipc_drv.h" +#include "tsse_ipc_service.h" +#include "tsse_ipc_epid.h" +#include "tsse_dev.h" + +#define ALIGN_TO_4_BYTES(length) (((length) + 3) & ~0x3) + +static int ipc_d2h_new_msg_process(struct tsse_ipc *tsseipc, void __iomem *d2h_msg) +{ + struct tsse_ipc_msg *ipc_msg = (struct tsse_ipc_msg *)d2h_msg; + void *payload; + u32 msg_len; + u32 header_len; + u32 payload_len; + int ret; + u64 epid; + + msg_len = ipc_msg->msg_len; + header_len = sizeof(struct tsse_ipc_msg); + payload_len = msg_len - header_len; + epid = ipc_msg->epid; + + if (msg_len < header_len || msg_len > IPC_MAX_DATA_LEN) { + pr_err("%s %d: invalid msg len: %u in resp\n", __func__, __LINE__, msg_len); + return -EINVAL; + } + payload = kzalloc(payload_len, GFP_ATOMIC); + if (!payload) + return -ENOMEM; + memcpy_fromio(payload, (u8 *)d2h_msg + header_len, payload_len); + if (ipc_msg->type == TSSE_IPC_TYPE_RING_SETUP_RSP) + ret = ipc_ring_setup_resp_receive(payload, payload_len); + else + ret = tsse_service_msg_receive(epid, payload, payload_len); + kfree(payload); + return ret; +} + +static struct tsse_ipc_msg *ipc_h2d_msg_header_create(u64 epid, u32 payload_length) +{ + struct tsse_ipc_msg *header = (struct tsse_ipc_msg *)( + kzalloc(sizeof(struct tsse_ipc_msg), GFP_ATOMIC)); + if (header) { + if (GET_SERVICE_ID(epid) == EPID_MANAGE_SERVICE_ID) { + if (GET_APP_SPECIFIC_ID(epid) == TSSE_IPC_SPECIFIC_RING_SETUP_REQ) + header->type = TSSE_IPC_TYPE_RING_SETUP_REQ; + else if (GET_APP_SPECIFIC_ID(epid) == TSSE_IPC_SPECIFIC_RING_SETUP_RSP) + header->type = TSSE_IPC_TYPE_RING_SETUP_RSP; + else + header->type = TSSE_IPC_TYPE_SERVICE; + } else { + header->type = TSSE_IPC_TYPE_SERVICE; + } + header->msg_len = sizeof(struct tsse_ipc_msg) + payload_length; + header->rev = 0; + header->epid = epid; + } + return header; +} + +int ipc_h2d_msg_send(int device_handle, u64 epid, void *msg_payload, u32 length) +{ + struct tsse_dev *tdev; + struct tsse_ipc *tsseipc; + struct tsse_ipc_msg *header; + u8 *h2d; + u32 int_reg; + u32 header_size; + + tdev = tsse_get_dev_by_handle(device_handle); + if (!tdev) + return -ENODEV; + + if (!msg_payload || !length) { + pr_err("%s %d: invalid msg payload\n", __func__, __LINE__); + return -EINVAL; + } + header_size = sizeof(struct tsse_ipc_msg); + if (length + header_size > IPC_MAX_DATA_LEN) { + pr_err("%s %d length too large: %u\n", __func__, __LINE__, length); + return -EINVAL; + } + tsseipc = tdev->ipc; + mutex_lock(&tsseipc->list_lock); + int_reg = readl(tsseipc->virt_addr + HOST2MAIN_INTR_SET_OFFSET); + if ((int_reg & IPC_REGISTER_INT_SET) != 0) { + mutex_unlock(&tsseipc->list_lock); + return -EAGAIN; + } + header = ipc_h2d_msg_header_create(epid, length); + if (!header) { + mutex_unlock(&tsseipc->list_lock); + pr_err("%s(): msg header kzalloc failed\n", __func__); + return -ENOMEM; + } + h2d = (u8 *)(tsseipc->virt_addr + HOST2MAIN_IPC_OFFSET); + ipc_memcpy_to_io(h2d, (u8 *)header, header_size); + ipc_memcpy_to_io(h2d + header_size, msg_payload, length); + + writel(0x1, tsseipc->virt_addr + HOST2MAIN_INTR_SET_OFFSET); + mutex_unlock(&tsseipc->list_lock); + kfree(header); + return 0; +} + +int ipc_d2h_msg_dispatch(struct tsse_ipc *tsseipc, void __iomem *d2h_msg) +{ + u16 type = (u16) cpu_to_le32(readl(d2h_msg)); + + switch (type) { + case TSSE_IPC_TYPE_LEGACY: + return ipc_d2h_legacy_msg_process(tsseipc, d2h_msg); + case TSSE_IPC_TYPE_SERVICE: + case TSSE_IPC_TYPE_RING_SETUP_RSP: + return ipc_d2h_new_msg_process(tsseipc, d2h_msg); + default: + pr_err("%s %d: invalid msg type: %u\n", __func__, __LINE__, type); + return -EINVAL; + } +} + +void ipc_memcpy_to_io(u8 *addr, u8 *src, u32 len) +{ + memcpy_toio(addr, src, len); +} diff --git a/drivers/crypto/montage/tsse/tsse_ipc_drv.h b/drivers/crypto/montage/tsse/tsse_ipc_drv.h new file mode 100644 index 0000000000000000000000000000000000000000..9129d91391325f0511ea98ab3250b2bbb9daa12d --- /dev/null +++ b/drivers/crypto/montage/tsse/tsse_ipc_drv.h @@ -0,0 +1,38 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * This file is part of tsse driver for Linux + * + * Copyright © 2023-2024 Montage Technology. All rights reserved. + */ + +#ifndef __TSSE_IPC_DRV_H__ +#define __TSSE_IPC_DRV_H__ + +#include +#include "tsse_ipc_setup.h" + +#define TSSE_IPC_SPECIFIC_RING_SETUP_REQ 100 +#define TSSE_IPC_SPECIFIC_RING_SETUP_RSP 101 + +#pragma pack(push, 4) +struct tsse_ipc_msg { + u16 type; + u16 msg_len; + u32 rev; + u64 epid; + u8 data[]; +}; +#pragma pack(pop) + +enum tsse_ipc_type { + TSSE_IPC_TYPE_LEGACY = 0, + TSSE_IPC_TYPE_SERVICE, + TSSE_IPC_TYPE_RING_SETUP_REQ, + TSSE_IPC_TYPE_RING_SETUP_RSP +}; + +int ipc_h2d_msg_send(int device_handle, u64 epid, void *msg_payload, u32 length); +int ipc_d2h_msg_dispatch(struct tsse_ipc *tsseipc, void __iomem *d2h_msg); +void ipc_memcpy_to_io(u8 *addr, u8 *src, u32 len); + +#endif diff --git a/drivers/crypto/montage/tsse/tsse_ipc_epid.c b/drivers/crypto/montage/tsse/tsse_ipc_epid.c new file mode 100644 index 0000000000000000000000000000000000000000..9965ba7d6f52dfe8dbc476f6248270b96dda1c96 --- /dev/null +++ b/drivers/crypto/montage/tsse/tsse_ipc_epid.c @@ -0,0 +1,76 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * This file is part of tsse driver for Linux + * + * Copyright © 2023-2024 Montage Technology. All rights reserved. + */ +#include +#include +#include "tsse_ipc_epid.h" +#include "tsse_ipc_hash.h" +#include "tsse_ipc_service.h" + +/* service max ids: 2^20 */ +#define SERVICE_RESERVED_NUM 16 +#define SERVICE_MAX_IDS (1 << 20) +#define SERVICE_BITMAP_SIZE (SERVICE_MAX_IDS / 8) + +#define IS_BIT_SET(bitmap, bit) ((bitmap[(bit) / 8] & (1 << ((bit) % 8))) != 0) +#define SET_BIT(bitmap, bit) (bitmap[(bit) / 8] |= (1 << ((bit) % 8))) +#define CLEAR_BIT(bitmap, bit) (bitmap[(bit) / 8] &= ~(1 << ((bit) % 8))) + +static u8 service_id_bitmap[SERVICE_BITMAP_SIZE] = {0}; +static u32 current_max_service_id = SERVICE_RESERVED_NUM; + +static int tsse_available_service_id(void) +{ + int i = current_max_service_id; + + if (i == SERVICE_MAX_IDS) + i = current_max_service_id = SERVICE_RESERVED_NUM; + for (; i < SERVICE_MAX_IDS; i++) { + if (!IS_BIT_SET(service_id_bitmap, i)) { + SET_BIT(service_id_bitmap, i); + if (i > current_max_service_id) + current_max_service_id = i; + return i; + } + } + return -1; +} + +static void fill_epid(struct tsse_service_instance *service_instance, int service_id) +{ + struct tsse_epid epid_data = {0}; + + epid_data.service_id = service_id; + epid_data.pasid_en = 0; + epid_data.vf_id = 0; + epid_data.is_pf = 1; + epid_data.device_id = service_instance->device_handle; + service_instance->service_epid = EPID_TO_UINT64(&epid_data); +} + +int tsse_alloc_service_epid(tsse_im_service_handle handle) +{ + int service_id; + struct tsse_service_instance *service_instance = (struct tsse_service_instance *)handle; + + if (strcmp(service_instance->service_name, TSSE_MANAGE_SERVICE_NAME) == 0) + service_id = EPID_MANAGE_SERVICE_ID; + else + service_id = tsse_available_service_id(); + if (service_id < 0) + return -EFAULT; + fill_epid(service_instance, service_id); + return 0; +} + +void tsse_free_service_epid(tsse_im_service_handle handle) +{ + struct tsse_service_instance *service_instance = (struct tsse_service_instance *)handle; + u32 service_id = GET_SERVICE_ID(service_instance->service_epid); + + if (service_id < SERVICE_MAX_IDS) + CLEAR_BIT(service_id_bitmap, service_id); +} diff --git a/drivers/crypto/montage/tsse/tsse_ipc_epid.h b/drivers/crypto/montage/tsse/tsse_ipc_epid.h new file mode 100644 index 0000000000000000000000000000000000000000..55886da8e07c6bc2edb7af933857cf5369e20bb8 --- /dev/null +++ b/drivers/crypto/montage/tsse/tsse_ipc_epid.h @@ -0,0 +1,50 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * This file is part of tsse driver for Linux + * + * Copyright © 2023-2024 Montage Technology. All rights reserved. + */ +#ifndef __TSSE_IPC_EPID_H__ +#define __TSSE_IPC_EPID_H__ + +#include +#include "tsse_ipc_msg.h" + +#define EPID_MANAGE_SERVICE_ID 0 +struct tsse_epid { + uint64_t app_id : 8; + uint64_t service_id : 20; + uint64_t pasid : 20; + uint64_t pasid_en : 2; + uint64_t vf_id : 4; + uint64_t is_pf : 2; + uint64_t device_id : 8; +}; + +#define GET_DEVICE_ID(epid) (((epid) >> 56) & 0xFF) +#define GET_SERVICE_ID(epid) (((epid) >> 8) & 0xFFFFF) +#define GET_APP_SPECIFIC_ID(epid) ((epid) & 0xFF) +#define EPID_SET_PF(epid, is_pf) (((epid) & 0xFF3FFFFFFFFFFFFF) | ((uint64_t)(is_pf)) << 54) +#define SERVICE_LEVEL_EPID(epid) ((epid) & 0xFFFFFFFFFFFFFF00) + +#define EPID_TO_UINT64(epid_data) \ + (((uint64_t)(epid_data)->app_id) | \ + ((uint64_t)(epid_data)->service_id << 8) | \ + ((uint64_t)(epid_data)->pasid << 28) | \ + ((uint64_t)(epid_data)->pasid_en << 48) | \ + ((uint64_t)(epid_data)->vf_id << 50) | \ + ((uint64_t)(epid_data)->is_pf << 54) | \ + ((uint64_t)(epid_data)->device_id << 56)) + +/* used to parse from response epid, + * contains device_id, service_id and app_id + */ +#define GET_BASIC_EPID(epid) ((epid) & 0xFF0000000FFFFFFF) + +#define APPEND_APP_ID_TO_EPID(epid, app_id) \ + (((epid) & 0xFFFFFFFFFFFFFF00) | ((app_id) & 0xFF)) + + +int tsse_alloc_service_epid(tsse_im_service_handle handle); +void tsse_free_service_epid(tsse_im_service_handle handle); +#endif diff --git a/drivers/crypto/montage/tsse/tsse_ipc_hash.c b/drivers/crypto/montage/tsse/tsse_ipc_hash.c new file mode 100644 index 0000000000000000000000000000000000000000..417e0e91382cd1a5b060ba4d4579cf26350b79de --- /dev/null +++ b/drivers/crypto/montage/tsse/tsse_ipc_hash.c @@ -0,0 +1,107 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * This file is part of tsse driver for Linux + * + * Copyright © 2023-2024 Montage Technology. All rights reserved. + */ +#include +#include +#include +#include +#include +#include "tsse_ipc_hash.h" + +DEFINE_HASHTABLE(service_info_table, SERVICE_TABLE_BUCKET_BITS); +DEFINE_HASHTABLE(service_handle_table, SERVICE_TABLE_BUCKET_BITS); + +static u32 hash_string(const char *str) +{ + return full_name_hash(NULL, str, strlen(str)); +} + +int tsse_service_info_hash_set(const char *service, void *service_info) +{ + struct service_info_entry *new_entry; + + new_entry = kzalloc(sizeof(struct service_info_entry), GFP_KERNEL); + if (!new_entry) + return -ENOMEM; + new_entry->service_name = kstrdup(service, GFP_KERNEL); + new_entry->service_info = service_info; + hash_add(service_info_table, &new_entry->node, + hash_min(hash_string(service), SERVICE_TABLE_BUCKET_BITS)); + return 0; +} + +struct service_info_entry *tsse_service_info_hash_get(const char *service) +{ + struct service_info_entry *entry; + + hash_for_each_possible(service_info_table, entry, node, + hash_min(hash_string(service), SERVICE_TABLE_BUCKET_BITS)) { + if (strcmp(entry->service_name, service) == 0) + return entry; + } + return NULL; +} + +void tsse_service_info_hash_remove_all(void) +{ + int bucket; + struct service_info_entry *entry; + struct hlist_node *tmp; + + hash_for_each_safe(service_info_table, bucket, tmp, entry, node) { + kfree(entry->service_name); + kfree(entry->service_info); + hash_del(&entry->node); + kfree(entry); + } +} + +int tsse_service_handle_hash_set(u64 epid, void *handle) +{ + struct service_handle_entry *new_entry; + + new_entry = kzalloc(sizeof(struct service_handle_entry), GFP_KERNEL); + if (!new_entry) + return -ENOMEM; + new_entry->epid = epid; + new_entry->handle = handle; + hash_add(service_handle_table, &new_entry->node, hash_min(epid, SERVICE_TABLE_BUCKET_BITS)); + return 0; +} + +struct service_handle_entry *tsse_service_handle_hash_get(u64 epid) +{ + struct service_handle_entry *entry; + + hash_for_each_possible(service_handle_table, entry, node, + hash_min(epid, SERVICE_TABLE_BUCKET_BITS)) { + if (entry->epid == epid) + return entry; + } + return NULL; +} + +void tsse_service_handle_hash_remove(u64 epid) +{ + struct service_handle_entry *entry = tsse_service_handle_hash_get(epid); + + if (entry) { + hash_del(&entry->node); + kfree(entry); + } +} + +void tsse_service_handle_hash_remove_all(void) +{ + int bucket; + struct service_handle_entry *entry; + struct hlist_node *tmp; + + hash_for_each_safe(service_handle_table, bucket, tmp, entry, node) { + hash_del(&entry->node); + kfree(entry); + } +} diff --git a/drivers/crypto/montage/tsse/tsse_ipc_hash.h b/drivers/crypto/montage/tsse/tsse_ipc_hash.h new file mode 100644 index 0000000000000000000000000000000000000000..963f33a633176f6ef2e824fa8ef3c9ff858c0770 --- /dev/null +++ b/drivers/crypto/montage/tsse/tsse_ipc_hash.h @@ -0,0 +1,33 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * This file is part of tsse driver for Linux + * + * Copyright © 2023-2024 Montage Technology. All rights reserved. + */ + +#ifndef __TSSE_IPC_HASH_H__ +#define __TSSE_IPC_HASH_H__ + +#define SERVICE_TABLE_BUCKET_BITS 8 + +struct service_info_entry { + char *service_name; + void *service_info; + struct hlist_node node; +}; + +struct service_handle_entry { + u64 epid; + void *handle; + struct hlist_node node; +}; + +int tsse_service_info_hash_set(const char *service, void *service_info); +struct service_info_entry *tsse_service_info_hash_get(const char *service); +void tsse_service_info_hash_remove_all(void); +int tsse_service_handle_hash_set(u64 epid, void *handle); +struct service_handle_entry *tsse_service_handle_hash_get(u64 epid); +void tsse_service_handle_hash_remove(u64 epid); +void tsse_service_handle_hash_remove_all(void); + +#endif diff --git a/drivers/crypto/montage/tsse/tsse_ipc_msg.h b/drivers/crypto/montage/tsse/tsse_ipc_msg.h new file mode 100644 index 0000000000000000000000000000000000000000..e8de74d339f973eef6e4c731169287011efdda0f --- /dev/null +++ b/drivers/crypto/montage/tsse/tsse_ipc_msg.h @@ -0,0 +1,63 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * This file is part of tsse driver for Linux + * + * Copyright © 2023-2024 Montage Technology. All rights reserved. + */ + +#ifndef __TSSE_IPC_MSG_H__ +#define __TSSE_IPC_MSG_H__ + +#include + +#define TSSE_IM_DEVICE_NUM_MAX 256 +#define TSSE_IM_SERVICE_NAME_LEN 16 + +enum tsse_im_msg_type { + IM_MSG_TYPE_REQ = 0, + IM_MSG_TYPE_RSP, + IM_MSG_TYPE_NOTIFY +}; + +#pragma pack(push, 4) +struct tsse_im_msg_hdr { + /** @ref enum tsse_im_msg_type */ + uint16_t msg_type; + /** internal command id of the service */ + uint16_t cmd; + uint64_t cookie; +}; + +struct tsse_im_service_info { + char service_name[TSSE_IM_SERVICE_NAME_LEN]; + uint32_t num_devices; + uint8_t device_ids[TSSE_IM_DEVICE_NUM_MAX]; +}; +#pragma pack(pop) + +#define tsse_im_service_handle void * + +/** + * tsse_im_cb_func - callback to process device-to-host IPC message, + * also called response handler. Service layer should register it + * when alloc service handle by tsse_im_service_handle_alloc. + * @handle: handle to TSSE service + * @msg_payload: actual data related to specific message class + * @payload_length: length of msg_payload + * Return: 0 on success, error code otherwise + */ +typedef int (*tsse_im_cb_func)(tsse_im_service_handle handle, + void *msg_payload, u32 payload_length); + +int tsse_im_startup(void); + +int tsse_im_service_exist(const char *name); + +int tsse_im_service_handle_alloc(const char *name, + tsse_im_cb_func cb, tsse_im_service_handle *handle); + +int tsse_im_service_handle_free(tsse_im_service_handle handle); + +int tsse_im_service_msg_h2d(tsse_im_service_handle handle, void *msg_payload, u32 payload_length); + +#endif diff --git a/drivers/crypto/montage/tsse/tsse_ipc_service.c b/drivers/crypto/montage/tsse/tsse_ipc_service.c new file mode 100644 index 0000000000000000000000000000000000000000..a9a2e0ba607686531afd114b521c8c6052e6f0ff --- /dev/null +++ b/drivers/crypto/montage/tsse/tsse_ipc_service.c @@ -0,0 +1,467 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * This file is part of tsse driver for Linux + * + * Copyright © 2023-2024 Montage Technology. All rights reserved. + */ +#include +#include +#include +#include +#include +#include +#include + +#include "tsse_ipc_service.h" +#include "tsse_ipc_drv.h" +#include "tsse_ipc_epid.h" +#include "tsse_ipc_hash.h" +#include "tsse_ipc_msg.h" +#include "tsse_dev.h" + +static DEFINE_MUTEX(tsse_device_table_lock); +static int tsse_device_last_used_index = -1; + +static int service_request_post_process( + int ret, void *req, + struct tsse_service_user_data *user_data, + tsse_im_service_handle handle, + post_process_func func) +{ + if (ret) + goto cleanup; + if (!wait_for_completion_timeout(&user_data->req_completion, + msecs_to_jiffies(TSSE_SERVICE_MAX_WAIT_MILLISECONDS))) { + pr_err("%s(): completion timeout\n", __func__); + ret = -EFAULT; + goto cleanup; + } + ret = user_data->status; + if ((ret == 0) && func) + func(handle); +cleanup: + kfree(user_data); + kfree(req); + return ret; +} + +int tsse_service_msg_send( + tsse_im_service_handle handle, + u32 service_cmd, + void *msg_payload, + u32 payload_length) +{ + struct tsse_service_instance *service_instance; + u64 epid; + + service_instance = (struct tsse_service_instance *)handle; + if ((service_cmd == TSSE_SERVICE_CMD_APP_MSG) && !service_instance->service_opened) { + pr_err("%s(): service handle is not ready to use\n", __func__); + return -EPERM; + } + epid = APPEND_APP_ID_TO_EPID(service_instance->service_epid, service_cmd); + return ipc_h2d_msg_send(service_instance->device_handle, epid, msg_payload, payload_length); +} + +static int tsse_service_open_post_process(tsse_im_service_handle handle) +{ + struct tsse_service_instance *service_instance; + int ret; + + service_instance = (struct tsse_service_instance *)handle; + ret = tsse_service_handle_hash_set(service_instance->service_epid, handle); + if (ret) { + pr_err("%s() failed to save service handle: %d\n", __func__, ret); + return ret; + } + service_instance->service_opened = 1; + return 0; +} + +int tsse_service_open(tsse_im_service_handle handle) +{ + struct tsse_service_open_req *req; + struct tsse_service_user_data *user_data; + struct tsse_service_instance *service_instance; + int ret; + + ret = tsse_alloc_service_epid(handle); + if (ret) + return ret; + + req = kzalloc(sizeof(struct tsse_service_open_req), GFP_ATOMIC); + if (!req) + return -ENOMEM; + + user_data = kzalloc(sizeof(struct tsse_service_user_data), GFP_ATOMIC); + if (!user_data) { + kfree(req); + return -ENOMEM; + } + + init_completion(&user_data->req_completion); + service_instance = (struct tsse_service_instance *)handle; + req->hdr.msg_type = IM_MSG_TYPE_REQ; + req->hdr.cookie = (u64)user_data; + memcpy(req->service_name, service_instance->service_name, TSSE_IM_SERVICE_NAME_LEN); + ret = tsse_service_msg_send(handle, TSSE_SERVICE_CMD_OPEN, + req, sizeof(struct tsse_service_open_req)); + return service_request_post_process(ret, req, user_data, + handle, tsse_service_open_post_process); +} + +static int tsse_service_close_post_process(tsse_im_service_handle handle) +{ + struct tsse_service_instance *service_instance; + + service_instance = (struct tsse_service_instance *)handle; + service_instance->service_opened = 0; + tsse_service_handle_hash_remove(service_instance->service_epid); + tsse_free_service_epid(handle); + return 0; +} + +int tsse_service_close(tsse_im_service_handle handle) +{ + struct tsse_service_close_req *req; + struct tsse_service_user_data *user_data; + int ret; + + req = kzalloc(sizeof(struct tsse_service_close_req), GFP_ATOMIC); + if (!req) + return -ENOMEM; + + user_data = kzalloc(sizeof(struct tsse_service_user_data), GFP_ATOMIC); + if (!user_data) { + kfree(req); + return -ENOMEM; + } + + init_completion(&user_data->req_completion); + req->hdr.msg_type = IM_MSG_TYPE_REQ; + req->hdr.cookie = (u64)user_data; + ret = tsse_service_msg_send(handle, TSSE_SERVICE_CMD_CLOSE, + req, sizeof(struct tsse_service_close_req)); + return service_request_post_process(ret, req, user_data, + handle, tsse_service_close_post_process); +} + +int tsse_services_query_request(tsse_im_service_handle handle) +{ + struct tsse_services_query_req *req; + struct tsse_service_user_data *user_data; + int ret; + + req = kzalloc(sizeof(struct tsse_services_query_req), GFP_ATOMIC); + if (!req) + return -ENOMEM; + + user_data = kzalloc(sizeof(struct tsse_service_user_data), GFP_ATOMIC); + if (!user_data) { + kfree(req); + return -ENOMEM; + } + + init_completion(&user_data->req_completion); + req->hdr.msg_type = IM_MSG_TYPE_REQ; + req->hdr.cmd = TSSE_SERVICES_QUERY_CMD; + req->hdr.cookie = (u64)user_data; + ret = tsse_service_msg_send(handle, TSSE_SERVICE_CMD_APP_MSG, + req, sizeof(struct tsse_services_query_req)); + return service_request_post_process(ret, req, user_data, handle, NULL); +} + +int tsse_services_query_response(tsse_im_service_handle handle, void *payload, u32 length) +{ + struct tsse_service_instance *instance; + struct tsse_service_comm_resp *resp; + struct tsse_service_user_data *user_data; + struct tsse_services_query_resp *resp_payload; + struct tsse_service_info *service_info; + struct service_info_entry *entry; + char service_name[TSSE_IM_SERVICE_NAME_LEN] = {0}; + u32 buffer_len; + u32 data_offset; + u32 index; + u32 device_exists = 0; + + instance = (struct tsse_service_instance *) handle; + if (length < sizeof(struct tsse_service_comm_resp)) { + pr_err("%s() invalid length: %u\n", __func__, length); + return -EFAULT; + } + resp = (struct tsse_service_comm_resp *)payload; + user_data = (struct tsse_service_user_data *)resp->hdr.cookie; + if (resp->hdr.msg_type != IM_MSG_TYPE_RSP) { + pr_err("%s() invalid msg_type: %u\n", __func__, resp->hdr.msg_type); + return -EFAULT; + } + if (!user_data) { + pr_err("%s() empty cookie in resp header\n", __func__); + return -EFAULT; + } + length -= sizeof(struct tsse_service_comm_resp); + data_offset = 0; + while (data_offset < length) { + resp_payload = (struct tsse_services_query_resp *)( + resp->data + data_offset); + buffer_len = resp_payload->len + 1; + if (buffer_len > TSSE_IM_SERVICE_NAME_LEN) + buffer_len = TSSE_IM_SERVICE_NAME_LEN; + strscpy(service_name, resp_payload->data, buffer_len); + entry = tsse_service_info_hash_get(service_name); + if (entry) { + service_info = (struct tsse_service_info *)entry->service_info; + for (index = 0; index < service_info->num_devices; index++) + device_exists |= (service_info->device_handles[index] + == instance->device_handle); + if (!device_exists) { + service_info->device_handles[service_info->num_devices] + = instance->device_handle; + service_info->num_devices++; + } + } else { + service_info = kzalloc(sizeof(struct tsse_service_info), GFP_ATOMIC); + if (!service_info) + return -ENOMEM; + memcpy(service_info->name, service_name, TSSE_IM_SERVICE_NAME_LEN); + service_info->num_devices = 1; + service_info->device_handles[0] = instance->device_handle; + tsse_service_info_hash_set(service_name, service_info); + } + data_offset += (sizeof(struct tsse_services_query_resp) + + resp_payload->len); + } + user_data->status = resp->ret_code; + complete(&user_data->req_completion); + return 0; +} + +static int tsse_service_open_close_resp(void *msg, u32 msg_len) +{ + struct tsse_service_comm_resp *resp; + struct tsse_service_user_data *user_data; + + if (msg_len < sizeof(struct tsse_service_comm_resp)) { + pr_err("%s() invalid msg_len: %u\n", __func__, msg_len); + return -EFAULT; + } + resp = (struct tsse_service_comm_resp *)msg; + user_data = (struct tsse_service_user_data *)resp->hdr.cookie; + if (resp->hdr.msg_type != IM_MSG_TYPE_RSP) { + pr_err("%s() invalid msg_type: %u\n", __func__, resp->hdr.msg_type); + return -EFAULT; + } + if (!user_data) { + pr_err("%s() empty cookie in resp header\n", __func__); + return -EFAULT; + } + user_data->status = resp->ret_code; + complete(&user_data->req_completion); + return 0; +} + +static int tsse_service_app_resp(u64 epid, void *msg, u32 msg_len) +{ + struct service_handle_entry *entry; + struct tsse_service_instance *instance; + + entry = tsse_service_handle_hash_get(SERVICE_LEVEL_EPID(epid)); + if (!entry || !entry->handle) { + pr_err("%s() cannot find service handle for epid: 0x%llx\n", __func__, epid); + return -EFAULT; + } + instance = (struct tsse_service_instance *)entry->handle; + return instance->cb(instance, msg, msg_len); +} + +int tsse_service_msg_receive(u64 epid, void *msg, u32 msg_len) +{ + u32 service_cmd; + + if (!msg || !msg_len) { + pr_err("%s() service resp msg should not be empty\n", __func__); + return -EFAULT; + } + service_cmd = GET_APP_SPECIFIC_ID(epid); + switch (service_cmd) { + case TSSE_SERVICE_CMD_OPEN: + case TSSE_SERVICE_CMD_CLOSE: + return tsse_service_open_close_resp(msg, msg_len); + case TSSE_SERVICE_CMD_APP_MSG: + return tsse_service_app_resp(epid, msg, msg_len); + default: + return -EFAULT; + } + return 0; +} + +int tsse_schedule_device_handle(tsse_im_service_handle handle) +{ + struct tsse_service_info *service_info; + struct tsse_service_instance *service_instance; + struct service_info_entry *entry; + u32 device_handle_index; + + service_instance = (struct tsse_service_instance *)handle; + entry = tsse_service_info_hash_get(service_instance->service_name); + if (!entry || !entry->service_info) { + pr_err("%s(): service %s not exist\n", __func__, service_instance->service_name); + return -EFAULT; + } + service_info = (struct tsse_service_info *)entry->service_info; + if (service_info->num_devices == 0) { + pr_err("%s(): no available device for service: %s\n", + __func__, service_instance->service_name); + return -EFAULT; + } + mutex_lock(&tsse_device_table_lock); + if (tsse_device_last_used_index < 0) + device_handle_index = 0; + else + device_handle_index = (tsse_device_last_used_index + 1) % service_info->num_devices; + tsse_device_last_used_index = device_handle_index; + mutex_unlock(&tsse_device_table_lock); + service_instance->device_handle = service_info->device_handles[device_handle_index]; + return 0; +} + +static u64 get_init_ring_epid(int device_handle) +{ + struct tsse_epid epid_data = {0}; + + epid_data.service_id = EPID_MANAGE_SERVICE_ID; + epid_data.pasid_en = 0; + epid_data.vf_id = 0; + epid_data.is_pf = 1; + epid_data.device_id = device_handle; + epid_data.app_id = TSSE_IPC_SPECIFIC_RING_SETUP_REQ; + return EPID_TO_UINT64(&epid_data); +} + +int tsse_ipc_setup_ring(int device_handle, u32 is_create) +{ + int ret; + u64 epid; + struct tsse_ipc_ring_setup_req *setup_req; + struct tsse_service_user_data *user_data; + + setup_req = kzalloc(sizeof(struct tsse_ipc_ring_setup_req), GFP_ATOMIC); + user_data = kzalloc(sizeof(struct tsse_service_user_data), GFP_ATOMIC); + if (!setup_req || !user_data) + return -ENOMEM; + + setup_req->cookie = (u64) user_data; + setup_req->is_create = is_create > 0 ? 1 : 0; + epid = get_init_ring_epid(device_handle); + if (is_create) + init_completion(&user_data->req_completion); + + ret = ipc_h2d_msg_send(device_handle, epid, setup_req, + sizeof(struct tsse_ipc_ring_setup_req)); + if (ret) + goto cleanup; + if (is_create) { + if (!wait_for_completion_timeout(&user_data->req_completion, + msecs_to_jiffies(TSSE_SERVICE_MAX_WAIT_MILLISECONDS))) { + pr_err("%s(): completion timeout\n", __func__); + ret = -EFAULT; + goto cleanup; + } + ret = user_data->status; + } +cleanup: + kfree(user_data); + kfree(setup_req); + return ret; +} + +int ipc_ring_setup_resp_receive(void *msg, u32 length) +{ + struct tsse_ipc_ring_setup_resp *resp; + struct tsse_service_user_data *user_data; + + if (length < sizeof(struct tsse_ipc_ring_setup_resp)) { + pr_err("%s %d: invalid resp len: %u\n", __func__, __LINE__, length); + return -EINVAL; + } + resp = (struct tsse_ipc_ring_setup_resp *)msg; + user_data = (struct tsse_service_user_data *)resp->cookie; + user_data->status = resp->ret; + complete(&user_data->req_completion); + return 0; +} + +static int tsse_im_services_init(struct pci_dev *pdev) +{ + struct tsse_dev *tdev = pci_to_tsse_dev(pdev); + struct tsse_service_instance *service_instance; + int ret; + + service_instance = kzalloc(sizeof(struct tsse_service_instance), GFP_ATOMIC); + if (!service_instance) + return -ENOMEM; + service_instance->service_opened = 0; + service_instance->device_handle = tdev->id; + service_instance->cb = tsse_services_query_response; + strscpy(service_instance->service_name, TSSE_MANAGE_SERVICE_NAME, TSSE_IM_SERVICE_NAME_LEN); + + ret = tsse_service_open(service_instance); + if (ret) { + pr_err("%s(): open service: %s failed: %d\n", + __func__, service_instance->service_name, ret); + goto cleanup; + } + ret = tsse_services_query_request(service_instance); + if (ret) { + pr_err("%s(): services query failed: %d\n", __func__, ret); + goto cleanup; + } + ret = tsse_service_close(service_instance); + if (ret) { + pr_err("%s(): close service: %s failed: %d\n", + __func__, service_instance->service_name, ret); + goto cleanup; + } +cleanup: + kfree(service_instance); + return ret; +} + +int tsse_im_startup_for_dev(struct tsse_dev *tdev) +{ + int ret; + + if (!tdev || !tdev->ipc) { + pr_err("failed to startup im, the device is not ready\n"); + return -EPERM; + } + if (tdev->ipc->im_inited) + return 0; + ret = tsse_ipc_setup_ring(tdev->id, 1); + if (ret == 0) + ret = tsse_im_services_init(tdev->tsse_pci_dev.pci_dev); + if (ret == 0) { + tdev->ipc->im_inited = 1; + return ret; + } + tsse_im_shutdown_for_dev(tdev); + return ret; +} + +int tsse_im_shutdown_for_dev(struct tsse_dev *tdev) +{ + struct tsse_ipc *tsseipc; + int ret = 0; + + if (!tdev) + return 0; + + tsseipc = tdev->ipc; + if (tsseipc && tsseipc->im_inited) { + ret = tsse_ipc_setup_ring(tdev->id, 0); + if (ret == 0) + tsseipc->im_inited = 0; + } + return ret; +} diff --git a/drivers/crypto/montage/tsse/tsse_ipc_service.h b/drivers/crypto/montage/tsse/tsse_ipc_service.h new file mode 100644 index 0000000000000000000000000000000000000000..24db2f5a174c0dc872c6605e128aa32ab9e06f6a --- /dev/null +++ b/drivers/crypto/montage/tsse/tsse_ipc_service.h @@ -0,0 +1,105 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * This file is part of tsse driver for Linux + * + * Copyright © 2023-2024 Montage Technology. All rights reserved. + */ + +#ifndef __TSSE_IPC_SERVICE_H__ +#define __TSSE_IPC_SERVICE_H__ + +#include +#include +#include "tsse_ipc_msg.h" +#include "tsse_dev.h" + +#define TSSE_SERVICES_QUERY_CMD 0 +#define TSSE_MANAGE_SERVICE_NAME "manage service" +#define TSSE_SERVICE_MAX_WAIT_MILLISECONDS 5000 + +enum tsse_service_cmd { + TSSE_SERVICE_CMD_OPEN = 50, + TSSE_SERVICE_CMD_CLOSE, + TSSE_SERVICE_CMD_APP_MSG, + TSSE_SERVICE_CMD_DELIMITER +}; + +#pragma pack(push, 4) +struct tsse_service_instance { + u8 service_opened; + u8 service_name[TSSE_IM_SERVICE_NAME_LEN]; + int device_handle; + tsse_im_cb_func cb; + u64 service_epid; +}; + +struct tsse_service_user_data { + struct completion req_completion; + int status; +}; + +struct tsse_service_open_req { + struct tsse_im_msg_hdr hdr; + uint8_t service_name[TSSE_IM_SERVICE_NAME_LEN]; +}; + +struct tsse_service_close_req { + struct tsse_im_msg_hdr hdr; +}; + +struct tsse_services_query_req { + struct tsse_im_msg_hdr hdr; +}; + +struct tsse_services_query_resp { + u16 type; + u16 len; + u8 data[]; +}; + +struct tsse_service_comm_resp { + struct tsse_im_msg_hdr hdr; + int ret_code; + u8 data[]; +}; + +struct tsse_service_info { + char name[TSSE_IM_SERVICE_NAME_LEN]; + u32 num_devices; + u8 device_handles[TSSE_IM_DEVICE_NUM_MAX]; + struct list_head list; +}; + +struct tsse_ipc_ring_setup_req { + u64 cookie; + u32 is_create; + u32 reserved[13]; +}; + +struct tsse_ipc_ring_setup_resp { + uint64_t cookie; + int32_t ret; +}; +#pragma pack(pop) + +int tsse_service_msg_send( + tsse_im_service_handle handle, + u32 service_cmd, + void *msg_payload, + u32 payload_length); + +int tsse_service_msg_receive(u64 epid, void *msg, u32 msg_len); + +int tsse_service_open(tsse_im_service_handle handle); +int tsse_service_close(tsse_im_service_handle handle); +int tsse_services_query_request(tsse_im_service_handle handle); +int tsse_services_query_response(tsse_im_service_handle handle, void *payload, u32 length); +int tsse_schedule_device_handle(tsse_im_service_handle handle); +int tsse_ipc_setup_ring(int device_handle, u32 is_create); +int ipc_ring_setup_resp_receive(void *msg, u32 length); + +int tsse_im_shutdown_for_dev(struct tsse_dev *tdev); +int tsse_im_startup_for_dev(struct tsse_dev *tdev); + +typedef int (*post_process_func)(tsse_im_service_handle handle); +#endif diff --git a/drivers/crypto/montage/tsse/tsse_ipc_setup.c b/drivers/crypto/montage/tsse/tsse_ipc_setup.c new file mode 100644 index 0000000000000000000000000000000000000000..d1d0bfc98b36ddaad5f0957002648b96c98d9480 --- /dev/null +++ b/drivers/crypto/montage/tsse/tsse_ipc_setup.c @@ -0,0 +1,138 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * This file is part of tsse driver for Linux + * + * Copyright © 2023-2024 Montage Technology. All rights reserved. + */ + +#include +#include +#include +#include +#include + +#include "tsse_ipc.h" +#include "tsse_ipc_drv.h" +#include "tsse_ipc_setup.h" +#include "tsse_ipc_msg.h" +#include "tsse_ipc_service.h" +#include "tsse_ipc_hash.h" +#include "tsse_ipc_epid.h" +#ifndef DISABLE_FW +#include "tsse_fw_service.h" +#endif +#include "tsse_dev.h" + +/** + * ipc_hw_init()- Enable main2host interrupt, cleanup interrupt + * set value in host2main and main2host. + * @hw_ipc: pointer to a structure used for IPC + */ +static void ipc_hw_init(struct tsse_ipc *hw_ipc) +{ + writel(0x1, hw_ipc->virt_addr + MAIN2HOST_INTR_ENABLE_OFFSET); + writel(0x0, hw_ipc->virt_addr + HOST2MAIN_INTR_SET_OFFSET); + writel(0x0, hw_ipc->virt_addr + MAIN2HOST_INTR_SET_OFFSET); +} + +static void tsse_ipc_bh_handler(unsigned long data) +{ + struct tsse_ipc *tsseipc = (struct tsse_ipc *)data; + void __iomem *d2h_msg = tsseipc->virt_addr + MAIN2HOST_IPC_OFFSET; + int ret = ipc_d2h_msg_dispatch(tsseipc, d2h_msg); + + if (ret) + dev_err(tsseipc->dev, "%s: device message callback result: %d\n", + __func__, ret); +} + +static irqreturn_t tsse_ipc_d2h_irqhandler(int irq, void *dev_id) +{ + struct tsse_ipc *tsseipc = (struct tsse_ipc *)dev_id; + + writel(0x0, tsseipc->virt_addr + MAIN2HOST_INTR_SET_OFFSET); + tasklet_schedule(&tsseipc->ipc_handle); + dev_err(tsseipc->dev, "irq%d\n", irq); + return IRQ_HANDLED; +} + +#ifndef DISABLE_FW +static int host_init_msg(int handle) +{ + uint32_t cmd = IPC_BASIC_CMD_HOST_INIT; + + return ipc_h2d_msg_send_legacy(handle, IPC_MESSAGE_BASIC, &cmd, sizeof(uint32_t)); +} +#endif + +int tsse_ipc_init(struct pci_dev *pdev) +{ + struct tsse_dev *tdev = pci_to_tsse_dev(pdev); + struct tsse_ipc *ipc; + int rc; + + ipc = devm_kzalloc(&pdev->dev, sizeof(*ipc), GFP_KERNEL); + if (ipc == NULL) + return -ENOMEM; + tdev->ipc = ipc; + ipc->pdev = pdev; + ipc->dev = &pdev->dev; + ipc->virt_addr = TSSE_DEV_BARS(tdev)[2].virt_addr; + ipc->im_inited = 0; + + mutex_init(&ipc->list_lock); + tasklet_init(&(ipc->ipc_handle), tsse_ipc_bh_handler, (ulong)(ipc)); + + rc = request_threaded_irq(pci_irq_vector(pdev, 0), NULL, + tsse_ipc_d2h_irqhandler, IRQF_SHARED, + "pf-ipc", ipc); + if (rc) { + dev_err(&pdev->dev, "request_threaded_irq failed: %d\n", rc); + return rc; + } + ipc_hw_init(ipc); +#ifndef DISABLE_FW + ipc->d2h_handlers[IPC_MESSAGE_BOOT] = fw_service; + rc = host_init_msg(tdev->id); + if (rc) { + dev_err(&pdev->dev, "host_init_msg failed: %d\n", rc); + tsse_ipc_deinit(tdev); + return rc; + } +#endif + return rc; +} + +void tsse_ipc_deinit(void *tdev_t) +{ + struct tsse_ipc *tsseipc; + struct pci_dev *pdev; + struct tsse_dev *tdev; + + tdev = tdev_t; + tsseipc = tdev->ipc; + pdev = tsseipc->pdev; + if (tsseipc) { + tsse_im_shutdown_for_dev(tdev); + free_irq(pci_irq_vector(pdev, 0), tdev->ipc); + tdev->ipc = NULL; + } + tsse_service_info_hash_remove_all(); + tsse_service_handle_hash_remove_all(); +} + +#ifndef DISABLE_FW +int tsse_fw_manual_load_ipc(struct pci_dev *pdev) +{ + struct tsse_dev *tdev = pci_to_tsse_dev(pdev); + struct tsse_ipc *ipc = tdev->ipc; + int rc = -EFAULT; + + if (ipc) { + rc = host_init_msg(tdev->id); + if (rc) + dev_err(&pdev->dev, "host_init_msg failed: %d\n", rc); + } + return rc; +} +#endif diff --git a/drivers/crypto/montage/tsse/tsse_ipc_setup.h b/drivers/crypto/montage/tsse/tsse_ipc_setup.h new file mode 100644 index 0000000000000000000000000000000000000000..610b2851f9900684ba4ff8324e41c62ee70a9153 --- /dev/null +++ b/drivers/crypto/montage/tsse/tsse_ipc_setup.h @@ -0,0 +1,72 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * This file is part of tsse driver for Linux + * + * Copyright © 2023-2024 Montage Technology. All rights reserved. + */ + +#ifndef __TSSE_IPC_SETUP_H__ +#define __TSSE_IPC_SETUP_H__ + +#include +#include +#include +#include + +#define HOST2MAIN_INTR_SET_OFFSET 0x2000 +#define HOST2MAIN_INTR_ENABLE_OFFSET 0x2004 +#define HOST2MAIN_ACK_INTR_CLR_OFFSET 0x2008 +#define HOST2MAIN_ACK_INTR_ENABLE_OFFSET 0x200c +#define HOST2MAIN_VLD_INTR_STATUS_OFFSET 0x2010 +#define HOST2MAIN_ACK_INTR_STATUS_OFFSET 0x2014 +#define MSIX_MASK_EN_REG_OFFSET 0x2020 +#define INTR_MASK_BIT_OFFSET 0x2024 +#define INTR_PENDING_BIT_OFFSET 0x2028 +#define HOST2MAIN_IPC_OFFSET 0x2400 + +#define MAIN2HOST_INTR_SET_OFFSET 0x3000 +#define MAIN2HOST_INTR_ENABLE_OFFSET 0x3004 +#define MAIN2HOST_ACK_INTR_CLR_OFFSET 0x3008 +#define MAIN2HOST_ACK_INTR_ENABLE_OFFSET 0x300c +#define MAIN2HOST_VEN_MSI_FUNC_NUM_OFFSET 0x3010 +#define MAIN2HOST_VEN_MSI_VFUNC_ACTIVE_OFFSET 0x3014 +#define MAIN2HOST_IPC_OFFSET 0x3400 + +#define IPC_REGISTER_INT_SET BIT(0) +#define IPC_REGISTER_INT_MASK BIT(1) + +#define IPC_MAX_DATA_LEN 1024 + +typedef int (*tsse_d2h_ipc_handler)(int handle, void *msg_payload, uint32_t payload_length); + +enum IPC_BASIC_CMD { + IPC_BASIC_CMD_HOST_INIT = 0x1, + IPC_BASIC_CMD_PING = 0x2 +}; + +enum IPC_BOOT_CMD { + IPC_BOOT_CMD_GET_FIRMWARE = 0x1 +}; + +enum IPC_MESSAGE_CLASS { + IPC_MESSAGE_BASIC = 1, + IPC_MESSAGE_BOOT, + IPC_MESSAGE_CLASS_NUM, +}; + +struct tsse_ipc { + struct device *dev; + struct pci_dev *pdev; + void __iomem *virt_addr; + struct mutex list_lock; + struct tasklet_struct ipc_handle; + tsse_d2h_ipc_handler d2h_handlers[IPC_MESSAGE_CLASS_NUM]; + u32 im_inited; +}; + +int tsse_ipc_init(struct pci_dev *pdev); +void tsse_ipc_deinit(void *tdev_t); +int tsse_fw_manual_load_ipc(struct pci_dev *pdev); +int tsse_ipc_services_init(struct pci_dev *pdev); + +#endif diff --git a/drivers/crypto/montage/tsse/tsse_service.c b/drivers/crypto/montage/tsse/tsse_service.c index e4be85535b7765f6dc3db73642b7d519950bd715..65d3fee9f212d32fac3a93cc900fc4c55bb3b55f 100644 --- a/drivers/crypto/montage/tsse/tsse_service.c +++ b/drivers/crypto/montage/tsse/tsse_service.c @@ -2,27 +2,42 @@ /* * This file is part of tsse driver for Linux * - * Copyright © 2023 Montage Technology. All rights reserved. + * Copyright © 2023-2024 Montage Technology. All rights reserved. */ #include #include "tsse_service.h" +#include "tsse_dev.h" int service_rout(struct tsse_ipc *tsseipc, struct ipc_msg *msg) { struct msg_info *info; + struct tsse_dev *tdev; + tsse_d2h_ipc_handler d2h_handler; + void *payload; uint32_t msg_class; - int ret = 0; + uint32_t comm_msg_length; + uint32_t payload_length; + int ret; info = (struct msg_info *)msg->i_data; msg_class = info->msg_class; - switch (msg_class) { - case IPC_MESSAGE_BOOT: - fw_service(tsseipc, msg); - break; + d2h_handler = tsseipc->d2h_handlers[msg_class]; - default: - ret = -EINVAL; - break; + if (!d2h_handler) { + dev_err(tsseipc->dev, "%s %d: no d2h handler for msg [%u]\n", + __func__, __LINE__, msg_class); + return -EFAULT; } + tdev = pci_to_tsse_dev(tsseipc->pdev); + if (!tdev) { + dev_err(tsseipc->dev, "%s %d: no related dev info for ipc\n", + __func__, __LINE__); + return -EFAULT; + } + comm_msg_length = sizeof(struct ipc_header) + sizeof(struct msg_info); + payload = (void *) ((uint8_t *)msg + comm_msg_length); + payload_length = msg->header.i_len - comm_msg_length; + + ret = d2h_handler(tdev->id, payload, payload_length); return ret; } diff --git a/drivers/crypto/montage/tsse/tsse_service.h b/drivers/crypto/montage/tsse/tsse_service.h index d5fd87ee7dce430146e0560973c8a405a0ef0947..8672f9e75f126cf4a40ec5c3403215f697616fd5 100644 --- a/drivers/crypto/montage/tsse/tsse_service.h +++ b/drivers/crypto/montage/tsse/tsse_service.h @@ -2,14 +2,13 @@ /* * This file is part of tsse driver for Linux * - * Copyright © 2023 Montage Technology. All rights reserved. + * Copyright © 2023-2024 Montage Technology. All rights reserved. */ #ifndef __TSSE_SERVICE_H__ #define __TSSE_SERVICE_H__ #include "tsse_ipc.h" -#include "tsse_fw_service.h" int service_rout(struct tsse_ipc *tsseipc, struct ipc_msg *msg);