From 314f51dd06b954c651b7ad70b9d576e4fb35b6b7 Mon Sep 17 00:00:00 2001 From: Huacai Chen Date: Sun, 26 Jan 2025 21:49:59 +0800 Subject: [PATCH 1/4] LoongArch: Add debugfs entries to switch SFB/TSO state commit 04816c1507b46baccd17a4bc948440b3634d5d13 upstream. We need to switch SFB (Store Fill Buffer) and TSO (Total Store Order) state at runtime to debug memory management and KVM virtualization, so add two debugfs entries "sfb_state" and "tso_state" under the directory /sys/kernel/debug/loongarch. Query SFB: cat /sys/kernel/debug/loongarch/sfb_state Enable SFB: echo 1 > /sys/kernel/debug/loongarch/sfb_state Disable SFB: echo 0 > /sys/kernel/debug/loongarch/sfb_state Query TSO: cat /sys/kernel/debug/loongarch/tso_state Switch TSO: echo [TSO] > /sys/kernel/debug/loongarch/tso_state Available [TSO] states: 0 (No Load No Store) 1 (All Load No Store) 3 (Same Load No Store) 4 (No Load All Store) 5 (All Load All Store) 7 (Same Load All Store) Signed-off-by: Huacai Chen Signed-off-by: Ming Wang --- arch/loongarch/include/asm/loongarch.h | 21 +++ arch/loongarch/kernel/Makefile | 2 +- arch/loongarch/kernel/kdebugfs.c | 171 +++++++++++++++++++++++++ arch/loongarch/kernel/unaligned.c | 8 +- 4 files changed, 195 insertions(+), 7 deletions(-) create mode 100644 arch/loongarch/kernel/kdebugfs.c diff --git a/arch/loongarch/include/asm/loongarch.h b/arch/loongarch/include/asm/loongarch.h index ab31e96a3023..1ab96632cc5f 100644 --- a/arch/loongarch/include/asm/loongarch.h +++ b/arch/loongarch/include/asm/loongarch.h @@ -107,6 +107,12 @@ #define CPUCFG3_SPW_HG_HF BIT(11) #define CPUCFG3_RVA BIT(12) #define CPUCFG3_RVAMAX GENMASK(16, 13) +#define CPUCFG3_ALDORDER_CAP BIT(18) /* All address load ordered, capability */ +#define CPUCFG3_ASTORDER_CAP BIT(19) /* All address store ordered, capability */ +#define CPUCFG3_ALDORDER_STA BIT(20) /* All address load ordered, status */ +#define CPUCFG3_ASTORDER_STA BIT(21) /* All address store ordered, status */ +#define CPUCFG3_SLDORDER_CAP BIT(22) /* Same address load ordered, capability */ +#define CPUCFG3_SLDORDER_STA BIT(23) /* Same address load ordered, status */ #define LOONGARCH_CPUCFG4 0x4 #define CPUCFG4_CCFREQ GENMASK(31, 0) @@ -566,6 +572,21 @@ /* Implement dependent */ #define LOONGARCH_CSR_IMPCTL1 0x80 /* Loongson config1 */ +#define CSR_LDSTORDER_SHIFT 28 +#define CSR_LDSTORDER_WIDTH 3 +#define CSR_LDSTORDER_MASK (_ULCAST_(0x7) << CSR_LDSTORDER_SHIFT) +/* 000 = No Load No Store */ +#define CSR_LDSTORDER_NLD_NST (_ULCAST_(0x0) << CSR_LDSTORDER_SHIFT) +/* 001 = All Load No Store */ +#define CSR_LDSTORDER_ALD_NST (_ULCAST_(0x1) << CSR_LDSTORDER_SHIFT) +/* 011 = Same Load No Store */ +#define CSR_LDSTORDER_SLD_NST (_ULCAST_(0x3) << CSR_LDSTORDER_SHIFT) +/* 100 = No Load All Store */ +#define CSR_LDSTORDER_NLD_AST (_ULCAST_(0x4) << CSR_LDSTORDER_SHIFT) +/* 101 = All Load All Store */ +#define CSR_LDSTORDER_ALD_AST (_ULCAST_(0x5) << CSR_LDSTORDER_SHIFT) +/* 111 = Same Load All Store */ +#define CSR_LDSTORDER_SLD_AST (_ULCAST_(0x7) << CSR_LDSTORDER_SHIFT) #define CSR_MISPEC_SHIFT 20 #define CSR_MISPEC_WIDTH 8 #define CSR_MISPEC (_ULCAST_(0xff) << CSR_MISPEC_SHIFT) diff --git a/arch/loongarch/kernel/Makefile b/arch/loongarch/kernel/Makefile index 327af6b33cf6..9cd36ef4cd6d 100644 --- a/arch/loongarch/kernel/Makefile +++ b/arch/loongarch/kernel/Makefile @@ -11,7 +11,7 @@ obj-y += extern.o obj-y += head.o cpu-probe.o cacheinfo.o env.o setup.o entry.o genex.o \ traps.o irq.o idle.o process.o dma.o mem.o io.o reset.o switch.o \ elf.o syscall.o signal.o time.o topology.o inst.o ptrace.o vdso.o \ - alternative.o unwind.o + alternative.o kdebugfs.o unwind.o obj-y += legacy_boot.o obj-$(CONFIG_ACPI) += acpi.o diff --git a/arch/loongarch/kernel/kdebugfs.c b/arch/loongarch/kernel/kdebugfs.c new file mode 100644 index 000000000000..f48586918003 --- /dev/null +++ b/arch/loongarch/kernel/kdebugfs.c @@ -0,0 +1,171 @@ +// SPDX-License-Identifier: GPL-2.0 +#include +#include +#include +#include +#include + +struct dentry *arch_debugfs_dir; +EXPORT_SYMBOL(arch_debugfs_dir); + +static int sfb_state, tso_state; + +static void set_sfb_state(void *info) +{ + int val = *(int *)info << CSR_STFILL_SHIFT; + + csr_xchg32(val, CSR_STFILL, LOONGARCH_CSR_IMPCTL1); +} + +static ssize_t sfb_read(struct file *file, char __user *buf, size_t count, loff_t *ppos) +{ + int s, state; + char str[32]; + + state = (csr_read32(LOONGARCH_CSR_IMPCTL1) & CSR_STFILL) >> CSR_STFILL_SHIFT; + + s = snprintf(str, sizeof(str), "Boot State: %x\nCurrent State: %x\n", sfb_state, state); + + if (*ppos >= s) + return 0; + + s -= *ppos; + s = min_t(u32, s, count); + + if (copy_to_user(buf, &str[*ppos], s)) + return -EFAULT; + + *ppos += s; + + return s; +} + +static ssize_t sfb_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos) +{ + int state; + + if (kstrtoint_from_user(buf, count, 10, &state)) + return -EFAULT; + + switch (state) { + case 0: case 1: + on_each_cpu(set_sfb_state, &state, 1); + break; + default: + return -EINVAL; + } + + return count; +} + +static const struct file_operations sfb_fops = { + .read = sfb_read, + .write = sfb_write, + .open = simple_open, + .llseek = default_llseek +}; + +#define LDSTORDER_NLD_NST 0x0 /* 000 = No Load No Store */ +#define LDSTORDER_ALD_NST 0x1 /* 001 = All Load No Store */ +#define LDSTORDER_SLD_NST 0x3 /* 011 = Same Load No Store */ +#define LDSTORDER_NLD_AST 0x4 /* 100 = No Load All Store */ +#define LDSTORDER_ALD_AST 0x5 /* 101 = All Load All Store */ +#define LDSTORDER_SLD_AST 0x7 /* 111 = Same Load All Store */ + +static char *tso_hints[] = { + "No Load No Store", + "All Load No Store", + "Invalid Config", + "Same Load No Store", + "No Load All Store", + "All Load All Store", + "Invalid Config", + "Same Load All Store" +}; + +static void set_tso_state(void *info) +{ + int val = *(int *)info << CSR_LDSTORDER_SHIFT; + + csr_xchg32(val, CSR_LDSTORDER_MASK, LOONGARCH_CSR_IMPCTL1); +} + +static ssize_t tso_read(struct file *file, char __user *buf, size_t count, loff_t *ppos) +{ + int s, state; + char str[240]; + + state = (csr_read32(LOONGARCH_CSR_IMPCTL1) & CSR_LDSTORDER_MASK) >> CSR_LDSTORDER_SHIFT; + + s = snprintf(str, sizeof(str), "Boot State: %d (%s)\n" + "Current State: %d (%s)\n\n" + "Available States:\n" + "0 (%s)\t1 (%s)\t3 (%s)\n" + "4 (%s)\t5 (%s)\t7 (%s)\n", + tso_state, tso_hints[tso_state], state, + tso_hints[state], tso_hints[0], tso_hints[1], + tso_hints[3], tso_hints[4], tso_hints[5], + tso_hints[7]); + + if (*ppos >= s) + return 0; + + s -= *ppos; + s = min_t(u32, s, count); + + if (copy_to_user(buf, &str[*ppos], s)) + return -EFAULT; + + *ppos += s; + + return s; +} + +static ssize_t tso_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos) +{ + int state; + + if (kstrtoint_from_user(buf, count, 10, &state)) + return -EFAULT; + + switch (state) { + case 0: case 1: case 3: + case 4: case 5: case 7: + on_each_cpu(set_tso_state, &state, 1); + break; + default: + return -EINVAL; + } + + return count; +} + +static const struct file_operations tso_fops = { + .read = tso_read, + .write = tso_write, + .open = simple_open, + .llseek = default_llseek +}; + +static int __init arch_kdebugfs_init(void) +{ + unsigned int config = read_cpucfg(LOONGARCH_CPUCFG3); + + arch_debugfs_dir = debugfs_create_dir("loongarch", NULL); + + if (config & CPUCFG3_SFB) { + debugfs_create_file("sfb_state", 0644, + arch_debugfs_dir, &sfb_state, &sfb_fops); + sfb_state = (csr_read32(LOONGARCH_CSR_IMPCTL1) & CSR_STFILL) >> CSR_STFILL_SHIFT; + } + + if (config & (CPUCFG3_ALDORDER_CAP | CPUCFG3_ASTORDER_CAP)) { + debugfs_create_file("tso_state", 0644, + arch_debugfs_dir, &tso_state, &tso_fops); + tso_state = (csr_read32(LOONGARCH_CSR_IMPCTL1) & + CSR_LDSTORDER_MASK) >> CSR_LDSTORDER_SHIFT; + } + + return 0; +} +postcore_initcall(arch_kdebugfs_init); diff --git a/arch/loongarch/kernel/unaligned.c b/arch/loongarch/kernel/unaligned.c index 3abf163dda05..5a4057627d2b 100644 --- a/arch/loongarch/kernel/unaligned.c +++ b/arch/loongarch/kernel/unaligned.c @@ -482,14 +482,10 @@ void emulate_load_store_insn(struct pt_regs *regs, void __user *addr, unsigned i #ifdef CONFIG_DEBUG_FS static int __init debugfs_unaligned(void) { - struct dentry *d; - - d = debugfs_create_dir("loongarch", NULL); - debugfs_create_u32("unaligned_instructions_user", - S_IRUGO, d, &unaligned_instructions_user); + 0444, arch_debugfs_dir, &unaligned_instructions_user); debugfs_create_u32("unaligned_instructions_kernel", - S_IRUGO, d, &unaligned_instructions_kernel); + 0444, arch_debugfs_dir, &unaligned_instructions_kernel); return 0; } -- Gitee From a3bb6db660b9cf3351235021125af35ae138b3d5 Mon Sep 17 00:00:00 2001 From: Ming Wang Date: Tue, 1 Jul 2025 10:38:06 +0800 Subject: [PATCH 2/4] LoongArch: Support mem= kernel parameter Upstream: no Conflict: none Checkpatch: pass The LoongArch mem= parameter parser was previously limited to the mem=@ format. This was inconvenient for the common use case of simply capping the total system memory, as it forced users to manually specify a start address. It was also inconsistent with the behavior on other architectures. This patch enhances the parser in early_parse_mem() to also support the more user-friendly mem= format. The implementation now checks for the presence of the '@' symbol to determine the user's intent: - If mem= is provided (no '@'), the kernel now calls memblock_enforce_memory_limit(). This trims memory from the top down to the specified size. - If mem=@ is provided, the original behavior is retained for backward compatibility. This allows for defining specific memory banks. This change introduces an important usage rule reflected in the code's comments: the mem= format should only be specified once on the kernel command line. It acts as a single, global cap on total memory. In contrast, the mem=@ format can be specified multiple times to define several distinct memory regions. Signed-off-by: Ming Wang Signed-off-by: Huacai Chen --- arch/loongarch/kernel/setup.c | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/arch/loongarch/kernel/setup.c b/arch/loongarch/kernel/setup.c index 6baa5f5233e0..2ec51a228394 100644 --- a/arch/loongarch/kernel/setup.c +++ b/arch/loongarch/kernel/setup.c @@ -212,6 +212,16 @@ static int __init early_parse_mem(char *p) return -EINVAL; } + start = 0; + size = memparse(p, &p); + if (*p == '@') /* Every mem=... should contain '@' */ + start = memparse(p + 1, &p); + else { /* Only one mem=... is allowed if no '@' */ + usermem = 1; + memblock_enforce_memory_limit(size); + return 0; + } + /* * If a user specifies memory size, we * blow away any automatically generated @@ -222,14 +232,6 @@ static int __init early_parse_mem(char *p) memblock_remove(memblock_start_of_DRAM(), memblock_end_of_DRAM() - memblock_start_of_DRAM()); } - start = 0; - size = memparse(p, &p); - if (*p == '@') - start = memparse(p + 1, &p); - else { - pr_err("Invalid format!\n"); - return -EINVAL; - } if (!IS_ENABLED(CONFIG_NUMA)) memblock_add(start, size); -- Gitee From de959023bb5598137dbbcac332a67ed61f391eb9 Mon Sep 17 00:00:00 2001 From: Qunqin Zhao Date: Fri, 4 Jul 2025 10:49:11 +0800 Subject: [PATCH 3/4] crypto: loongson: add loongson sm3 and ecb(sm4) support Upstream: no Conflict: none Checkpatch: pass The Loongson Security Engine (SE) provides hardware acceleration for various cryptographic operations. This commit introduces support for SM3 and ECB(SM4) algorithms via the standard kernel Crypto API. The new crypto drivers are enabled in the `loongson3_defconfig` to make them available by default. Existing drivers that depend on the SE, such as the TPM driver, have been updated to use the new MFD interface. Signed-off-by: Qunqin Zhao Signed-off-by: Ming Wang --- arch/loongarch/configs/loongson3_defconfig | 4 + drivers/char/Kconfig | 12 +- drivers/char/Makefile | 1 - drivers/char/loongson_se.c | 536 -------------------- drivers/char/lsse_sdf_cdev.c | 95 ++-- drivers/char/tpm/Kconfig | 2 +- drivers/char/tpm/tpm_loongson.c | 79 ++- drivers/crypto/Kconfig | 1 + drivers/crypto/Makefile | 1 + drivers/crypto/loongson/Kconfig | 31 ++ drivers/crypto/loongson/Makefile | 3 + drivers/crypto/loongson/loongson-hash.c | 312 ++++++++++++ drivers/crypto/loongson/loongson-rng.c | 209 ++++++++ drivers/crypto/loongson/loongson-skcipher.c | 248 +++++++++ drivers/mfd/Kconfig | 11 + drivers/mfd/Makefile | 2 + drivers/mfd/loongson-se.c | 256 ++++++++++ include/linux/mfd/loongson-se.h | 59 +++ 18 files changed, 1198 insertions(+), 664 deletions(-) delete mode 100644 drivers/char/loongson_se.c create mode 100644 drivers/crypto/loongson/Kconfig create mode 100644 drivers/crypto/loongson/Makefile create mode 100644 drivers/crypto/loongson/loongson-hash.c create mode 100644 drivers/crypto/loongson/loongson-rng.c create mode 100644 drivers/crypto/loongson/loongson-skcipher.c create mode 100644 drivers/mfd/loongson-se.c create mode 100644 include/linux/mfd/loongson-se.h diff --git a/arch/loongarch/configs/loongson3_defconfig b/arch/loongarch/configs/loongson3_defconfig index 88362d8f1b85..42b8f7a1fae7 100644 --- a/arch/loongarch/configs/loongson3_defconfig +++ b/arch/loongarch/configs/loongson3_defconfig @@ -1312,6 +1312,7 @@ CONFIG_MFD_VIPERBOARD=m CONFIG_MFD_SM501=m CONFIG_MFD_SM501_GPIO=y CONFIG_MFD_VX855=m +CONFIG_MFD_LOONGSON_SE=m CONFIG_RC_CORE=m CONFIG_LIRC=y CONFIG_RC_DECODERS=y @@ -2193,6 +2194,9 @@ CONFIG_CRYPTO_CRC32_LOONGARCH=m CONFIG_CRYPTO_DEV_NITROX_CNN55XX=m CONFIG_CRYPTO_DEV_CHELSIO=m CONFIG_CRYPTO_DEV_VIRTIO=m +CONFIG_CRYPTO_DEV_LOONGSON_HASH=m +CONFIG_CRYPTO_DEV_LOONGSON_RNG=m +CONFIG_CRYPTO_DEV_LOONGSON_SKCIPHER=m CONFIG_SW_SE_CHIP=m CONFIG_SIGNED_PE_FILE_VERIFICATION=y CONFIG_SECONDARY_TRUSTED_KEYRING=y diff --git a/drivers/char/Kconfig b/drivers/char/Kconfig index cfc46bb0365e..66342648117c 100644 --- a/drivers/char/Kconfig +++ b/drivers/char/Kconfig @@ -391,19 +391,9 @@ config UV_MMTIMER The uv_mmtimer device allows direct userspace access to the UV system timer. -config LOONGSON_SE - tristate "LOONGSON SECURITY MODULE Interface" - depends on LOONGARCH - select MFD_CORE - default m - help - If you have LOONGSON security module (SE) support say Yes and it - will be accessible from within Linux. To compile this driver - as a module, choose M here; the module will be called loongson-se. - config LOONGSON_SE_SDF tristate "LOONGSON SECURITY MODULE SDF Interface" - depends on LOONGARCH && LOONGSON_SE + depends on LOONGARCH && MFD_LOONGSON_SE default m help If you want to use LOONGSON security module (SE) as SDF say Yes diff --git a/drivers/char/Makefile b/drivers/char/Makefile index 109af71c5416..1242ca2ddd81 100644 --- a/drivers/char/Makefile +++ b/drivers/char/Makefile @@ -32,7 +32,6 @@ obj-$(CONFIG_SCx200_GPIO) += scx200_gpio.o obj-$(CONFIG_PC8736x_GPIO) += pc8736x_gpio.o obj-$(CONFIG_NSC_GPIO) += nsc_gpio.o obj-$(CONFIG_TELCLOCK) += tlclk.o -obj-$(CONFIG_LOONGSON_SE) += loongson_se.o obj-$(CONFIG_LOONGSON_SE_SDF) += lsse_sdf_cdev.o obj-$(CONFIG_MWAVE) += mwave/ diff --git a/drivers/char/loongson_se.c b/drivers/char/loongson_se.c deleted file mode 100644 index c9bd51adc792..000000000000 --- a/drivers/char/loongson_se.c +++ /dev/null @@ -1,536 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0+ -/* - * Copyright (C) 2024 Loongson Technology Corporation Limited - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -/* - * 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->base + off); -} - -static inline void se_writel(struct loongson_se *se, u32 val, u32 off) -{ - writel(val, se->base + off); -} - -static inline bool se_ch_status(struct loongson_se *se, u32 int_bit) -{ - return !!(se->ch_status & int_bit); -} - -static void se_enable_int(struct loongson_se *se, u32 int_bit) -{ - unsigned long flag; - u32 tmp; - - spin_lock_irqsave(&se->dev_lock, flag); - - tmp = se_readl(se, SE_S2LINT_EN); - tmp |= int_bit; - se_writel(se, tmp, SE_S2LINT_EN); - - spin_unlock_irqrestore(&se->dev_lock, flag); -} - -static void se_disable_int(struct loongson_se *se, u32 int_bit) -{ - unsigned long flag; - u32 tmp; - - spin_lock_irqsave(&se->dev_lock, flag); - - tmp = se_readl(se, SE_S2LINT_EN); - tmp &= ~(int_bit); - 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_data *req) -{ - unsigned long flag; - u32 status; - int err; - int i; - - if (!se || !req) - return -EINVAL; - - 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.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); - - spin_unlock_irqrestore(&se->cmd_lock, flag); - - return err; -} - -static int se_get_response(struct loongson_se *se, struct se_data *res) -{ - unsigned long flag; - int i; - - if (!se || !res) - return -EINVAL; - - 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.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); - - return 0; -} - -static int loongson_se_get_res(struct loongson_se *se, u32 int_bit, u32 cmd, - struct se_data *res) -{ - res->int_bit = int_bit; - - if (se_get_response(se, res)) { - dev_err(se->dev, "Int 0x%x get response fail.\n", int_bit); - return -EFAULT; - } - - /* Check response */ - 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); - return -EFAULT; - } - - return 0; -} - -static int se_send_genl_cmd(struct loongson_se *se, struct se_data *req, - struct se_data *res, int retry) -{ - int err, cnt = 0; - -try_again: - if (cnt++ >= retry) { - err = -ETIMEDOUT; - goto out; - } - - dev_dbg(se->dev, "%d time send cmd 0x%x\n", cnt, req->u.gcmd.cmd); - - err = se_send_requeset(se, req); - if (err) - goto try_again; - - 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); - goto try_again; - } - -out: - se_enable_int(se, req->int_bit); - - return err; -} - -static int loongson_se_set_msg(struct lsse_ch *ch) -{ - struct loongson_se *se = ch->se; - struct se_data req = {0}; - struct se_data res = {0}; - int err; - - req.int_bit = SE_INT_SETUP; - req.u.gcmd.cmd = SE_CMD_SETMSG; - /* MSG off */ - req.u.gcmd.info[0] = ch->id; - 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]); - - err = se_send_genl_cmd(se, &req, &res, 5); - if (res.u.res.cmd_ret) - return res.u.res.cmd_ret; - - return err; -} - -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, SE_S2LINT_STAT); - - dev_dbg(se->dev, "%s int status is 0x%x\n", __func__, int_status); - - se_disable_int(se, int_status); - - if (int_status & SE_INT_SETUP) { - complete(&se->cmd_completion); - int_status &= ~SE_INT_SETUP; - } - - while (int_status) { - int id = __ffs(int_status); - - ch = &se->chs[id]; - if (ch->complete) - ch->complete(ch); - int_status &= ~BIT(id); - se_writel(se, BIT(id), SE_S2LINT_CL); - } - - return IRQ_HANDLED; -} - -static int se_init_hw(struct loongson_se *se, dma_addr_t addr, int size) -{ - struct se_data req; - struct se_data res; - int err, retry = 5; - - se_enable_int(se, SE_INT_SETUP); - - /* Start engine */ - 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); - if (err) - return err; - - /* Get Version */ - 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]; - - /* 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] = addr & 0xffffffff; - req.u.gcmd.info[1] = addr >> 32; - /* MASK */ - req.u.gcmd.info[2] = ~(size - 1); - req.u.gcmd.info[3] = 0xffffffff; - err = se_send_genl_cmd(se, &req, &res, retry); - if (err) - return err; - 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 se_disable_hw(struct loongson_se *se) -{ - 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, 5); - se_disable_int(se, SE_INT_ALL); -} - -/* - * 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; - - se = ch->se; - int_bit = ch->int_bit; - mutex_lock(&se->ch_init_lock); - if ((se_readl(se, SE_L2SINT_STAT) & int_bit) || - !(se_readl(se, SE_L2SINT_EN) & int_bit)) { - mutex_unlock(&se->ch_init_lock); - return -EBUSY; - } - - se_enable_int(se, int_bit); - 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); - mutex_unlock(&se->ch_init_lock); - - return err; -} -EXPORT_SYMBOL_GPL(se_send_ch_requeset); - -/* - * 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 = dev_get_drvdata(dev); - struct lsse_ch *ch; - int data_first, data_nr; - int msg_first, msg_nr; - - mutex_lock(&se->ch_init_lock); - if (!se) { - pr_err("SE has bot been initialized\n"); - return NULL; - } - - if (id > SE_CH_MAX) { - dev_err(se->dev, "Channel number %d is invalid\n", id); - return NULL; - } - - if (se_ch_status(se, BIT(id))) { - dev_err(se->dev, "Channel number %d has been initialized\n", id); - return NULL; - } - - 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, 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"); - mutex_unlock(&se->ch_init_lock); - return NULL; - } - - bitmap_set(se->mem_map, data_first, data_nr); - 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, 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); - mutex_unlock(&se->ch_init_lock); - return NULL; - } - - bitmap_set(se->mem_map, msg_first, msg_nr); - 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); - - - if (loongson_se_set_msg(ch)) { - dev_err(se->dev, "Channel %d setup message address failed\n", id); - mutex_unlock(&se->ch_init_lock); - return NULL; - } - - se_enable_int(se, ch->int_bit); - mutex_unlock(&se->ch_init_lock); - - return ch; -} -EXPORT_SYMBOL_GPL(se_init_ch); - -void se_deinit_ch(struct lsse_ch *ch) -{ - struct loongson_se *se = ch->se; - unsigned long flag; - int first, nr; - int id = ch->id; - - if (!se) { - pr_err("SE has bot been initialized\n"); - return; - } - - if (id > SE_CH_MAX) { - dev_err(se->dev, "Channel number %d is invalid\n", id); - return; - } - - if (!se_ch_status(se, BIT(id))) { - dev_err(se->dev, "Channel number %d has not been initialized\n", id); - return; - } - - spin_lock_irqsave(&se->dev_lock, flag); - se->ch_status &= ~BIT(ch->id); - - 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) / 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); - -} -EXPORT_SYMBOL_GPL(se_deinit_ch); - -static const struct mfd_cell engines[] = { - { .name = "loongson-tpm" }, -}; - -static int loongson_se_probe(struct platform_device *pdev) -{ - struct loongson_se *se; - struct device *dev = &pdev->dev; - int nr_irq, irq, err, size; - - 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); - mutex_init(&se->ch_init_lock); - 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_platform_ioremap_resource(pdev, 0); - if (IS_ERR(se->base)) - return PTR_ERR(se->base); - - 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) - dev_err(dev, "failed to request irq: %d\n", err); - } - - err = se_init_hw(se, se->mem_addr, size); - if (err) { - se_disable_hw(se); - return err; - } - - return devm_mfd_add_devices(dev, 0, engines, ARRAY_SIZE(engines), NULL, 0, NULL); -} - -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, - .driver = { - .name = "loongson-se", - .acpi_match_table = loongson_se_acpi_match, - }, -}; -module_platform_driver(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 3bbe3cbb35ae..7ffccede03a4 100644 --- a/drivers/char/lsse_sdf_cdev.c +++ b/drivers/char/lsse_sdf_cdev.c @@ -16,20 +16,17 @@ #include #include #include -#include +#include +#define SE_CMD_SDF 0x700 #define SE_SDF_BUFSIZE (PAGE_SIZE * 2) #define SDF_OPENSESSION (0x204) #define SDF_CLOSESESSION (0x205) struct sdf_dev { struct miscdevice miscdev; - struct lsse_ch *se_ch; + struct loongson_se_engine *se_ch; struct mutex data_lock; - bool processing_cmd; - - /* Synchronous CMD */ - wait_queue_head_t wq; }; struct se_sdf_msg { @@ -65,63 +62,36 @@ struct sdf_file_pvt_data { struct sdf_handle *ph; }; -static void sdf_complete(struct lsse_ch *ch) -{ - struct sdf_dev *se = (struct sdf_dev *)ch->priv; - - se->processing_cmd = false; - wake_up(&se->wq); -} - -static int se_send_sdf_cmd(struct sdf_dev *se, int len, int retry) +static int se_send_sdf_cmd(struct sdf_dev *se, int len) { - struct se_sdf_msg *smsg = (struct se_sdf_msg *)se->se_ch->smsg; - int err; - - spin_lock_irq(&se->se_ch->ch_lock); + struct se_sdf_msg *command = (struct se_sdf_msg *)se->se_ch->command; - smsg->cmd = SE_CMD_SDF; + command->cmd = SE_CMD_SDF; /* One time one cmd */ - smsg->data_off = se->se_ch->data_buffer - se->se_ch->se->mem_base; - smsg->data_len = len; + command->data_off = se->se_ch->buffer_off; + command->data_len = len; -try_again: - if (!retry--) - goto out; - - err = se_send_ch_requeset(se->se_ch); - if (err) { - udelay(5); - goto try_again; - } - -out: - spin_unlock_irq(&se->se_ch->ch_lock); - - return err; + return loongson_se_send_engine_cmd(se->se_ch); } static int sdf_recvu(struct sdf_file_pvt_data *pvt, char __user *buf, int *se_ret) { struct sdf_dev *se = pvt->se; struct sdf_kernel_command *skc; - struct se_sdf_msg *rmsg; + struct se_sdf_msg *command_ret; struct sdf_handle *ph; int ret; - if (!wait_event_timeout(se->wq, !se->processing_cmd, HZ*10)) - return -ETIME; - - rmsg = (struct se_sdf_msg *)se->se_ch->rmsg; - if (rmsg->cmd != SE_CMD_SDF) { + command_ret = (struct se_sdf_msg *)se->se_ch->command_ret; + if (command_ret->cmd != SE_CMD_SDF) { pr_err("se get wrong response\n"); return -EIO; } ret = copy_to_user((char __user *)buf, - se->se_ch->data_buffer + rmsg->data_off, rmsg->data_len); + se->se_ch->data_buffer + command_ret->data_off, command_ret->data_len); - skc = (struct sdf_kernel_command *)(se->se_ch->data_buffer + rmsg->data_off); + skc = (struct sdf_kernel_command *)(se->se_ch->data_buffer + command_ret->data_off); *se_ret = skc->header.u.ret; if (skc->header.command == SDF_OPENSESSION && !*se_ret) { ph = kmalloc(sizeof(*ph), GFP_KERNEL); @@ -165,10 +135,11 @@ static int sdf_sendu(struct sdf_file_pvt_data *pvt, 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); + ret = se_send_sdf_cmd(se, count); if (ret) { - pr_err("se_send_sdf_cmd failed\n"); + pr_debug("se_send_sdf_cmd failed u %d\n", ret); + udelay(LOONGSON_ENGINE_CMD_TIMEOUT_US); + reinit_completion(&se->se_ch->completion); goto out_unlock; } @@ -206,19 +177,14 @@ static ssize_t sdf_write(struct file *filp, const char __user *buf, 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; + struct se_sdf_msg *command_ret; - rmsg = (struct se_sdf_msg *)se->se_ch->rmsg; - if (rmsg->cmd != SE_CMD_SDF) { + command_ret = (struct se_sdf_msg *)se->se_ch->command_ret; + if (command_ret->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); + memcpy(buf, se->se_ch->data_buffer + command_ret->data_off, command_ret->data_len); return 0; } @@ -231,10 +197,11 @@ static int sdf_sendk(struct sdf_file_pvt_data *pvt, char *buf, size_t count) 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); + ret = se_send_sdf_cmd(se, count); if (ret) { - pr_err("se_send_sdf_cmd failed\n"); + pr_debug("se_send_sdf_cmd failed k %d\n", ret); + udelay(LOONGSON_ENGINE_CMD_TIMEOUT_US); + reinit_completion(&se->se_ch->completion); goto out_unlock; } @@ -259,8 +226,8 @@ static int close_one_handle(struct sdf_file_pvt_data *pvt, struct sdf_handle *ph skc->header.param_len[0] = sizeof(skc->handle); /* close one session */ 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", + if (skc->header.u.ret || ret) { + pr_debug("Auto Close Session failed, session handle: %llx, ret: %d\n", (u64)ph->handle, skc->header.u.ret); return skc->header.u.ret; } @@ -327,15 +294,12 @@ static int sdf_probe(struct platform_device *pdev) 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); - sdf->se_ch = se_init_ch(pdev->dev.parent, ch, SE_SDF_BUFSIZE, - msg_size, sdf, sdf_complete); + sdf->se_ch = loongson_se_init_engine(pdev->dev.parent, ch); sdf->miscdev.minor = MISC_DYNAMIC_MINOR; sdf->miscdev.name = "lsse_sdf"; @@ -352,7 +316,6 @@ static int sdf_remove(struct platform_device *pdev) struct sdf_dev *sdf = platform_get_drvdata(pdev); misc_deregister(&sdf->miscdev); - se_deinit_ch(sdf->se_ch); return 0; } diff --git a/drivers/char/tpm/Kconfig b/drivers/char/tpm/Kconfig index 3524b2e6086e..a8046d1a17a1 100644 --- a/drivers/char/tpm/Kconfig +++ b/drivers/char/tpm/Kconfig @@ -176,7 +176,7 @@ config TCG_IBMVTPM config TCG_LOONGSON tristate "Loongson TPM Interface" - depends on LOONGSON_SE + depends on MFD_LOONGSON_SE help If you want to make Loongson TPM support available, say Yes and it will be accessible from within Linux. To compile this diff --git a/drivers/char/tpm/tpm_loongson.c b/drivers/char/tpm/tpm_loongson.c index 3c27d86b77ba..a4ec23639911 100644 --- a/drivers/char/tpm/tpm_loongson.c +++ b/drivers/char/tpm/tpm_loongson.c @@ -2,56 +2,44 @@ /* Copyright (c) 2025 Loongson Technology Corporation Limited. */ #include -#include +#include #include #include #include "tpm.h" -struct tpm_loongson_msg { - u32 cmd; +struct tpm_loongson_cmd { + u32 cmd_id; u32 data_off; u32 data_len; - u32 info[5]; + u32 pad[5]; }; -struct tpm_loongson_dev { - struct lsse_ch *se_ch; - struct completion tpm_loongson_completion; -}; - -static void tpm_loongson_complete(struct lsse_ch *ch) -{ - struct tpm_loongson_dev *td = ch->priv; - - complete(&td->tpm_loongson_completion); -} - static int tpm_loongson_recv(struct tpm_chip *chip, u8 *buf, size_t count) { - struct tpm_loongson_dev *td = dev_get_drvdata(&chip->dev); - struct tpm_loongson_msg *rmsg; - int sig; + struct loongson_se_engine *tpm_engine = dev_get_drvdata(&chip->dev); + struct tpm_loongson_cmd *cmd_ret = tpm_engine->command_ret; - sig = wait_for_completion_interruptible(&td->tpm_loongson_completion); - if (sig) - return sig; + if (cmd_ret->data_len > count) + return -EIO; - rmsg = td->se_ch->rmsg; - memcpy(buf, td->se_ch->data_buffer, rmsg->data_len); + memcpy(buf, tpm_engine->data_buffer, cmd_ret->data_len); - return rmsg->data_len; + return cmd_ret->data_len; } static int tpm_loongson_send(struct tpm_chip *chip, u8 *buf, size_t count) { - struct tpm_loongson_dev *td = dev_get_drvdata(&chip->dev); - struct tpm_loongson_msg *smsg = td->se_ch->smsg; + struct loongson_se_engine *tpm_engine = dev_get_drvdata(&chip->dev); + struct tpm_loongson_cmd *cmd = tpm_engine->command; - memcpy(td->se_ch->data_buffer, buf, count); - smsg->data_len = count; + if (count > tpm_engine->buffer_size) + return -E2BIG; - return se_send_ch_requeset(td->se_ch); + cmd->data_len = count; + memcpy(tpm_engine->data_buffer, buf, count); + + return loongson_se_send_engine_cmd(tpm_engine); } static const struct tpm_class_ops tpm_loongson_ops = { @@ -62,42 +50,35 @@ static const struct tpm_class_ops tpm_loongson_ops = { static int tpm_loongson_probe(struct platform_device *pdev) { + struct loongson_se_engine *tpm_engine; struct device *dev = &pdev->dev; - struct tpm_loongson_msg *smsg; - struct tpm_loongson_dev *td; + struct tpm_loongson_cmd *cmd; struct tpm_chip *chip; - td = devm_kzalloc(dev, sizeof(struct tpm_loongson_dev), GFP_KERNEL); - if (!td) - return -ENOMEM; - - init_completion(&td->tpm_loongson_completion); - td->se_ch = se_init_ch(dev->parent, SE_CH_TPM, PAGE_SIZE, - 2 * sizeof(struct tpm_loongson_msg), td, - tpm_loongson_complete); - if (!td->se_ch) + tpm_engine = loongson_se_init_engine(dev->parent, SE_ENGINE_TPM); + if (!tpm_engine) return -ENODEV; - smsg = td->se_ch->smsg; - smsg->cmd = SE_CMD_TPM; - smsg->data_off = td->se_ch->data_buffer - td->se_ch->se->mem_base; + cmd = tpm_engine->command; + cmd->cmd_id = SE_CMD_TPM; + cmd->data_off = tpm_engine->buffer_off; chip = tpmm_chip_alloc(dev, &tpm_loongson_ops); if (IS_ERR(chip)) return PTR_ERR(chip); chip->flags = TPM_CHIP_FLAG_TPM2 | TPM_CHIP_FLAG_IRQ; - dev_set_drvdata(&chip->dev, td); + dev_set_drvdata(&chip->dev, tpm_engine); return tpm_chip_register(chip); } -static struct platform_driver tpm_loongson_driver = { +static struct platform_driver tpm_loongson = { .probe = tpm_loongson_probe, .driver = { - .name = "loongson-tpm", + .name = "tpm_loongson", }, }; -module_platform_driver(tpm_loongson_driver); +module_platform_driver(tpm_loongson); -MODULE_ALIAS("platform:loongson-tpm"); +MODULE_ALIAS("platform:tpm_loongson"); MODULE_LICENSE("GPL"); MODULE_DESCRIPTION("Loongson TPM driver"); diff --git a/drivers/crypto/Kconfig b/drivers/crypto/Kconfig index c6766f08c5af..3e9a0cba01a5 100644 --- a/drivers/crypto/Kconfig +++ b/drivers/crypto/Kconfig @@ -816,6 +816,7 @@ config CRYPTO_DEV_CCREE If unsure say Y. source "drivers/crypto/hisilicon/Kconfig" +source "drivers/crypto/loongson/Kconfig" source "drivers/crypto/amlogic/Kconfig" diff --git a/drivers/crypto/Makefile b/drivers/crypto/Makefile index 6ad337bad109..0ce1daed7dc9 100644 --- a/drivers/crypto/Makefile +++ b/drivers/crypto/Makefile @@ -50,6 +50,7 @@ obj-$(CONFIG_CRYPTO_DEV_SAFEXCEL) += inside-secure/ obj-$(CONFIG_CRYPTO_DEV_ARTPEC6) += axis/ obj-y += xilinx/ obj-y += hisilicon/ +obj-y += loongson/ obj-$(CONFIG_CRYPTO_DEV_AMLOGIC_GXL) += amlogic/ obj-y += intel/ obj-y += starfive/ diff --git a/drivers/crypto/loongson/Kconfig b/drivers/crypto/loongson/Kconfig new file mode 100644 index 000000000000..115ed745f065 --- /dev/null +++ b/drivers/crypto/loongson/Kconfig @@ -0,0 +1,31 @@ +config CRYPTO_DEV_LOONGSON_HASH + tristate "Support for Loongson hash Driver" + depends on MFD_LOONGSON_SE + select CRYPTO_SM3_GENERIC + help + Support for Loongson hash Driver. + If you want to make Loongson hash support available, say Yes and + it will be accessible from within Linux. To compile this + driver as a module, choose M here; the module will be called + loongson_hash. + +config CRYPTO_DEV_LOONGSON_RNG + tristate "Support for Loongson RNG Driver" + depends on MFD_LOONGSON_SE + help + Support for Loongson RNG Driver. + If you want to make Loongson rng support available, say Yes and + it will be accessible from within Linux. To compile this + driver as a module, choose M here; the module will be called + loongson_rng. + +config CRYPTO_DEV_LOONGSON_SKCIPHER + tristate "Support for Loongson skcipher Driver" + depends on MFD_LOONGSON_SE + select CRYPTO_SM4_GENERIC + help + Support for Loongson skcipher Driver. + If you want to make Loongson hash support available, say Yes and + it will be accessible from within Linux. To compile this + driver as a module, choose M here; the module will be called + loongson_skcipher. diff --git a/drivers/crypto/loongson/Makefile b/drivers/crypto/loongson/Makefile new file mode 100644 index 000000000000..e583c500c250 --- /dev/null +++ b/drivers/crypto/loongson/Makefile @@ -0,0 +1,3 @@ +obj-$(CONFIG_CRYPTO_DEV_LOONGSON_HASH) += loongson-hash.o +obj-$(CONFIG_CRYPTO_DEV_LOONGSON_RNG) += loongson-rng.o +obj-$(CONFIG_CRYPTO_DEV_LOONGSON_SKCIPHER) += loongson-skcipher.o diff --git a/drivers/crypto/loongson/loongson-hash.c b/drivers/crypto/loongson/loongson-hash.c new file mode 100644 index 000000000000..ece990a53cec --- /dev/null +++ b/drivers/crypto/loongson/loongson-hash.c @@ -0,0 +1,312 @@ +// SPDX-License-Identifier: GPL-2.0 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define LOONGSON_SM3_CTX_SIZE 64 + +#define LOONGSON_HASH_UPDATE 1 +#define LOONGSON_HASH_FINAL 2 + +struct loongson_hash_dev_list { + struct mutex lock; + struct list_head list; + int registered; +}; + +struct loongson_hash_dev { + struct loongson_se_engine *loongson_engine; + struct crypto_engine *crypto_engine; + struct list_head list; + u32 used; +}; + +struct loongson_hash_ctx { + struct loongson_hash_dev *hdev; + u8 sm3_ctx[LOONGSON_SM3_CTX_SIZE]; +}; + +struct loongson_hash_reqctx { + int op; +}; + +struct loongson_hash_cmd { + u32 cmd_id; + union { + u32 len; + u32 ret; + } u; + u32 block_off; + u32 digest_off; + u32 pad[4]; +}; + +static struct loongson_hash_dev_list hash_devices = { + .lock = __MUTEX_INITIALIZER(hash_devices.lock), + .list = LIST_HEAD_INIT(hash_devices.list), +}; + +static int loongson_sm3_init(struct ahash_request *req) +{ + struct loongson_hash_ctx *ctx = crypto_ahash_ctx(crypto_ahash_reqtfm(req)); + + memset(ctx->sm3_ctx, 0, LOONGSON_SM3_CTX_SIZE); + + return 0; +} + +static int loongson_sm3_update(struct ahash_request *req) +{ + struct loongson_hash_ctx *ctx = crypto_ahash_ctx(crypto_ahash_reqtfm(req)); + struct loongson_hash_reqctx *rctx = ahash_request_ctx(req); + + rctx->op = LOONGSON_HASH_UPDATE; + + return crypto_transfer_hash_request_to_engine(ctx->hdev->crypto_engine, req); +} + +static int loongson_sm3_final(struct ahash_request *req) +{ + struct loongson_hash_ctx *ctx = crypto_ahash_ctx(crypto_ahash_reqtfm(req)); + struct loongson_hash_reqctx *rctx = ahash_request_ctx(req); + + rctx->op = LOONGSON_HASH_FINAL; + + return crypto_transfer_hash_request_to_engine(ctx->hdev->crypto_engine, req); +} + +static int loongson_sm3_finup(struct ahash_request *req) +{ + struct loongson_hash_ctx *ctx = crypto_ahash_ctx(crypto_ahash_reqtfm(req)); + struct loongson_hash_reqctx *rctx = ahash_request_ctx(req); + + rctx->op = LOONGSON_HASH_UPDATE | LOONGSON_HASH_FINAL; + + return crypto_transfer_hash_request_to_engine(ctx->hdev->crypto_engine, req); +} + +static int loongson_sm3_do_update(struct ahash_request *req, + struct loongson_hash_ctx *ctx, + struct loongson_hash_reqctx *rctx) +{ + struct loongson_hash_cmd *cmd; + void *dma_buff = ctx->hdev->loongson_engine->data_buffer + LOONGSON_SM3_CTX_SIZE; + u32 dma_buff_size = ctx->hdev->loongson_engine->buffer_size - LOONGSON_SM3_CTX_SIZE; + int err = 0, skip = 0, copyed; + + /* Import */ + memcpy(ctx->hdev->loongson_engine->data_buffer, ctx->sm3_ctx, LOONGSON_SM3_CTX_SIZE); + + while (skip < req->nbytes) { + copyed = sg_pcopy_to_buffer(req->src, sg_nents(req->src), + dma_buff, min(dma_buff_size, req->nbytes), skip); + + cmd = ctx->hdev->loongson_engine->command; + cmd->cmd_id = SE_CMD_HASH | LOONGSON_HASH_UPDATE; + cmd->u.len = copyed; + err = loongson_se_send_engine_cmd(ctx->hdev->loongson_engine); + if (err) + break; + + cmd = ctx->hdev->loongson_engine->command_ret; + if (cmd->u.ret) { + err = -EIO; + break; + } + + skip += copyed; + } + + /* Export */ + memcpy(ctx->sm3_ctx, ctx->hdev->loongson_engine->data_buffer, LOONGSON_SM3_CTX_SIZE); + + return err; +} + +static int loongson_sm3_do_final(struct ahash_request *req, + struct loongson_hash_ctx *ctx, + struct loongson_hash_reqctx *rctx) +{ + struct loongson_hash_cmd *cmd = ctx->hdev->loongson_engine->command; + int err; + + cmd->cmd_id = SE_CMD_HASH | LOONGSON_HASH_FINAL; + cmd->u.len = SM3_DIGEST_SIZE; + err = loongson_se_send_engine_cmd(ctx->hdev->loongson_engine); + if (err) + goto out; + + cmd = ctx->hdev->loongson_engine->command_ret; + if (cmd->u.ret) + err = -EIO; + + memcpy(req->result, ctx->hdev->loongson_engine->data_buffer, SM3_DIGEST_SIZE); + /* Init */ + memset(ctx->sm3_ctx, 0, LOONGSON_SM3_CTX_SIZE); +out: + return err; +} + +static int loongson_sm3_do_one_request(struct crypto_engine *engine, void *areq) +{ + struct ahash_request *req = container_of(areq, struct ahash_request, base); + struct loongson_hash_ctx *ctx = crypto_ahash_ctx(crypto_ahash_reqtfm(req)); + struct loongson_hash_reqctx *rctx = ahash_request_ctx(req); + int err; + + if (rctx->op & LOONGSON_HASH_UPDATE) { + err = loongson_sm3_do_update(req, ctx, rctx); + if (err) + goto out; + } + + if (rctx->op & LOONGSON_HASH_FINAL) + err = loongson_sm3_do_final(req, ctx, rctx); + +out: + crypto_finalize_hash_request(ctx->hdev->crypto_engine, req, err); + + return err; +} + +static int loongson_sm3_export(struct ahash_request *req, void *out) +{ + struct loongson_hash_ctx *ctx = crypto_ahash_ctx(crypto_ahash_reqtfm(req)); + + memcpy(out, ctx->sm3_ctx, LOONGSON_SM3_CTX_SIZE); + + return 0; +} + +static int loongson_sm3_import(struct ahash_request *req, const void *in) +{ + struct loongson_hash_ctx *ctx = crypto_ahash_ctx(crypto_ahash_reqtfm(req)); + + memcpy(ctx->sm3_ctx, in, LOONGSON_SM3_CTX_SIZE); + + return 0; +} + +static int loongson_ahash_init(struct crypto_tfm *tfm) +{ + struct loongson_hash_ctx *ctx = crypto_tfm_ctx(tfm); + struct loongson_hash_dev *hdev; + u32 min_used = U32_MAX; + + mutex_lock(&hash_devices.lock); + list_for_each_entry(hdev, &hash_devices.list, list) { + if (hdev->used < min_used) { + ctx->hdev = hdev; + min_used = hdev->used; + } + } + ctx->hdev->used++; + mutex_unlock(&hash_devices.lock); + + crypto_ahash_set_reqsize(__crypto_ahash_cast(tfm), + sizeof(struct loongson_hash_reqctx)); + + return 0; +} + +static void loongson_ahash_exit(struct crypto_tfm *tfm) +{ + struct loongson_hash_ctx *ctx = crypto_tfm_ctx(tfm); + + mutex_lock(&hash_devices.lock); + ctx->hdev->used--; + mutex_unlock(&hash_devices.lock); +} + +static struct ahash_engine_alg loongson_sm3 = { + .base = { + .init = loongson_sm3_init, + .update = loongson_sm3_update, + .final = loongson_sm3_final, + .finup = loongson_sm3_finup, + .digest = loongson_sm3_finup, + .export = loongson_sm3_export, + .import = loongson_sm3_import, + .halg.digestsize = SM3_DIGEST_SIZE, + .halg.statesize = LOONGSON_SM3_CTX_SIZE, + .halg.base = { + .cra_name = "sm3", + .cra_driver_name = "loongson-sm3", + .cra_priority = 300, + .cra_flags = CRYPTO_ALG_ASYNC, + .cra_blocksize = SM3_BLOCK_SIZE, + .cra_ctxsize = sizeof(struct loongson_hash_ctx), + .cra_module = THIS_MODULE, + .cra_init = loongson_ahash_init, + .cra_exit = loongson_ahash_exit, + }, + }, + .op.do_one_request = loongson_sm3_do_one_request, +}; + +static int loongson_hash_probe(struct platform_device *pdev) +{ + struct loongson_hash_cmd *cmd; + struct loongson_hash_dev *hdev; + int ret = 0; + + hdev = devm_kzalloc(&pdev->dev, sizeof(*hdev), GFP_KERNEL); + if (!hdev) + return -ENOMEM; + + hdev->loongson_engine = loongson_se_init_engine(pdev->dev.parent, SE_ENGINE_HASH); + if (!hdev->loongson_engine) + return -ENODEV; + + cmd = hdev->loongson_engine->command; + cmd->digest_off = hdev->loongson_engine->buffer_off; + cmd->block_off = hdev->loongson_engine->buffer_off + LOONGSON_SM3_CTX_SIZE; + + hdev->crypto_engine = crypto_engine_alloc_init(&pdev->dev, 1); + crypto_engine_start(hdev->crypto_engine); + + mutex_lock(&hash_devices.lock); + if (!hash_devices.registered) { + hash_devices.registered = 1; + list_add_tail(&hdev->list, &hash_devices.list); + mutex_unlock(&hash_devices.lock); + + ret = crypto_engine_register_ahash(&loongson_sm3); + if (ret) + dev_err(&pdev->dev, "failed to register crypto(%d)\n", ret); + + return ret; + } + + list_add_tail(&hdev->list, &hash_devices.list); + mutex_unlock(&hash_devices.lock); + + return ret; +} + +static struct platform_driver loongson_hash_driver = { + .probe = loongson_hash_probe, + .driver = { + .name = "loongson-hash", + }, +}; +module_platform_driver(loongson_hash_driver); + +MODULE_ALIAS("platform:loongson-hash"); +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Yinggang Gu "); +MODULE_AUTHOR("Qunqin Zhao "); +MODULE_DESCRIPTION("Loongson hash acceleration engine driver"); diff --git a/drivers/crypto/loongson/loongson-rng.c b/drivers/crypto/loongson/loongson-rng.c new file mode 100644 index 000000000000..3a4940260f9e --- /dev/null +++ b/drivers/crypto/loongson/loongson-rng.c @@ -0,0 +1,209 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Copyright (c) 2019 HiSilicon Limited. */ +/* Copyright (c) 2025 Loongson Technology Corporation Limited. */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define SE_SEED_SIZE 32 + +struct loongson_rng_list { + struct mutex lock; + struct list_head list; + int registered; +}; + +struct loongson_rng { + u32 used; + struct loongson_se_engine *engine; + struct list_head list; + struct mutex lock; +}; + +struct loongson_rng_ctx { + struct loongson_rng *rng; +}; + +struct loongson_rng_cmd { + u32 cmd_id; + union { + u32 len; + u32 ret; + } u; + u32 seed_off; + u32 out_off; + u32 pad[4]; +}; + +static struct loongson_rng_list rng_devices = { + .lock = __MUTEX_INITIALIZER(rng_devices.lock), + .list = LIST_HEAD_INIT(rng_devices.list), +}; + +static int loongson_rng_generate(struct crypto_rng *tfm, const u8 *src, + unsigned int slen, u8 *dstn, unsigned int dlen) +{ + struct loongson_rng_ctx *ctx = crypto_rng_ctx(tfm); + struct loongson_rng *rng = ctx->rng; + struct loongson_rng_cmd *cmd = rng->engine->command; + int err, len; + + mutex_lock(&rng->lock); + cmd->seed_off = 0; + do { + len = min(dlen, rng->engine->buffer_size); + cmd = rng->engine->command; + cmd->u.len = len; + err = loongson_se_send_engine_cmd(rng->engine); + if (err) + break; + + cmd = rng->engine->command_ret; + if (cmd->u.ret) { + err = -EIO; + break; + } + + memcpy(dstn, rng->engine->data_buffer, len); + dlen -= len; + dstn += len; + } while (dlen > 0); + mutex_unlock(&rng->lock); + + return err; +} + +static int loongson_rng_init(struct crypto_tfm *tfm) +{ + struct loongson_rng_ctx *ctx = crypto_tfm_ctx(tfm); + struct loongson_rng *rng; + u32 min_used = U32_MAX; + + mutex_lock(&rng_devices.lock); + list_for_each_entry(rng, &rng_devices.list, list) { + if (rng->used < min_used) { + ctx->rng = rng; + min_used = rng->used; + } + } + ctx->rng->used++; + mutex_unlock(&rng_devices.lock); + + return 0; +} + +static void loongson_rng_exit(struct crypto_tfm *tfm) +{ + struct loongson_rng_ctx *ctx = crypto_tfm_ctx(tfm); + + mutex_lock(&rng_devices.lock); + ctx->rng->used--; + mutex_unlock(&rng_devices.lock); +} + +static int loongson_rng_seed(struct crypto_rng *tfm, const u8 *seed, + unsigned int slen) +{ + struct loongson_rng_ctx *ctx = crypto_rng_ctx(tfm); + struct loongson_rng *rng = ctx->rng; + struct loongson_rng_cmd *cmd; + int err; + + if (slen < SE_SEED_SIZE) + return -EINVAL; + + slen = min(slen, rng->engine->buffer_size); + + mutex_lock(&rng->lock); + cmd = rng->engine->command; + cmd->u.len = slen; + cmd->seed_off = rng->engine->buffer_off; + memcpy(rng->engine->data_buffer, seed, slen); + err = loongson_se_send_engine_cmd(rng->engine); + if (err) + goto out; + + cmd = rng->engine->command_ret; + if (cmd->u.ret) + err = -EIO; +out: + mutex_unlock(&rng->lock); + + return err; +} + +static struct rng_alg loongson_rng_alg = { + .generate = loongson_rng_generate, + .seed = loongson_rng_seed, + .seedsize = SE_SEED_SIZE, + .base = { + .cra_name = "stdrng", + .cra_driver_name = "loongson_stdrng", + .cra_priority = 300, + .cra_ctxsize = sizeof(struct loongson_rng_ctx), + .cra_module = THIS_MODULE, + .cra_init = loongson_rng_init, + .cra_exit = loongson_rng_exit, + }, +}; + +static int loongson_rng_probe(struct platform_device *pdev) +{ + struct loongson_rng_cmd *cmd; + struct loongson_rng *rng; + int ret = 0; + + rng = devm_kzalloc(&pdev->dev, sizeof(*rng), GFP_KERNEL); + if (!rng) + return -ENOMEM; + + rng->engine = loongson_se_init_engine(pdev->dev.parent, SE_ENGINE_RNG); + if (!rng->engine) + return -ENODEV; + cmd = rng->engine->command; + cmd->cmd_id = SE_CMD_RNG; + cmd->out_off = rng->engine->buffer_off; + mutex_init(&rng->lock); + + mutex_lock(&rng_devices.lock); + + if (!rng_devices.registered) { + ret = crypto_register_rng(&loongson_rng_alg); + if (ret) { + dev_err(&pdev->dev, "failed to register crypto(%d)\n", ret); + goto out; + } + rng_devices.registered = 1; + } + + list_add_tail(&rng->list, &rng_devices.list); +out: + mutex_unlock(&rng_devices.lock); + + return ret; +} + +static struct platform_driver loongson_rng_driver = { + .probe = loongson_rng_probe, + .driver = { + .name = "loongson-rng", + }, +}; +module_platform_driver(loongson_rng_driver); + +MODULE_ALIAS("platform:loongson-rng"); +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Yinggang Gu "); +MODULE_AUTHOR("Qunqin Zhao "); +MODULE_DESCRIPTION("Loongson Random Number Generator driver"); diff --git a/drivers/crypto/loongson/loongson-skcipher.c b/drivers/crypto/loongson/loongson-skcipher.c new file mode 100644 index 000000000000..c7b6fad4e67f --- /dev/null +++ b/drivers/crypto/loongson/loongson-skcipher.c @@ -0,0 +1,248 @@ +// SPDX-License-Identifier: GPL-2.0 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define LOONGSON_SM4_CTX_SIZE 64 + +#define LOONGSON_SKCIPHER_ENCRYPT 0 +#define LOONGSON_SKCIPHER_DECRYPT 1 + +struct loongson_skcipher_dev_list { + struct mutex lock; + struct list_head list; + int registered; +}; + +struct loongson_skcipher_dev { + struct loongson_se_engine *loongson_engine; + struct crypto_engine *crypto_engine; + struct list_head list; + u8 sm4_ctx[LOONGSON_SM4_CTX_SIZE]; + u32 used; +}; + +struct loongson_skcipher_ctx { + struct loongson_skcipher_dev *sdev; +}; + +struct loongson_skcipher_reqctx { + int op; +}; + +struct loongson_skcipher_cmd { + u32 cmd_id; + union { + u32 len; + u32 ret; + } u; + u32 in_off; + u32 out_off; + u32 key_off; + u32 pad[3]; +}; + +static struct loongson_skcipher_dev_list skcipher_devices = { + .lock = __MUTEX_INITIALIZER(skcipher_devices.lock), + .list = LIST_HEAD_INIT(skcipher_devices.list), +}; + +static int loongson_sm4_setkey(struct crypto_skcipher *tfm, const u8 *key, + unsigned int keylen) +{ + struct loongson_skcipher_ctx *ctx = crypto_skcipher_ctx(tfm); + + if (keylen != SM4_KEY_SIZE) + return -EINVAL; + + memcpy(ctx->sdev->sm4_ctx, key, keylen); + + return 0; +} + +static int loongson_sm4_encrypt(struct skcipher_request *req) +{ + struct loongson_skcipher_ctx *ctx = crypto_skcipher_ctx(crypto_skcipher_reqtfm(req)); + struct loongson_skcipher_reqctx *rctx = skcipher_request_ctx(req); + + if (req->cryptlen % SM4_BLOCK_SIZE) + return -EINVAL; + + rctx->op = LOONGSON_SKCIPHER_ENCRYPT; + + return crypto_transfer_skcipher_request_to_engine(ctx->sdev->crypto_engine, req); +} + +static int loongson_sm4_decrypt(struct skcipher_request *req) +{ + struct loongson_skcipher_ctx *ctx = crypto_skcipher_ctx(crypto_skcipher_reqtfm(req)); + struct loongson_skcipher_reqctx *rctx = skcipher_request_ctx(req); + + if (req->cryptlen % SM4_BLOCK_SIZE) + return -EINVAL; + + rctx->op = LOONGSON_SKCIPHER_DECRYPT; + + return crypto_transfer_skcipher_request_to_engine(ctx->sdev->crypto_engine, req); +} + +static int loongson_sm4_do_one_request(struct crypto_engine *engine, void *areq) +{ + struct skcipher_request *req = container_of(areq, struct skcipher_request, base); + struct loongson_skcipher_ctx *ctx = crypto_skcipher_ctx(crypto_skcipher_reqtfm(req)); + struct loongson_skcipher_reqctx *rctx = skcipher_request_ctx(req); + void *dma_buff = ctx->sdev->loongson_engine->data_buffer + LOONGSON_SM4_CTX_SIZE; + u32 dma_buff_size = ctx->sdev->loongson_engine->buffer_size - LOONGSON_SM4_CTX_SIZE; + struct loongson_skcipher_cmd *cmd; + int err = 0, skip = 0, copyed; + + memcpy(ctx->sdev->loongson_engine->data_buffer, ctx->sdev->sm4_ctx, LOONGSON_SM4_CTX_SIZE); + + while (skip < req->cryptlen) { + copyed = sg_pcopy_to_buffer(req->src, sg_nents(req->src), + dma_buff, min(dma_buff_size, req->cryptlen), skip); + + cmd = ctx->sdev->loongson_engine->command; + cmd->cmd_id = SE_CMD_SKCIPHER | rctx->op; + cmd->u.len = ALIGN(copyed, LOONGSON_SM4_CTX_SIZE); + err = loongson_se_send_engine_cmd(ctx->sdev->loongson_engine); + if (err) + break; + + cmd = ctx->sdev->loongson_engine->command_ret; + if (cmd->u.ret) { + err = -EIO; + break; + } + + sg_pcopy_from_buffer(req->dst, sg_nents(req->dst), + dma_buff, min(dma_buff_size, req->cryptlen), skip); + + skip += copyed; + } + + crypto_finalize_skcipher_request(ctx->sdev->crypto_engine, req, err); + + return err; +} + +static int loongson_skcipher_init(struct crypto_tfm *tfm) +{ + struct loongson_skcipher_ctx *ctx = crypto_tfm_ctx(tfm); + struct loongson_skcipher_dev *sdev; + u32 min_used = U32_MAX; + + mutex_lock(&skcipher_devices.lock); + list_for_each_entry(sdev, &skcipher_devices.list, list) { + if (sdev->used < min_used) { + ctx->sdev = sdev; + min_used = sdev->used; + } + } + ctx->sdev->used++; + mutex_unlock(&skcipher_devices.lock); + + crypto_skcipher_set_reqsize(__crypto_skcipher_cast(tfm), + sizeof(struct loongson_skcipher_reqctx)); + + return 0; +} + +static void loongson_skcipher_exit(struct crypto_tfm *tfm) +{ + struct loongson_skcipher_ctx *ctx = crypto_tfm_ctx(tfm); + + mutex_lock(&skcipher_devices.lock); + ctx->sdev->used--; + mutex_unlock(&skcipher_devices.lock); +} + +static struct skcipher_engine_alg loongson_sm4 = { + .base = { + .min_keysize = SM4_KEY_SIZE, + .max_keysize = SM4_KEY_SIZE, + .setkey = loongson_sm4_setkey, + .encrypt = loongson_sm4_encrypt, + .decrypt = loongson_sm4_decrypt, + .base = { + .cra_name = "ecb(sm4)", + .cra_driver_name = "loongson-ecb(sm4)", + .cra_priority = 300, + .cra_flags = CRYPTO_ALG_ASYNC, + .cra_blocksize = SM4_BLOCK_SIZE, + .cra_ctxsize = sizeof(struct loongson_skcipher_ctx), + .cra_module = THIS_MODULE, + .cra_init = loongson_skcipher_init, + .cra_exit = loongson_skcipher_exit, + }, + }, + .op.do_one_request = loongson_sm4_do_one_request, +}; + +static int loongson_skcipher_probe(struct platform_device *pdev) +{ + struct loongson_skcipher_cmd *cmd; + struct loongson_skcipher_dev *sdev; + int ret = 0; + + sdev = devm_kzalloc(&pdev->dev, sizeof(*sdev), GFP_KERNEL); + if (!sdev) + return -ENOMEM; + + sdev->loongson_engine = loongson_se_init_engine(pdev->dev.parent, SE_ENGINE_SKCIPHER); + if (!sdev->loongson_engine) + return -ENODEV; + + cmd = sdev->loongson_engine->command; + cmd->key_off = sdev->loongson_engine->buffer_off; + cmd->in_off = sdev->loongson_engine->buffer_off + LOONGSON_SM4_CTX_SIZE; + cmd->out_off = cmd->in_off; + + sdev->crypto_engine = crypto_engine_alloc_init(&pdev->dev, 1); + crypto_engine_start(sdev->crypto_engine); + + mutex_lock(&skcipher_devices.lock); + if (!skcipher_devices.registered) { + skcipher_devices.registered = 1; + list_add_tail(&sdev->list, &skcipher_devices.list); + mutex_unlock(&skcipher_devices.lock); + + ret = crypto_engine_register_skcipher(&loongson_sm4); + if (ret) + dev_err(&pdev->dev, "failed to register crypto(%d)\n", ret); + + return ret; + } + + list_add_tail(&sdev->list, &skcipher_devices.list); + mutex_unlock(&skcipher_devices.lock); + + return ret; +} + +static struct platform_driver loongson_skcipher_driver = { + .probe = loongson_skcipher_probe, + .driver = { + .name = "loongson-skcipher", + }, +}; +module_platform_driver(loongson_skcipher_driver); + +MODULE_ALIAS("platform:loongson-skcipher"); +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Yinggang Gu "); +MODULE_AUTHOR("Qunqin Zhao "); +MODULE_DESCRIPTION("Loongson skcipher acceleration engine driver"); diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig index 68d71b4b55bd..fc2eee58f61b 100644 --- a/drivers/mfd/Kconfig +++ b/drivers/mfd/Kconfig @@ -2305,6 +2305,17 @@ config MFD_INTEL_M10_BMC_PMCI additional drivers must be enabled in order to use the functionality of the device. +config MFD_LOONGSON_SE + tristate "Loongson Security Engine chip controller driver" + depends on LOONGARCH && ACPI + select MFD_CORE + help + The Loongson Security Engine chip supports RNG, SM2, SM3 and + SM4 accelerator engines. Each engine have its own DMA buffer + provided by the controller. The kernel cannot directly send + commands to the engine and must first send them to the controller, + which will forward them to the corresponding engine. + config MFD_RSMU_I2C tristate "Renesas Synchronization Management Unit with I2C" depends on I2C && OF diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile index db1ba39de3b5..d513de377359 100644 --- a/drivers/mfd/Makefile +++ b/drivers/mfd/Makefile @@ -282,3 +282,5 @@ obj-$(CONFIG_MFD_ATC260X_I2C) += atc260x-i2c.o obj-$(CONFIG_MFD_RSMU_I2C) += rsmu_i2c.o rsmu_core.o obj-$(CONFIG_MFD_RSMU_SPI) += rsmu_spi.o rsmu_core.o + +obj-$(CONFIG_MFD_LOONGSON_SE) += loongson-se.o diff --git a/drivers/mfd/loongson-se.c b/drivers/mfd/loongson-se.c new file mode 100644 index 000000000000..2692a4e93a3a --- /dev/null +++ b/drivers/mfd/loongson-se.c @@ -0,0 +1,256 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2025 Loongson Technology Corporation Limited + * + * Author: Yinggang Gu + * Author: Qunqin Zhao + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +struct loongson_se { + void __iomem *base; + spinlock_t dev_lock; + struct completion cmd_completion; + + void *dmam_base; + int dmam_size; + + struct mutex engine_init_lock; + struct loongson_se_engine engines[SE_ENGINE_MAX]; +}; + +struct loongson_se_controller_cmd { + u32 command_id; + u32 info[7]; +}; + +static int loongson_se_poll(struct loongson_se *se, u32 int_bit) +{ + u32 status; + int err; + + spin_lock_irq(&se->dev_lock); + + /* Notify the controller that the engine needs to be started */ + writel(int_bit, se->base + SE_L2SINT_SET); + + /* Polling until the controller has forwarded the engine command */ + err = readl_relaxed_poll_timeout_atomic(se->base + SE_L2SINT_STAT, status, + !(status & int_bit), + 1, LOONGSON_ENGINE_CMD_TIMEOUT_US); + + spin_unlock_irq(&se->dev_lock); + + return err; +} + +static int loongson_se_send_controller_cmd(struct loongson_se *se, + struct loongson_se_controller_cmd *cmd) +{ + u32 *send_cmd = (u32 *)cmd; + int err, i; + + for (i = 0; i < SE_SEND_CMD_REG_LEN; i++) + writel(send_cmd[i], se->base + SE_SEND_CMD_REG + i * 4); + + err = loongson_se_poll(se, SE_INT_CONTROLLER); + if (err) + return err; + + return wait_for_completion_interruptible(&se->cmd_completion); +} + +int loongson_se_send_engine_cmd(struct loongson_se_engine *engine) +{ + /* + * After engine initialization, the controller already knows + * where to obtain engine commands from. Now all we need to + * do is notify the controller that the engine needs to be started. + */ + int err = loongson_se_poll(engine->se, BIT(engine->id)); + + if (err) + return err; + + return wait_for_completion_interruptible(&engine->completion); +} +EXPORT_SYMBOL_GPL(loongson_se_send_engine_cmd); + +struct loongson_se_engine *loongson_se_init_engine(struct device *dev, int id) +{ + struct loongson_se *se = dev_get_drvdata(dev); + struct loongson_se_engine *engine = &se->engines[id]; + struct loongson_se_controller_cmd cmd; + + engine->se = se; + engine->id = id; + init_completion(&engine->completion); + + /* Divide DMA memory equally among all engines */ + engine->buffer_size = se->dmam_size / SE_ENGINE_MAX; + engine->buffer_off = (se->dmam_size / SE_ENGINE_MAX) * id; + engine->data_buffer = se->dmam_base + engine->buffer_off; + + /* + * There has no engine0, use its data buffer as command buffer for other + * engines. The DMA memory size is obtained from the ACPI table, which + * ensures that the data buffer size of engine0 is larger than the + * command buffer size of all engines. + */ + engine->command = se->dmam_base + id * (2 * SE_ENGINE_CMD_SIZE); + engine->command_ret = engine->command + SE_ENGINE_CMD_SIZE; + + mutex_lock(&se->engine_init_lock); + + /* Tell the controller where to find engine command */ + cmd.command_id = SE_CMD_SET_ENGINE_CMDBUF; + cmd.info[0] = id; + cmd.info[1] = engine->command - se->dmam_base; + cmd.info[2] = 2 * SE_ENGINE_CMD_SIZE; + + if (loongson_se_send_controller_cmd(se, &cmd)) + engine = NULL; + + mutex_unlock(&se->engine_init_lock); + + return engine; +} +EXPORT_SYMBOL_GPL(loongson_se_init_engine); + +static irqreturn_t se_irq_handler(int irq, void *dev_id) +{ + struct loongson_se *se = dev_id; + u32 int_status; + int id; + + spin_lock(&se->dev_lock); + + int_status = readl(se->base + SE_S2LINT_STAT); + + + /* For controller */ + if (int_status & SE_INT_CONTROLLER) { + complete(&se->cmd_completion); + int_status &= ~SE_INT_CONTROLLER; + writel(SE_INT_CONTROLLER, se->base + SE_S2LINT_CL); + } + + /* For engines */ + while (int_status) { + id = __ffs(int_status); + complete(&se->engines[id].completion); + int_status &= ~BIT(id); + writel(BIT(id), se->base + SE_S2LINT_CL); + } + + spin_unlock(&se->dev_lock); + + return IRQ_HANDLED; +} + +static int loongson_se_init(struct loongson_se *se, dma_addr_t addr, int size) +{ + struct loongson_se_controller_cmd cmd; + int err; + + cmd.command_id = SE_CMD_START; + err = loongson_se_send_controller_cmd(se, &cmd); + if (err) + return err; + + cmd.command_id = SE_CMD_SET_DMA; + cmd.info[0] = lower_32_bits(addr); + cmd.info[1] = upper_32_bits(addr); + cmd.info[2] = size; + + return loongson_se_send_controller_cmd(se, &cmd); +} + +static const struct mfd_cell engines[] = { + { .name = "loongson-hash" }, + { .name = "loongson-rng" }, + { .name = "loongson-skcipher" }, + { .name = "tpm_loongson" }, +}; + +static int loongson_se_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct loongson_se *se; + int nr_irq, irq, err, i; + dma_addr_t paddr; + + se = devm_kmalloc(dev, sizeof(*se), GFP_KERNEL); + if (!se) + return -ENOMEM; + + dev_set_drvdata(dev, se); + init_completion(&se->cmd_completion); + spin_lock_init(&se->dev_lock); + mutex_init(&se->engine_init_lock); + + dma_set_mask_and_coherent(dev, DMA_BIT_MASK(64)); + if (device_property_read_u32(dev, "dmam_size", &se->dmam_size)) + return -ENODEV; + + se->dmam_base = dmam_alloc_coherent(dev, se->dmam_size, &paddr, GFP_KERNEL); + if (!se->dmam_base) + return -ENOMEM; + + se->base = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(se->base)) + return PTR_ERR(se->base); + + writel(SE_INT_ALL, se->base + SE_S2LINT_EN); + + nr_irq = platform_irq_count(pdev); + if (nr_irq <= 0) + return -ENODEV; + + for (i = 0; i < nr_irq; i++) { + irq = platform_get_irq(pdev, i); + err = devm_request_irq(dev, irq, se_irq_handler, 0, "loongson-se", se); + if (err) + dev_err(dev, "failed to request IRQ: %d\n", irq); + } + + err = loongson_se_init(se, paddr, se->dmam_size); + if (err) + return err; + + return devm_mfd_add_devices(dev, PLATFORM_DEVID_NONE, engines, + ARRAY_SIZE(engines), NULL, 0, NULL); +} + +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, + .driver = { + .name = "loongson-se", + .acpi_match_table = loongson_se_acpi_match, + }, +}; +module_platform_driver(loongson_se_driver); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Yinggang Gu "); +MODULE_AUTHOR("Qunqin Zhao "); +MODULE_DESCRIPTION("Loongson Security Engine chip controller driver"); diff --git a/include/linux/mfd/loongson-se.h b/include/linux/mfd/loongson-se.h new file mode 100644 index 000000000000..48c69aa60dd4 --- /dev/null +++ b/include/linux/mfd/loongson-se.h @@ -0,0 +1,59 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* Copyright (C) 2025 Loongson Technology Corporation Limited */ + +#ifndef __MFD_LOONGSON_SE_H__ +#define __MFD_LOONGSON_SE_H__ + +#define LOONGSON_ENGINE_CMD_TIMEOUT_US 10000 +#define SE_SEND_CMD_REG 0x0 +#define SE_SEND_CMD_REG_LEN 0x8 +/* Controller command ID */ +#define SE_CMD_START 0x0 +#define SE_CMD_SET_DMA 0x3 +#define SE_CMD_SET_ENGINE_CMDBUF 0x4 + +#define SE_S2LINT_STAT 0x88 +#define SE_S2LINT_EN 0x8c +#define SE_S2LINT_CL 0x94 +#define SE_L2SINT_STAT 0x98 +#define SE_L2SINT_SET 0xa0 + +#define SE_INT_ALL 0xffffffff +#define SE_INT_CONTROLLER BIT(0) + +#define SE_ENGINE_MAX 16 +#define SE_ENGINE_RNG 1 +#define SE_CMD_RNG 0x100 + +#define SE_ENGINE_HASH 3 +#define SE_CMD_HASH 0x300 + +#define SE_ENGINE_SKCIPHER 4 +#define SE_CMD_SKCIPHER 0x400 + +#define SE_ENGINE_TPM 5 +#define SE_CMD_TPM 0x500 + +#define SE_ENGINE_CMD_SIZE 32 + +struct loongson_se_engine { + struct loongson_se *se; + int id; + + /* Command buffer */ + void *command; + void *command_ret; + + void *data_buffer; + uint buffer_size; + /* Data buffer offset to DMA base */ + uint buffer_off; + + struct completion completion; + +}; + +struct loongson_se_engine *loongson_se_init_engine(struct device *dev, int id); +int loongson_se_send_engine_cmd(struct loongson_se_engine *engine); + +#endif -- Gitee From d5fdb1706a10869aec3c03cac2185ec627eac55b Mon Sep 17 00:00:00 2001 From: Qunqin Zhao Date: Tue, 15 Jul 2025 14:23:54 +0800 Subject: [PATCH 4/4] EDAC: Add EDAC driver for loongson memory controller commit 558aff7a63f67dc4723a4deed419a2dfd0fb14f2 upstream. Add ECC support for Loongson SoC DDR controller. This driver reports single bit errors (CE) only. Only ACPI firmware is supported. Signed-off-by: Qunqin Zhao Link: https://lore.kernel.org/r/20241219124846.1876-1-zhaoqunqin@loongson.cn Signed-off-by: Ming Wang --- MAINTAINERS | 6 + arch/loongarch/Kconfig | 1 + arch/loongarch/configs/loongson3_defconfig | 2 + drivers/edac/Kconfig | 9 ++ drivers/edac/Makefile | 1 + drivers/edac/loongson_edac.c | 152 +++++++++++++++++++++ 6 files changed, 171 insertions(+) create mode 100644 drivers/edac/loongson_edac.c diff --git a/MAINTAINERS b/MAINTAINERS index b0b6ab5d069f..082aac073070 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -12470,6 +12470,12 @@ S: Maintained F: Documentation/devicetree/bindings/thermal/loongson,ls2k-thermal.yaml F: drivers/thermal/loongson2_thermal.c +LOONGSON EDAC DRIVER +M: Zhao Qunqin +L: linux-edac@vger.kernel.org +S: Maintained +F: drivers/edac/loongson_edac.c + LSILOGIC MPT FUSION DRIVERS (FC/SAS/SPI) M: Sathya Prakash M: Sreekanth Reddy diff --git a/arch/loongarch/Kconfig b/arch/loongarch/Kconfig index e27f7aa3c963..929a22cd3f49 100644 --- a/arch/loongarch/Kconfig +++ b/arch/loongarch/Kconfig @@ -69,6 +69,7 @@ config LOONGARCH select BUILDTIME_TABLE_SORT select COMMON_CLK select CPU_PM + select EDAC_SUPPORT select EFI select GENERIC_CLOCKEVENTS select GENERIC_CMOS_UPDATE diff --git a/arch/loongarch/configs/loongson3_defconfig b/arch/loongarch/configs/loongson3_defconfig index 42b8f7a1fae7..f899ffca02f2 100644 --- a/arch/loongarch/configs/loongson3_defconfig +++ b/arch/loongarch/configs/loongson3_defconfig @@ -1854,6 +1854,8 @@ CONFIG_INFINIBAND_SRP=m CONFIG_INFINIBAND_SRPT=m CONFIG_INFINIBAND_ISER=m CONFIG_INFINIBAND_ISERT=m +CONFIG_EDAC=y +CONFIG_EDAC_LOONGSON=y CONFIG_RTC_CLASS=y CONFIG_RTC_DRV_DS1307=m CONFIG_RTC_DRV_DS1374=m diff --git a/drivers/edac/Kconfig b/drivers/edac/Kconfig index 110e99b86a66..0300dc8d11d0 100644 --- a/drivers/edac/Kconfig +++ b/drivers/edac/Kconfig @@ -561,4 +561,13 @@ config EDAC_NPCM error detection (in-line ECC in which a section 1/8th of the memory device used to store data is used for ECC storage). +config EDAC_LOONGSON + tristate "Loongson Memory Controller" + depends on LOONGARCH && ACPI + help + Support for error detection and correction on the Loongson + family memory controller. This driver reports single bit + errors (CE) only. Loongson-3A5000/3C5000/3D5000/3A6000/3C6000 + are compatible. + endif # EDAC diff --git a/drivers/edac/Makefile b/drivers/edac/Makefile index 446364264e2b..699b818ac7cb 100644 --- a/drivers/edac/Makefile +++ b/drivers/edac/Makefile @@ -88,3 +88,4 @@ obj-$(CONFIG_EDAC_BLUEFIELD) += bluefield_edac.o obj-$(CONFIG_EDAC_DMC520) += dmc520_edac.o obj-$(CONFIG_EDAC_NPCM) += npcm_edac.o obj-$(CONFIG_EDAC_ZYNQMP) += zynqmp_edac.o +obj-$(CONFIG_EDAC_LOONGSON) += loongson_edac.o diff --git a/drivers/edac/loongson_edac.c b/drivers/edac/loongson_edac.c new file mode 100644 index 000000000000..46d6a3ab2ce2 --- /dev/null +++ b/drivers/edac/loongson_edac.c @@ -0,0 +1,152 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2024 Loongson Technology Corporation Limited. + */ + +#include +#include +#include +#include +#include +#include +#include "edac_module.h" + +#define ECC_CS_COUNT_REG 0x18 + +struct loongson_edac_pvt { + void __iomem *ecc_base; + int last_ce_count; +}; + +static int read_ecc(struct mem_ctl_info *mci) +{ + struct loongson_edac_pvt *pvt = mci->pvt_info; + u64 ecc; + int cs; + + ecc = readq(pvt->ecc_base + ECC_CS_COUNT_REG); + /* cs0 -- cs3 */ + cs = ecc & 0xff; + cs += (ecc >> 8) & 0xff; + cs += (ecc >> 16) & 0xff; + cs += (ecc >> 24) & 0xff; + + return cs; +} + +static void edac_check(struct mem_ctl_info *mci) +{ + struct loongson_edac_pvt *pvt = mci->pvt_info; + int new, add; + + new = read_ecc(mci); + add = new - pvt->last_ce_count; + pvt->last_ce_count = new; + if (add <= 0) + return; + + edac_mc_handle_error(HW_EVENT_ERR_CORRECTED, mci, add, + 0, 0, 0, 0, 0, -1, "error", ""); +} + +static void dimm_config_init(struct mem_ctl_info *mci) +{ + struct dimm_info *dimm; + u32 size, npages; + + /* size not used */ + size = -1; + npages = MiB_TO_PAGES(size); + + dimm = edac_get_dimm(mci, 0, 0, 0); + dimm->nr_pages = npages; + snprintf(dimm->label, sizeof(dimm->label), + "MC#%uChannel#%u_DIMM#%u", mci->mc_idx, 0, 0); + dimm->grain = 8; +} + +static void pvt_init(struct mem_ctl_info *mci, void __iomem *vbase) +{ + struct loongson_edac_pvt *pvt = mci->pvt_info; + + pvt->ecc_base = vbase; + pvt->last_ce_count = read_ecc(mci); +} + +static int edac_probe(struct platform_device *pdev) +{ + struct edac_mc_layer layers[2]; + struct mem_ctl_info *mci; + void __iomem *vbase; + int ret; + + vbase = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(vbase)) + return PTR_ERR(vbase); + + layers[0].type = EDAC_MC_LAYER_CHANNEL; + layers[0].size = 1; + layers[0].is_virt_csrow = false; + layers[1].type = EDAC_MC_LAYER_SLOT; + layers[1].size = 1; + layers[1].is_virt_csrow = true; + mci = edac_mc_alloc(0, ARRAY_SIZE(layers), layers, + sizeof(struct loongson_edac_pvt)); + if (mci == NULL) + return -ENOMEM; + + mci->mc_idx = edac_device_alloc_index(); + mci->mtype_cap = MEM_FLAG_RDDR4; + mci->edac_ctl_cap = EDAC_FLAG_NONE; + mci->edac_cap = EDAC_FLAG_NONE; + mci->mod_name = "loongson_edac.c"; + mci->ctl_name = "loongson_edac_ctl"; + mci->dev_name = "loongson_edac_dev"; + mci->ctl_page_to_phys = NULL; + mci->pdev = &pdev->dev; + mci->error_desc.grain = 8; + mci->edac_check = edac_check; + + pvt_init(mci, vbase); + dimm_config_init(mci); + + ret = edac_mc_add_mc(mci); + if (ret) { + edac_dbg(0, "MC: failed edac_mc_add_mc()\n"); + edac_mc_free(mci); + return ret; + } + edac_op_state = EDAC_OPSTATE_POLL; + + return 0; +} + +static int edac_remove(struct platform_device *pdev) +{ + struct mem_ctl_info *mci = edac_mc_del_mc(&pdev->dev); + + if (mci) + edac_mc_free(mci); + + return 0; +} + +static const struct acpi_device_id loongson_edac_acpi_match[] = { + {"LOON0010", 0}, + {} +}; +MODULE_DEVICE_TABLE(acpi, loongson_edac_acpi_match); + +static struct platform_driver loongson_edac_driver = { + .probe = edac_probe, + .remove = edac_remove, + .driver = { + .name = "loongson-mc-edac", + .acpi_match_table = loongson_edac_acpi_match, + }, +}; +module_platform_driver(loongson_edac_driver); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Zhao Qunqin "); +MODULE_DESCRIPTION("EDAC driver for loongson memory controller"); -- Gitee