From: Zhao Qunqin zhaoqunqin@loongson.cn
LoongArch inclusion category: feature bugzilla: https://gitee.com/openeuler/kernel/issues/IBENRY CVE: NA
--------------------------------
ACPI HID for Loongson SE is "LOONG0011", for Loongson SDF is "LOON0012". And cleanned up coding style. Moved SE header file form arch/loongarch/include/asm to include/soc/loongson.
Signed-off-by: Zhao Qunqin zhaoqunqin@loongson.cn Change-Id: Ie15d377ce0435643ac950b34dff48142f21f2d02 --- drivers/char/loongson_se.c | 440 +++++++----------- drivers/char/lsse_sdf_cdev.c | 310 ++++++------ .../include/asm => include/soc/loongson}/se.h | 52 +-- 3 files changed, 355 insertions(+), 447 deletions(-) rename {arch/loongarch/include/asm => include/soc/loongson}/se.h (68%)
diff --git a/drivers/char/loongson_se.c b/drivers/char/loongson_se.c index 40d2d6518265..853f7e10cd41 100644 --- a/drivers/char/loongson_se.c +++ b/drivers/char/loongson_se.c @@ -1,67 +1,61 @@ -// SPDX-License-Identifier: GPL-2.0 +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2024 Loongson Technology Corporation Limited + */
-#include <linux/kernel.h> -#include <linux/delay.h> -#include <linux/init.h> -#include <linux/module.h> -#include <linux/errno.h> +#include <linux/acpi.h> #include <linux/cdev.h> +#include <linux/delay.h> #include <linux/device.h> -#include <linux/of.h> -#include <linux/of_address.h> -#include <linux/platform_device.h> -#include <linux/miscdevice.h> -#include <linux/iopoll.h> #include <linux/dma-mapping.h> +#include <linux/errno.h> +#include <linux/init.h> #include <linux/interrupt.h> -#include <asm/se.h> - -static int se_mem_size = 0x800000; -module_param(se_mem_size, int, 0444); -MODULE_PARM_DESC(se_mem_size, "LOONGSON SE shared memory size"); - -static int se_mem_page = PAGE_SIZE; -module_param(se_mem_page, int, 0444); -MODULE_PARM_DESC(se_mem_page, "LOONGSON SE shared memory page size"); - -static struct loongson_se se_dev; - -static int lsse_open(struct inode *inode, struct file *filp) -{ - return 0; -} - -static ssize_t lsse_write(struct file *filp, const char __user *buf, - size_t cnt, loff_t *offt) -{ - return 0; -} - -static const struct file_operations lsse_fops = { - .owner = THIS_MODULE, - .open = lsse_open, - .write = lsse_write, -}; - -static const struct miscdevice lsse_miscdev = { - .minor = MISC_DYNAMIC_MINOR, - .name = "loongson-se", - .fops = &lsse_fops, -}; - -static inline u32 se_readl(u64 addr) +#include <linux/iopoll.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/platform_device.h> +#include <soc/loongson/se.h> + +/* + * The Loongson Security Module provides the control for hardware + * encryption acceleration child devices. The SE framework is + * shown as follows: + * + * +------------+ + * | CPU | + * +------------+ + * ^ ^ + * DMA | | IRQ + * v v + * +-----------------------------------+ + * | Loongson Security Module | + * +-----------------------------------+ + * ^ ^ + * chnnel0 | channel1 | + * v v + * +-----------+ +----------+ + * | sub-dev0 | | sub-dev1 | ..... Max sub-dev31 + * +-----------+ +----------+ + * + * The CPU cannot directly communicate with SE's sub devices, + * but sends commands to SE, which processes the commands and + * sends them to the corresponding sub devices. + */ + +static inline u32 se_readl(struct loongson_se *se, u32 off) { - return readl(se_dev.base + addr); + return readl(se->base + off); }
-static inline void se_writel(u32 val, u64 addr) +static inline void se_writel(struct loongson_se *se, u32 val, u32 off) { - writel(val, se_dev.base + addr); + writel(val, se->base + off); }
static inline bool se_ch_status(struct loongson_se *se, u32 int_bit) { - return !!(se->ch_status & int_bit) == 1; + return !!(se->ch_status & int_bit); }
static void se_enable_int(struct loongson_se *se, u32 int_bit) @@ -69,14 +63,11 @@ static void se_enable_int(struct loongson_se *se, u32 int_bit) unsigned long flag; u32 tmp;
- if (!int_bit) - return; - spin_lock_irqsave(&se->dev_lock, flag);
- tmp = se_readl(SE_S2LINT_EN); + tmp = se_readl(se, SE_S2LINT_EN); tmp |= int_bit; - se_writel(tmp, SE_S2LINT_EN); + se_writel(se, tmp, SE_S2LINT_EN);
spin_unlock_irqrestore(&se->dev_lock, flag); } @@ -86,50 +77,43 @@ static void se_disable_int(struct loongson_se *se, u32 int_bit) unsigned long flag; u32 tmp;
- if (!int_bit) - return; - spin_lock_irqsave(&se->dev_lock, flag);
- tmp = se_readl(SE_S2LINT_EN); + tmp = se_readl(se, SE_S2LINT_EN); tmp &= ~(int_bit); - se_writel(tmp, SE_S2LINT_EN); + se_writel(se, tmp, SE_S2LINT_EN);
spin_unlock_irqrestore(&se->dev_lock, flag); }
-static int se_send_requeset(struct loongson_se *se, - struct se_mailbox_data *req) +static int se_send_requeset(struct loongson_se *se, struct se_data *req) { unsigned long flag; u32 status; - int err = 0; + int err; int i;
if (!se || !req) return -EINVAL;
- if (se_readl(SE_L2SINT_STAT) || - !(se_readl(SE_L2SINT_EN) & req->int_bit)) + if (se_readl(se, SE_L2SINT_STAT) || + !(se_readl(se, SE_L2SINT_EN) & req->int_bit)) return -EBUSY;
spin_lock_irqsave(&se->cmd_lock, flag);
- for (i = 0; i < ARRAY_SIZE(req->u.mailbox); i++) - se_writel(req->u.mailbox[i], SE_MAILBOX_S + i * 4); - - se_writel(req->int_bit, SE_L2SINT_SET); - + for (i = 0; i < ARRAY_SIZE(req->u.data); i++) + se_writel(se, req->u.data[i], SE_DATA_S + i * 4); + se_writel(se, req->int_bit, SE_L2SINT_SET); err = readl_relaxed_poll_timeout_atomic(se->base + SE_L2SINT_STAT, status, - !(status & req->int_bit), 10, 10000); + !(status & req->int_bit), 10, 10000);
spin_unlock_irqrestore(&se->cmd_lock, flag);
return err; }
-static int se_get_response(struct loongson_se *se, - struct se_mailbox_data *res) +static int se_get_response(struct loongson_se *se, struct se_data *res) { unsigned long flag; int i; @@ -137,15 +121,14 @@ static int se_get_response(struct loongson_se *se, if (!se || !res) return -EINVAL;
- if ((se_readl(SE_S2LINT_STAT) & res->int_bit) == 0) + if ((se_readl(se, SE_S2LINT_STAT) & res->int_bit) == 0) return -EBUSY;
spin_lock_irqsave(&se->cmd_lock, flag);
- for (i = 0; i < ARRAY_SIZE(res->u.mailbox); i++) - res->u.mailbox[i] = se_readl(SE_MAILBOX_L + i * 4); - - se_writel(res->int_bit, SE_S2LINT_CL); + for (i = 0; i < ARRAY_SIZE(res->u.data); i++) + res->u.data[i] = se_readl(se, SE_DATA_L + i * 4); + se_writel(se, res->int_bit, SE_S2LINT_CL);
spin_unlock_irqrestore(&se->cmd_lock, flag);
@@ -153,10 +136,8 @@ static int se_get_response(struct loongson_se *se, }
static int loongson_se_get_res(struct loongson_se *se, u32 int_bit, u32 cmd, - struct se_mailbox_data *res) + struct se_data *res) { - int err = 0; - res->int_bit = int_bit;
if (se_get_response(se, res)) { @@ -165,21 +146,19 @@ static int loongson_se_get_res(struct loongson_se *se, u32 int_bit, u32 cmd, }
/* Check response */ - if (res->u.res.cmd == cmd) - err = 0; - else { + if (res->u.res.cmd != cmd) { dev_err(se->dev, "Response cmd is 0x%x, not expect cmd 0x%x.\n", - res->u.res.cmd, cmd); - err = -EFAULT; + res->u.res.cmd, cmd); + return -EFAULT; }
- return err; + return 0; }
-static int se_send_genl_cmd(struct loongson_se *se, struct se_mailbox_data *req, - struct se_mailbox_data *res, int retry) +static int se_send_genl_cmd(struct loongson_se *se, struct se_data *req, + struct se_data *res, int retry) { - int err = 0, cnt = 0; + int err, cnt = 0;
try_again: if (cnt++ >= retry) { @@ -193,12 +172,10 @@ static int se_send_genl_cmd(struct loongson_se *se, struct se_mailbox_data *req, if (err) goto try_again;
- if (!wait_for_completion_timeout(&se->cmd_completion, - msecs_to_jiffies(0x1000))) { + if (!wait_for_completion_timeout(&se->cmd_completion, HZ)) { se_enable_int(se, req->int_bit); goto try_again; } - err = loongson_se_get_res(se, req->int_bit, req->u.gcmd.cmd, res); if (err || res->u.res.cmd_ret) { se_enable_int(se, req->int_bit); @@ -214,8 +191,8 @@ static int se_send_genl_cmd(struct loongson_se *se, struct se_mailbox_data *req, static int loongson_se_set_msg(struct lsse_ch *ch) { struct loongson_se *se = ch->se; - struct se_mailbox_data req = {0}; - struct se_mailbox_data res = {0}; + struct se_data req = {0}; + struct se_data res = {0}; int err;
req.int_bit = SE_INT_SETUP; @@ -225,8 +202,8 @@ static int loongson_se_set_msg(struct lsse_ch *ch) req.u.gcmd.info[1] = ch->smsg - se->mem_base; req.u.gcmd.info[2] = ch->msg_size;
- dev_dbg(se->dev, "Set Channel %d msg off 0x%x, msg size %d\n", ch->id, - req.u.gcmd.info[1], req.u.gcmd.info[2]); + dev_dbg(se->dev, "Set Channel %d msg off 0x%x, msg size %d\n", + ch->id, req.u.gcmd.info[1], req.u.gcmd.info[2]);
err = se_send_genl_cmd(se, &req, &res, 5); if (res.u.res.cmd_ret) @@ -235,13 +212,13 @@ static int loongson_se_set_msg(struct lsse_ch *ch) return err; }
-static irqreturn_t loongson_se_irq(int irq, void *dev_id) +static irqreturn_t se_irq(int irq, void *dev_id) { struct loongson_se *se = (struct loongson_se *)dev_id; struct lsse_ch *ch; u32 int_status;
- int_status = se_readl(SE_S2LINT_STAT); + int_status = se_readl(se, SE_S2LINT_STAT);
dev_dbg(se->dev, "%s int status is 0x%x\n", __func__, int_status);
@@ -259,32 +236,23 @@ static irqreturn_t loongson_se_irq(int irq, void *dev_id) if (ch->complete) ch->complete(ch); int_status &= ~BIT(id); - se_writel(BIT(id), SE_S2LINT_CL); + se_writel(se, BIT(id), SE_S2LINT_CL); }
return IRQ_HANDLED; }
-static int se_init_hw(struct loongson_se *se) +static int se_init_hw(struct loongson_se *se, dma_addr_t addr, int size) { - struct se_mailbox_data req = {0}; - struct se_mailbox_data res = {0}; - struct device *dev = se->dev; + struct se_data req; + struct se_data res; int err, retry = 5; - u64 size; - - size = se_mem_size; - - if (size & (size - 1)) { - size = roundup_pow_of_two(size); - se_mem_size = size; - }
se_enable_int(se, SE_INT_SETUP);
/* Start engine */ - memset(&req, 0, sizeof(struct se_mailbox_data)); - memset(&res, 0, sizeof(struct se_mailbox_data)); + memset(&req, 0, sizeof(struct se_data)); + memset(&res, 0, sizeof(struct se_data)); req.int_bit = SE_INT_SETUP; req.u.gcmd.cmd = SE_CMD_START; err = se_send_genl_cmd(se, &req, &res, retry); @@ -292,99 +260,82 @@ static int se_init_hw(struct loongson_se *se) return err;
/* Get Version */ - memset(&req, 0, sizeof(struct se_mailbox_data)); - memset(&res, 0, sizeof(struct se_mailbox_data)); + memset(&req, 0, sizeof(struct se_data)); + memset(&res, 0, sizeof(struct se_data)); req.int_bit = SE_INT_SETUP; req.u.gcmd.cmd = SE_CMD_GETVER; err = se_send_genl_cmd(se, &req, &res, retry); if (err) return err; - se->version = res.u.res.info[0];
- /* Setup data buffer */ - se->mem_base = dmam_alloc_coherent(dev, size, - &se->mem_addr, GFP_KERNEL); - if (!se->mem_base) - return -ENOMEM; - - memset(se->mem_base, 0, size); - - memset(&req, 0, sizeof(struct se_mailbox_data)); - memset(&res, 0, sizeof(struct se_mailbox_data)); + /* Set shared mem */ + memset(&req, 0, sizeof(struct se_data)); + memset(&res, 0, sizeof(struct se_data)); req.int_bit = SE_INT_SETUP; req.u.gcmd.cmd = SE_CMD_SETBUF; /* MMAP */ - req.u.gcmd.info[0] = (se->mem_addr & 0xffffffff) | 0x80; - req.u.gcmd.info[1] = se->mem_addr >> 32; + req.u.gcmd.info[0] = addr & 0xffffffff; + req.u.gcmd.info[1] = addr >> 32; /* MASK */ req.u.gcmd.info[2] = ~(size - 1); req.u.gcmd.info[3] = 0xffffffff; - - pr_debug("Set win mmap 0x%llx, mask 0x%llx\n", - ((u64)req.u.gcmd.info[1] << 32) | req.u.gcmd.info[0], - ((u64)req.u.gcmd.info[3] << 32) | req.u.gcmd.info[2]); - err = se_send_genl_cmd(se, &req, &res, retry); if (err) return err; - - se->mem_map_size = size / se_mem_page; - se->mem_map = bitmap_zalloc(se->mem_map_size, GFP_KERNEL); - if (!se->mem_map) - return -ENOMEM; - - dev_info(se->dev, "SE module setup down, shared memory size is 0x%x bytes, memory page size is 0x%x bytes\n", - se_mem_size, se_mem_page); + pr_debug("Set win mmap 0x%llx, mask 0x%llx\n", + ((u64)req.u.gcmd.info[1] << 32) | req.u.gcmd.info[0], + ((u64)req.u.gcmd.info[3] << 32) | req.u.gcmd.info[2]);
return err; }
-static void loongson_se_disable_hw(struct loongson_se *se) +static void se_disable_hw(struct loongson_se *se) { - struct se_mailbox_data req = {0}; - struct se_mailbox_data res = {0}; - int retry = 5; + struct se_data req = {0}; + struct se_data res = {0};
/* Stop engine */ req.int_bit = SE_INT_SETUP; req.u.gcmd.cmd = SE_CMD_STOP; - se_send_genl_cmd(se, &req, &res, retry); - + se_send_genl_cmd(se, &req, &res, 5); se_disable_int(se, SE_INT_ALL); - kfree(se->mem_map); }
+/* + * Called by SE's child device driver. + */ int se_send_ch_requeset(struct lsse_ch *ch) { struct loongson_se *se; u32 status, int_bit; - int err = 0; - - if (!ch) - return -EINVAL;
se = ch->se; int_bit = ch->int_bit; - - if ((se_readl(SE_L2SINT_STAT) & int_bit) || - !(se_readl(SE_L2SINT_EN) & int_bit)) + if ((se_readl(se, SE_L2SINT_STAT) & int_bit) || + !(se_readl(se, SE_L2SINT_EN) & int_bit)) return -EBUSY;
se_enable_int(se, int_bit); - se_writel(int_bit, SE_L2SINT_SET); + se_writel(se, int_bit, SE_L2SINT_SET);
- err = readl_relaxed_poll_timeout_atomic(se->base + SE_L2SINT_STAT, status, - !(status & int_bit), 10, 10000); + return readl_relaxed_poll_timeout_atomic(se->base + SE_L2SINT_STAT, status, + !(status & int_bit), 10, 10000);
- return err; } EXPORT_SYMBOL_GPL(se_send_ch_requeset);
-struct lsse_ch *se_init_ch(int id, int data_size, int msg_size, void *priv, - void (*complete)(struct lsse_ch *se_ch)) +/* + * se_init_ch() - Init the channel used by child device. + * + * Allocate the shared memory agreed upon with SE on SE probe, + * and register the callback function when the data processing + * in this channel is completed. + */ +struct lsse_ch *se_init_ch(struct device *dev, int id, int data_size, int msg_size, + void *priv, void (*complete)(struct lsse_ch *se_ch)) { - struct loongson_se *se = &se_dev; + struct loongson_se *se = dev_get_drvdata(dev); struct lsse_ch *ch; unsigned long flag; int data_first, data_nr; @@ -395,7 +346,7 @@ struct lsse_ch *se_init_ch(int id, int data_size, int msg_size, void *priv, return NULL; }
- if (id == 0 || id > SE_CH_MAX) { + if (id > SE_CH_MAX) { dev_err(se->dev, "Channel number %d is invalid\n", id); return NULL; } @@ -407,30 +358,30 @@ struct lsse_ch *se_init_ch(int id, int data_size, int msg_size, void *priv,
spin_lock_irqsave(&se->dev_lock, flag);
- ch = &se_dev.chs[id]; + ch = &se->chs[id]; ch->se = se; ch->id = id; ch->int_bit = BIT(id); se->ch_status |= BIT(id);
- data_nr = round_up(data_size, se_mem_page) / se_mem_page; - data_first = bitmap_find_next_zero_area(se->mem_map, se->mem_map_size, - 0, data_nr, 0); - if (data_first >= se->mem_map_size) { + data_nr = round_up(data_size, PAGE_SIZE) / PAGE_SIZE; + data_first = bitmap_find_next_zero_area(se->mem_map, se->mem_map_pages, + 0, data_nr, 0); + if (data_first >= se->mem_map_pages) { dev_err(se->dev, "Insufficient memory space\n"); spin_unlock_irqrestore(&se->dev_lock, flag); return NULL; }
bitmap_set(se->mem_map, data_first, data_nr); - ch->data_buffer = se->mem_base + data_first * se_mem_page; - ch->data_addr = se->mem_addr + data_first * se_mem_page; + ch->data_buffer = se->mem_base + data_first * PAGE_SIZE; + ch->data_addr = se->mem_addr + data_first * PAGE_SIZE; ch->data_size = data_size;
- msg_nr = round_up(msg_size, se_mem_page) / se_mem_page; - msg_first = bitmap_find_next_zero_area(se->mem_map, se->mem_map_size, - 0, msg_nr, 0); - if (msg_first >= se->mem_map_size) { + msg_nr = round_up(msg_size, PAGE_SIZE) / PAGE_SIZE; + msg_first = bitmap_find_next_zero_area(se->mem_map, se->mem_map_pages, + 0, msg_nr, 0); + if (msg_first >= se->mem_map_pages) { dev_err(se->dev, "Insufficient memory space\n"); bitmap_clear(se->mem_map, data_first, data_nr); spin_unlock_irqrestore(&se->dev_lock, flag); @@ -438,13 +389,11 @@ struct lsse_ch *se_init_ch(int id, int data_size, int msg_size, void *priv, }
bitmap_set(se->mem_map, msg_first, msg_nr); - ch->smsg = se->mem_base + msg_first * se_mem_page; + ch->smsg = se->mem_base + msg_first * PAGE_SIZE; ch->rmsg = ch->smsg + msg_size / 2; ch->msg_size = msg_size; - ch->complete = complete; ch->priv = priv; - spin_lock_init(&ch->ch_lock);
spin_unlock_irqrestore(&se->dev_lock, flag); @@ -462,7 +411,7 @@ EXPORT_SYMBOL_GPL(se_init_ch);
void se_deinit_ch(struct lsse_ch *ch) { - struct loongson_se *se = &se_dev; + struct loongson_se *se = ch->se; unsigned long flag; int first, nr; int id = ch->id; @@ -472,7 +421,7 @@ void se_deinit_ch(struct lsse_ch *ch) return; }
- if (id == 0 || id > SE_CH_MAX) { + if (id > SE_CH_MAX) { dev_err(se->dev, "Channel number %d is invalid\n", id); return; } @@ -483,119 +432,92 @@ void se_deinit_ch(struct lsse_ch *ch) }
spin_lock_irqsave(&se->dev_lock, flag); - se->ch_status &= ~BIT(ch->id);
- first = (ch->data_buffer - se->mem_base) / se_mem_page; - nr = round_up(ch->data_size, se_mem_page) / se_mem_page; + first = (ch->data_buffer - se->mem_base) / PAGE_SIZE; + nr = round_up(ch->data_size, PAGE_SIZE) / PAGE_SIZE; bitmap_clear(se->mem_map, first, nr);
- first = (ch->smsg - se->mem_base) / se_mem_page; - nr = round_up(ch->msg_size, se_mem_page) / se_mem_page; + first = (ch->smsg - se->mem_base) / PAGE_SIZE; + nr = round_up(ch->msg_size, PAGE_SIZE) / PAGE_SIZE; bitmap_clear(se->mem_map, first, nr);
+ se_disable_int(se, ch->int_bit); spin_unlock_irqrestore(&se->dev_lock, flag);
- se_disable_int(se, ch->int_bit); } EXPORT_SYMBOL_GPL(se_deinit_ch);
-static struct platform_device lsse_sdf_pdev = { - .name = "loongson-sdf", - .id = -1, -}; - -static const struct of_device_id loongson_se_of_match[] = { - { .compatible = "loongson,ls3c6000se", }, - {} -}; -MODULE_DEVICE_TABLE(of, loongson_se_of_match); - static int loongson_se_probe(struct platform_device *pdev) { - struct loongson_se *se = &se_dev; - struct resource *res; + struct loongson_se *se; struct device *dev = &pdev->dev; - int nr_irq, err, i; - int irq[8]; - - nr_irq = platform_irq_count(pdev); - if (nr_irq < 0) - return -ENODEV; - - for (i = 0; i < nr_irq; i++) { - irq[i] = platform_get_irq(pdev, i); - if (irq[i] < 0) - return -ENODEV; - } + int nr_irq, irq, err, size;
- res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!res) + se = devm_kmalloc(dev, sizeof(*se), GFP_KERNEL); + if (!se) + return -ENOMEM; + se->dev = dev; + dev_set_drvdata(dev, se); + init_completion(&se->cmd_completion); + spin_lock_init(&se->cmd_lock); + spin_lock_init(&se->dev_lock); + /* Setup DMA buffer */ + dma_set_mask_and_coherent(dev, DMA_BIT_MASK(64)); + if (device_property_read_u32(dev, "dmam_size", &size)) return -ENODEV; + size = roundup_pow_of_two(size); + se->mem_base = dmam_alloc_coherent(dev, size, &se->mem_addr, GFP_KERNEL); + if (!se->mem_base) + return -ENOMEM; + se->mem_map_pages = size / PAGE_SIZE; + se->mem_map = devm_bitmap_zalloc(dev, se->mem_map_pages, GFP_KERNEL); + if (!se->mem_map) + return -ENOMEM;
- se->base = devm_ioremap_resource(dev, res); + se->base = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(se->base)) return PTR_ERR(se->base);
- se->dev = &pdev->dev; - platform_set_drvdata(pdev, se); - dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(64)); - init_completion(&se->cmd_completion); - spin_lock_init(&se->cmd_lock); - spin_lock_init(&se->dev_lock); - - for (i = 0; i < nr_irq; i++) { - err = devm_request_irq(dev, irq[i], loongson_se_irq, 0, - "loongson-se", se); + nr_irq = platform_irq_count(pdev); + if (nr_irq <= 0) + return -ENODEV; + while (nr_irq) { + irq = platform_get_irq(pdev, --nr_irq); + if (irq < 0) + return -ENODEV; + /* Use the same interrupt handler address. + * Determine which irq it is accroding + * SE_S2LINT_STAT register. + */ + err = devm_request_irq(dev, irq, se_irq, 0, + "loongson-se", se); if (err) - goto out; + dev_err(dev, "failed to request irq: %d\n", err); }
- err = se_init_hw(se); - if (err) - goto disable_hw; - - err = misc_register(&lsse_miscdev); + err = se_init_hw(se, se->mem_addr, size); if (err) - goto disable_hw; - - err = platform_device_register(&lsse_sdf_pdev); - if (err) - pr_err("register sdf device failed\n"); - - return 0; - -disable_hw: - loongson_se_disable_hw(se); -out: - for ( ; i >= 0; i--) - devm_free_irq(dev, irq[i], se); + se_disable_hw(se);
return err; }
-static int loongson_se_remove(struct platform_device *pdev) -{ - struct loongson_se *se = platform_get_drvdata(pdev); - - misc_deregister(&lsse_miscdev); - loongson_se_disable_hw(se); - platform_device_unregister(&lsse_sdf_pdev); - - return 0; -} +static const struct acpi_device_id loongson_se_acpi_match[] = { + {"LOON0011", 0}, + {} +}; +MODULE_DEVICE_TABLE(acpi, loongson_se_acpi_match);
static struct platform_driver loongson_se_driver = { .probe = loongson_se_probe, - .remove = loongson_se_remove, .driver = { .name = "loongson-se", - .of_match_table = loongson_se_of_match, + .acpi_match_table = loongson_se_acpi_match, }, }; - module_platform_driver(loongson_se_driver);
-MODULE_AUTHOR("Yinggang Gu"); -MODULE_DESCRIPTION("Loongson SE driver"); MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Loongson Technology Corporation"); +MODULE_DESCRIPTION("Loongson Security Module driver"); diff --git a/drivers/char/lsse_sdf_cdev.c b/drivers/char/lsse_sdf_cdev.c index a4806fbf08d1..3bbe3cbb35ae 100644 --- a/drivers/char/lsse_sdf_cdev.c +++ b/drivers/char/lsse_sdf_cdev.c @@ -1,25 +1,29 @@ -// SPDX-License-Identifier: GPL-2.0 +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2024 Loongson Technology Corporation Limited + */
-#include <linux/kernel.h> -#include <linux/slab.h> +#include <linux/acpi.h> +#include <linux/cdev.h> #include <linux/init.h> +#include <linux/iopoll.h> +#include <linux/kernel.h> +#include <linux/list.h> #include <linux/miscdevice.h> #include <linux/module.h> -#include <linux/cdev.h> -#include <linux/module.h> -#include <linux/wait.h> #include <linux/of.h> -#include <linux/iopoll.h> #include <linux/platform_device.h> +#include <linux/slab.h> #include <linux/uaccess.h> -#include <asm/se.h> -#include <linux/list.h> +#include <linux/wait.h> +#include <soc/loongson/se.h>
#define SE_SDF_BUFSIZE (PAGE_SIZE * 2) -#define SDF_OPENSESSION 0x204 -#define SDF_CLOSESESSION 0x205 +#define SDF_OPENSESSION (0x204) +#define SDF_CLOSESESSION (0x205)
-struct lsse_sdf_dev { +struct sdf_dev { + struct miscdevice miscdev; struct lsse_ch *se_ch; struct mutex data_lock; bool processing_cmd; @@ -49,37 +53,32 @@ struct sdf_kernel_command { void *handle; };
-#define KERNEL_COMMAND_SIZE (sizeof(struct sdf_kernel_command)) - struct sdf_handle { struct list_head handle_list; void *handle; };
struct sdf_file_pvt_data { - struct lsse_sdf_dev *se; + struct sdf_dev *se; struct list_head handle_list; struct sdf_kernel_command skc; struct sdf_handle *ph; };
-static struct lsse_sdf_dev *se_sdf_dev; - -static void lsse_sdf_complete(struct lsse_ch *ch) +static void sdf_complete(struct lsse_ch *ch) { - struct lsse_sdf_dev *se = (struct lsse_sdf_dev *)ch->priv; + struct sdf_dev *se = (struct sdf_dev *)ch->priv;
se->processing_cmd = false; wake_up(&se->wq); }
-static int se_send_sdf_cmd(struct lsse_sdf_dev *se, int len, int retry) +static int se_send_sdf_cmd(struct sdf_dev *se, int len, int retry) { struct se_sdf_msg *smsg = (struct se_sdf_msg *)se->se_ch->smsg; - unsigned long flag; int err;
- spin_lock_irqsave(&se->se_ch->ch_lock, flag); + spin_lock_irq(&se->se_ch->ch_lock);
smsg->cmd = SE_CMD_SDF; /* One time one cmd */ @@ -90,8 +89,6 @@ static int se_send_sdf_cmd(struct lsse_sdf_dev *se, int len, int retry) if (!retry--) goto out;
- pr_debug("Send sdf cmd, last retry %d times\n", retry); - err = se_send_ch_requeset(se->se_ch); if (err) { udelay(5); @@ -99,27 +96,20 @@ static int se_send_sdf_cmd(struct lsse_sdf_dev *se, int len, int retry) }
out: - spin_unlock_irqrestore(&se->se_ch->ch_lock, flag); + spin_unlock_irq(&se->se_ch->ch_lock);
return err; }
-static int lsse_sdf_recv(struct sdf_file_pvt_data *pvt, char *buf, - size_t size, int user, int *se_ret) +static int sdf_recvu(struct sdf_file_pvt_data *pvt, char __user *buf, int *se_ret) { - int len, time, ret = 0; - struct se_sdf_msg *rmsg; + struct sdf_dev *se = pvt->se; struct sdf_kernel_command *skc; + struct se_sdf_msg *rmsg; struct sdf_handle *ph; - struct lsse_sdf_dev *se = pvt->se; - - if (!se->se_ch->rmsg) { - pr_err("se device is not ready\n"); - return -EBUSY; - } + int ret;
- time = wait_event_timeout(se->wq, !se->processing_cmd, HZ*30); - if (!time) + if (!wait_event_timeout(se->wq, !se->processing_cmd, HZ*10)) return -ETIME;
rmsg = (struct se_sdf_msg *)se->se_ch->rmsg; @@ -127,36 +117,25 @@ static int lsse_sdf_recv(struct sdf_file_pvt_data *pvt, char *buf, pr_err("se get wrong response\n"); return -EIO; } - len = rmsg->data_len; - - if ((!user && len > KERNEL_COMMAND_SIZE) || len > SE_SDF_BUFSIZE - || (size && len > size)) - return -E2BIG;
- if (user) { - ret = copy_to_user((char __user *)buf, - se->se_ch->data_buffer + rmsg->data_off, len); - if (!se_ret) - return ret; - - skc = (struct sdf_kernel_command *) - (se->se_ch->data_buffer + rmsg->data_off); - *se_ret = skc->header.u.ret; - if (skc->header.command == SDF_OPENSESSION && !*se_ret) { - ph = kmalloc(sizeof(*ph), GFP_KERNEL); - if (!ph) - return -ENOMEM; - ph->handle = skc->handle; - list_add(&ph->handle_list, &pvt->handle_list); - } - } else - memcpy(buf, se->se_ch->data_buffer + rmsg->data_off, len); + ret = copy_to_user((char __user *)buf, + se->se_ch->data_buffer + rmsg->data_off, rmsg->data_len); + + skc = (struct sdf_kernel_command *)(se->se_ch->data_buffer + rmsg->data_off); + *se_ret = skc->header.u.ret; + if (skc->header.command == SDF_OPENSESSION && !*se_ret) { + ph = kmalloc(sizeof(*ph), GFP_KERNEL); + if (!ph) + return -ENOMEM; + ph->handle = skc->handle; + list_add(&ph->handle_list, &pvt->handle_list); + }
return ret; }
static struct sdf_handle *find_sdf_handle(void *handle, - struct sdf_file_pvt_data *pvt) + struct sdf_file_pvt_data *pvt) { struct sdf_handle *ph;
@@ -168,43 +147,23 @@ static struct sdf_handle *find_sdf_handle(void *handle, return NULL; }
-static int lsse_sdf_send(struct sdf_file_pvt_data *pvt, const char *buf, - size_t count, int user) +static int sdf_sendu(struct sdf_file_pvt_data *pvt, + const char __user *buf, size_t count) { - int ret, se_ret; - struct sdf_handle *ph = NULL; + struct sdf_dev *se = pvt->se; struct sdf_kernel_command *skc; - struct lsse_sdf_dev *se = pvt->se; + struct sdf_handle *ph = NULL; + int ret, se_ret;
- if (!se->se_ch->smsg) { - pr_err("se device is not ready\n"); - return 0; - } + mutex_lock(&se->data_lock);
- if (count > se->se_ch->data_size) { - pr_err("Invalid size in send: count=%zd, size=%d\n", - count, se->se_ch->data_size); - return -EIO; + if (copy_from_user(se->se_ch->data_buffer, buf, count)) { + ret = -EFAULT; + goto out_unlock; } - - if (user) { - ret = mutex_lock_interruptible(&se->data_lock); - if (ret) - goto out; - } else - mutex_lock(&se->data_lock); - - if (user) { - ret = copy_from_user(se->se_ch->data_buffer, buf, count); - if (ret) { - ret = -EFAULT; - goto out_unlock; - } - skc = (struct sdf_kernel_command *)se->se_ch->data_buffer; - if (skc->header.command == SDF_CLOSESESSION) - ph = find_sdf_handle(skc->handle, pvt); - } else - memcpy(se->se_ch->data_buffer, buf, count); + skc = (struct sdf_kernel_command *)se->se_ch->data_buffer; + if (skc->header.command == SDF_CLOSESESSION) + ph = find_sdf_handle(skc->handle, pvt);
se->processing_cmd = true; ret = se_send_sdf_cmd(se, count, 5); @@ -213,65 +172,107 @@ static int lsse_sdf_send(struct sdf_file_pvt_data *pvt, const char *buf, goto out_unlock; }
- ret = lsse_sdf_recv(pvt, (char *)buf, 0, user, &se_ret); + ret = sdf_recvu(pvt, (char __user *)buf, &se_ret); if (ret) { pr_err("recv failed ret: %x\n", ret); goto out_unlock; } + if (ph && !se_ret) { list_del(&ph->handle_list); kfree(ph); } + out_unlock: mutex_unlock(&se->data_lock); -out: + return ret; }
-static ssize_t lsse_sdf_write(struct file *filp, const char __user *buf, - size_t cnt, loff_t *offt) +static ssize_t sdf_write(struct file *filp, const char __user *buf, + size_t cnt, loff_t *offt) { struct sdf_file_pvt_data *pvt = filp->private_data;
if (cnt > SE_SDF_BUFSIZE) return -E2BIG;
- if (lsse_sdf_send(pvt, buf, cnt, 1)) + if (sdf_sendu(pvt, buf, cnt)) return -EFAULT;
return cnt; }
-static ssize_t lsse_sdf_read(struct file *filp, char __user *buf, - size_t size, loff_t *off) +static int sdf_recvk(struct sdf_file_pvt_data *pvt, char *buf) +{ + struct sdf_dev *se = pvt->se; + struct se_sdf_msg *rmsg; + int time; + + time = wait_event_timeout(se->wq, !se->processing_cmd, HZ*10); + if (!time) + return -ETIME; + + rmsg = (struct se_sdf_msg *)se->se_ch->rmsg; + if (rmsg->cmd != SE_CMD_SDF) { + pr_err("se get wrong response\n"); + return -EIO; + } + memcpy(buf, se->se_ch->data_buffer + rmsg->data_off, rmsg->data_len); + + return 0; +} + +static int sdf_sendk(struct sdf_file_pvt_data *pvt, char *buf, size_t count) { - return lsse_sdf_recv(filp->private_data, buf, size, 1, NULL); + struct sdf_dev *se = pvt->se; + int ret; + + mutex_lock(&se->data_lock); + + memcpy(se->se_ch->data_buffer, buf, count); + se->processing_cmd = true; + ret = se_send_sdf_cmd(se, count, 5); + if (ret) { + pr_err("se_send_sdf_cmd failed\n"); + goto out_unlock; + } + + ret = sdf_recvk(pvt, buf); + if (ret) + pr_err("recv failed ret: %x\n", ret); + +out_unlock: + mutex_unlock(&se->data_lock); + + return ret; }
static int close_one_handle(struct sdf_file_pvt_data *pvt, struct sdf_handle *ph) { struct sdf_kernel_command *skc = &pvt->skc; + int ret;
- skc->header.command = 0x205; + skc->header.command = SDF_CLOSESESSION; skc->header.u.param_cnt = 1; - skc->header.param_len[0] = 8; skc->handle = ph->handle; + skc->header.param_len[0] = sizeof(skc->handle); /* close one session */ - lsse_sdf_send(pvt, (char *)&pvt->skc, KERNEL_COMMAND_SIZE, 0); + ret = sdf_sendk(pvt, (char *)&pvt->skc, sizeof(*skc)); if (skc->header.u.ret) { pr_err("Auto Close Session failed, session handle: %llx, ret: %d\n", - (u64)ph->handle, skc->header.u.ret); + (u64)ph->handle, skc->header.u.ret); return skc->header.u.ret; } kfree(ph);
- return 0; + return ret; }
static int close_all_handle(struct sdf_file_pvt_data *pvt) { - int ret = 0; struct sdf_handle *ph, *tmp; + int ret;
list_for_each_entry_safe(ph, tmp, &pvt->handle_list, handle_list) { list_del(&ph->handle_list); @@ -283,98 +284,95 @@ static int close_all_handle(struct sdf_file_pvt_data *pvt) return 0; }
-static int lsse_sdf_release(struct inode *inode, struct file *filp) +static int sdf_release(struct inode *inode, struct file *filp) { - int ret; struct sdf_file_pvt_data *pvt = filp->private_data; + int ret;
ret = close_all_handle(pvt); - filp->private_data = NULL; kfree(pvt);
- if (ret) - ret = -EFAULT; return ret; }
-static int lsse_sdf_open(struct inode *inode, struct file *filp) +static int sdf_open(struct inode *inode, struct file *filp) { - struct sdf_file_pvt_data *pvt = kmalloc(sizeof(*pvt), GFP_KERNEL); + struct sdf_file_pvt_data *pvt;
+ pvt = kmalloc(sizeof(*pvt), GFP_KERNEL); if (!pvt) return -ENOMEM;
INIT_LIST_HEAD(&pvt->handle_list); - pvt->se = se_sdf_dev; + pvt->se = container_of(filp->private_data, + struct sdf_dev, miscdev); filp->private_data = pvt;
return 0; }
-static const struct file_operations lsse_sdf_fops = { +static const struct file_operations sdf_fops = { .owner = THIS_MODULE, - .open = lsse_sdf_open, - .write = lsse_sdf_write, - .read = lsse_sdf_read, - .release = lsse_sdf_release, + .open = sdf_open, + .write = sdf_write, + .release = sdf_release, };
-static const struct miscdevice lsse_sdf_miscdev = { - .minor = MISC_DYNAMIC_MINOR, - .name = "lsse_sdf", - .fops = &lsse_sdf_fops, -}; - -static int lsse_sdf_probe(struct platform_device *pdev) +static int sdf_probe(struct platform_device *pdev) { - int msg_size; - int ret; + struct sdf_dev *sdf; + int msg_size, ret, ch;
- se_sdf_dev = kzalloc(sizeof(*se_sdf_dev), GFP_KERNEL); - if (IS_ERR_OR_NULL(se_sdf_dev)) - return PTR_ERR(se_sdf_dev); - - mutex_init(&se_sdf_dev->data_lock); - init_waitqueue_head(&se_sdf_dev->wq); - se_sdf_dev->processing_cmd = false; + sdf = devm_kzalloc(&pdev->dev, sizeof(*sdf), GFP_KERNEL); + if (!sdf) + return -ENOMEM; + mutex_init(&sdf->data_lock); + init_waitqueue_head(&sdf->wq); + sdf->processing_cmd = false; + platform_set_drvdata(pdev, sdf);
+ if (device_property_read_u32(&pdev->dev, "channel", &ch)) + return -ENODEV; msg_size = 2 * sizeof(struct se_sdf_msg); - se_sdf_dev->se_ch = se_init_ch(SE_CH_SDF, SE_SDF_BUFSIZE, msg_size, - se_sdf_dev, lsse_sdf_complete); + sdf->se_ch = se_init_ch(pdev->dev.parent, ch, SE_SDF_BUFSIZE, + msg_size, sdf, sdf_complete);
- ret = misc_register(&lsse_sdf_miscdev); - if (ret < 0) { + sdf->miscdev.minor = MISC_DYNAMIC_MINOR; + sdf->miscdev.name = "lsse_sdf"; + sdf->miscdev.fops = &sdf_fops; + ret = misc_register(&sdf->miscdev); + if (ret) pr_err("register sdf dev failed!\n"); - goto out; - } - - return 0; - -out: - kfree(se_sdf_dev);
return ret; }
-static int lsse_sdf_remove(struct platform_device *pdev) +static int sdf_remove(struct platform_device *pdev) { - misc_deregister(&lsse_sdf_miscdev); - se_deinit_ch(se_sdf_dev->se_ch); - kfree(se_sdf_dev); + struct sdf_dev *sdf = platform_get_drvdata(pdev); + + misc_deregister(&sdf->miscdev); + se_deinit_ch(sdf->se_ch);
return 0; }
+static const struct acpi_device_id loongson_sdf_acpi_match[] = { + {"LOON0012", 0}, + {} +}; +MODULE_DEVICE_TABLE(acpi, loongson_sdf_acpi_match); + static struct platform_driver loongson_sdf_driver = { - .probe = lsse_sdf_probe, - .remove = lsse_sdf_remove, + .probe = sdf_probe, + .remove = sdf_remove, .driver = { .name = "loongson-sdf", + .acpi_match_table = loongson_sdf_acpi_match, }, }; module_platform_driver(loongson_sdf_driver);
-MODULE_ALIAS("platform:loongson-sdf"); -MODULE_AUTHOR("Yinggang Gu"); -MODULE_DESCRIPTION("Loongson SE sdf driver"); MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Loongson Technology Corporation"); +MODULE_DESCRIPTION("Loongson Secure Device Function driver"); diff --git a/arch/loongarch/include/asm/se.h b/include/soc/loongson/se.h similarity index 68% rename from arch/loongarch/include/asm/se.h rename to include/soc/loongson/se.h index d0f9d43d60d2..d9864e3f3b85 100644 --- a/arch/loongarch/include/asm/se.h +++ b/include/soc/loongson/se.h @@ -1,23 +1,11 @@ /* SPDX-License-Identifier: GPL-2.0 */ -/* - * Copyright (C) 2012 IBM Corporation - * - * Copyright 2023 Loongson Technology, Inc. - * Yinggang Gu guyinggang@loongson.cn - * - * Device driver for Loongson SE module. - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation, version 2 of the - * License. - * - */ +/* Copyright (C) 2024 Loongson Technology Corporation Limited */ + #ifndef __LOONGSON_SE_H__ #define __LOONGSON_SE_H__
-#define SE_MAILBOX_S 0x0 -#define SE_MAILBOX_L 0x20 +#define SE_DATA_S 0x0 +#define SE_DATA_L 0x20 #define SE_S2LINT_STAT 0x88 #define SE_S2LINT_EN 0x8c #define SE_S2LINT_SET 0x90 @@ -29,20 +17,20 @@
/* INT bit definition */ #define SE_INT_SETUP BIT(0) -#define SE_INT_SM2 BIT(0) -#define SE_INT_SM3 BIT(0) -#define SE_INT_SM4 BIT(0) -#define SE_INT_RNG BIT(0) -#define SE_INT_TPM BIT(5) -#define SE_INT_ALL 0xffffffff +#define SE_INT_SM2 BIT(0) +#define SE_INT_SM3 BIT(0) +#define SE_INT_SM4 BIT(0) +#define SE_INT_RNG BIT(0) +#define SE_INT_TPM BIT(5) +#define SE_INT_ALL 0xffffffff
#define SE_CMD_START 0x0 -#define SE_CMD_STOP 0x1 +#define SE_CMD_STOP 0x1 #define SE_CMD_GETVER 0x2 #define SE_CMD_SETBUF 0x3 #define SE_CMD_SETMSG 0x4
-#define SE_CMD_RNG 0x100 +#define SE_CMD_RNG 0x100
#define SE_CMD_SM2_SIGN 0x200 #define SE_CMD_SM2_VSIGN 0x201 @@ -57,11 +45,11 @@ #define SE_CMD_SM4_CBC_DECRY 0x403 #define SE_CMD_SM4_CTR 0x404
-#define SE_CMD_TPM 0x500 +#define SE_CMD_TPM 0x500 #define SE_CMD_ZUC_INIT_READ 0x600 #define SE_CMD_ZUC_READ 0x601
-#define SE_CMD_SDF 0x700 +#define SE_CMD_SDF 0x700
#define SE_CH_MAX 32
@@ -91,10 +79,10 @@ struct se_res { u32 info[6]; };
-struct se_mailbox_data { +struct se_data { u32 int_bit; union { - u32 mailbox[8]; + u32 data[8]; struct se_cmd gcmd; struct se_res res; } u; @@ -128,19 +116,19 @@ struct loongson_se { void *mem_base; dma_addr_t mem_addr; unsigned long *mem_map; - int mem_map_size; + int mem_map_pages; void *smsg; void *rmsg;
/* Synchronous CMD */ struct completion cmd_completion;
- /* Virtual Channel */ + /* Channel */ struct lsse_ch chs[SE_CH_MAX]; };
-struct lsse_ch *se_init_ch(int id, int data_size, int msg_size, void *priv, - void (*complete)(struct lsse_ch *se_ch)); +struct lsse_ch *se_init_ch(struct device *dev, int id, int data_size, int msg_size, + void *priv, void (*complete)(struct lsse_ch *se_ch)); void se_deinit_ch(struct lsse_ch *ch); int se_send_ch_requeset(struct lsse_ch *ch);