From: Ma Wupeng mawupeng1@huawei.com
hulk inclusion category: feature bugzilla: https://gitee.com/openeuler/kernel/issues/I9UDJX
--------------------------------
Last Level Cache driver for platforms such Kunpeng 920. This provides interfaces to enable LLC cache lockdown.
Signed-off-by: Ma Wupeng mawupeng1@huawei.com --- drivers/soc/hisilicon/Kconfig | 9 + drivers/soc/hisilicon/Makefile | 2 + drivers/soc/hisilicon/hisi_l3t.h | 46 ++++ drivers/soc/hisilicon/hisi_lockdown.c | 138 ++++++++++++ drivers/soc/hisilicon/l3t.c | 307 ++++++++++++++++++++++++++ 5 files changed, 502 insertions(+) create mode 100644 drivers/soc/hisilicon/hisi_l3t.h create mode 100644 drivers/soc/hisilicon/hisi_lockdown.c create mode 100644 drivers/soc/hisilicon/l3t.c
diff --git a/drivers/soc/hisilicon/Kconfig b/drivers/soc/hisilicon/Kconfig index 45754528873e..19c4f4602d51 100644 --- a/drivers/soc/hisilicon/Kconfig +++ b/drivers/soc/hisilicon/Kconfig @@ -54,4 +54,13 @@ config HISI_HBMDEV_ACLS
If not sure say no.
+config HISI_L3T + tristate "Add support for l3t" + depends on ARM64 && ACPI + help + Last Level Cache driver for platforms such Kunpeng 920. This provides + interfaces to enable LLC cache lockdown. + + If not sure say no. + endmenu diff --git a/drivers/soc/hisilicon/Makefile b/drivers/soc/hisilicon/Makefile index e0f966d1ed6d..022d83eccc79 100644 --- a/drivers/soc/hisilicon/Makefile +++ b/drivers/soc/hisilicon/Makefile @@ -4,3 +4,5 @@ obj-$(CONFIG_KUNPENG_HCCS) += kunpeng_hccs.o obj-$(CONFIG_HISI_HBMDEV) += hisi_hbmdev.o obj-$(CONFIG_HISI_HBMCACHE) += hisi_hbmcache.o obj-$(CONFIG_ARM64_PBHA) += pbha.o +hisi_l3t-objs := hisi_lockdown.o l3t.o +obj-$(CONFIG_HISI_L3T) += hisi_l3t.o diff --git a/drivers/soc/hisilicon/hisi_l3t.h b/drivers/soc/hisilicon/hisi_l3t.h new file mode 100644 index 000000000000..b3aebea36ae2 --- /dev/null +++ b/drivers/soc/hisilicon/hisi_l3t.h @@ -0,0 +1,46 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) Huawei Technologies Co., Ltd. 2024. All rights reserved. + */ + +#ifndef _HISI_L3T_H +#define _HISI_L3T_H + +/* L3T register definition */ +#define L3T_VERSION 0x1cf0 +#define L3T_LOCK_CTRL 0x0440 +#define L3T_LOCK_AREA 0x0444 +#define L3T_LOCK_START_L 0x0448 +#define L3T_LOCK_START_H 0x044C +#define L3T_LOCK_STEP 0x10 + +#define L3T_REG_NUM 4 + +extern struct mutex l3t_mutex; + +struct hisi_l3t { + struct list_head list; + struct device *dev; + int sccl_id; + int ccl_id; + void __iomem *base; + int nid; +}; + +struct hisi_sccl { + int nid; /* numa node id */ + int ccl_cnt; /* ccl count for this sccl */ + struct hisi_l3t **l3t; +}; + +struct hisi_sccl *hisi_l3t_get_sccl(int nid); +void hisi_l3t_read(struct hisi_l3t *l3t, int slot_idx, unsigned long *s_addr, + int *size); +void hisi_l3t_lock(struct hisi_l3t *l3t, int slot_idx, unsigned long s_addr, + int size); +void hisi_l3t_unlock(struct hisi_l3t *l3t, int slot_idx); + +int l3t_shared_lock(int nid, unsigned long pfn, unsigned long size); +int l3t_shared_unlock(int nid, unsigned long pfn, unsigned long size); + +#endif diff --git a/drivers/soc/hisilicon/hisi_lockdown.c b/drivers/soc/hisilicon/hisi_lockdown.c new file mode 100644 index 000000000000..2a9bc03e04c8 --- /dev/null +++ b/drivers/soc/hisilicon/hisi_lockdown.c @@ -0,0 +1,138 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) Huawei Technologies Co., Ltd. 2024. All rights reserved. + */ + +#define pr_fmt(fmt) "hisi_l3t: " fmt + +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/kallsyms.h> +#include <linux/mm.h> + +#include "hisi_l3t.h" + +static bool hisi_l3t_test_slot_empty(struct hisi_l3t *l3t, int slot_idx) +{ + unsigned long addr; + int size; + + if (slot_idx < 0 || slot_idx >= L3T_REG_NUM) { + pr_err("slot index is invalid: %d\n", slot_idx); + return false; + } + + hisi_l3t_read(l3t, slot_idx, &addr, &size); + + return addr == 0; +} + +static int hisi_l3t_lock_sccl(struct hisi_sccl *sccl, unsigned long addr, + int size) +{ + int ccl_cnt = sccl->ccl_cnt; + int i, slot_idx; + bool empty; + struct hisi_l3t *l3t; + + if (!sccl->l3t || !sccl->l3t[0]) + return -ENODEV; + + l3t = sccl->l3t[0]; + + mutex_lock(&l3t_mutex); + for (slot_idx = 0; slot_idx < L3T_REG_NUM; slot_idx++) { + empty = hisi_l3t_test_slot_empty(l3t, slot_idx); + if (empty) + break; + } + + if (slot_idx >= L3T_REG_NUM) { + mutex_unlock(&l3t_mutex); + return -ENOMEM; + } + + for (i = 0; i < ccl_cnt; i++) { + l3t = sccl->l3t[i]; + if (!l3t) + continue; + + hisi_l3t_lock(l3t, slot_idx, addr, size); + } + mutex_unlock(&l3t_mutex); + + return 0; +} + +int l3t_shared_lock(int nid, unsigned long pfn, unsigned long size) +{ + struct hisi_sccl *sccl; + + sccl = hisi_l3t_get_sccl(nid); + if (!sccl || !sccl->ccl_cnt) + return -ENODEV; + + return hisi_l3t_lock_sccl(sccl, pfn << PAGE_SHIFT, size); +} +EXPORT_SYMBOL_GPL(l3t_shared_lock); + +static bool hisi_l3t_test_equal(struct hisi_l3t *l3t, int slot_idx, + unsigned long addr, int size) +{ + unsigned long addr_inner; + int size_inner; + + hisi_l3t_read(l3t, slot_idx, &addr_inner, &size_inner); + + return (addr_inner == addr && size_inner == size); +} + +static int hisi_l3t_unlock_sccl(struct hisi_sccl *sccl, unsigned long addr, + int size) +{ + int ccl_cnt = sccl->ccl_cnt; + int slot_idx, i; + bool equal; + struct hisi_l3t *l3t; + + if (!sccl->l3t || !sccl->l3t[0]) + return -ENODEV; + + l3t = sccl->l3t[0]; + + mutex_lock(&l3t_mutex); + for (slot_idx = 0; slot_idx < L3T_REG_NUM; slot_idx++) { + equal = hisi_l3t_test_equal(l3t, slot_idx, addr, size); + if (equal) + break; + } + + if (slot_idx >= L3T_REG_NUM) { + mutex_unlock(&l3t_mutex); + return -EINVAL; + } + + for (i = 0; i < ccl_cnt; i++) { + l3t = sccl->l3t[i]; + if (!l3t) + continue; + + hisi_l3t_unlock(l3t, slot_idx); + } + mutex_unlock(&l3t_mutex); + + return 0; +} + +int l3t_shared_unlock(int nid, unsigned long pfn, unsigned long size) +{ + struct hisi_sccl *sccl; + + sccl = hisi_l3t_get_sccl(nid); + if (!sccl || !sccl->ccl_cnt) + return -ENODEV; + + return hisi_l3t_unlock_sccl(sccl, pfn << PAGE_SHIFT, size); +} +EXPORT_SYMBOL_GPL(l3t_shared_unlock); diff --git a/drivers/soc/hisilicon/l3t.c b/drivers/soc/hisilicon/l3t.c new file mode 100644 index 000000000000..b6be3c6f5efe --- /dev/null +++ b/drivers/soc/hisilicon/l3t.c @@ -0,0 +1,307 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) Huawei Technologies Co., Ltd. 2024. All rights reserved. + */ + +#define pr_fmt(fmt) "hisi_l3t: " fmt + +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/kallsyms.h> +#include <linux/mm.h> +#include <linux/slab.h> +#include <linux/platform_device.h> +#include <linux/smp.h> +#include <linux/xarray.h> +#include <linux/irqchip.h> + +#include "hisi_l3t.h" + +DEFINE_MUTEX(l3t_mutex); +static DEFINE_XARRAY(l3t_mapping); + +static const struct acpi_device_id hisi_l3t_acpi_match[] = { + { "HISI0501", }, + {} +}; +MODULE_DEVICE_TABLE(acpi, hisi_l3t_acpi_match); + +static int sccl_to_node_id(int id) +{ + int cpu; + u64 mpidr; + int sccl_id; + + for_each_possible_cpu(cpu) { + mpidr = cpu_logical_map(cpu); + sccl_id = MPIDR_AFFINITY_LEVEL(mpidr, 3); + if (sccl_id == id) + return cpu_to_node(cpu); + } + + pr_err("invalid sccl id: %d\n", id); + return -EINVAL; +} + +static int hisi_l3t_init_data(struct platform_device *pdev, + struct hisi_l3t *l3t) +{ + if (device_property_read_u32(&pdev->dev, "hisilicon,scl-id", + &l3t->sccl_id)) + return -EINVAL; + + if (device_property_read_u32(&pdev->dev, "hisilicon,ccl-id", + &l3t->ccl_id)) + return -EINVAL; + + l3t->base = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(l3t->base)) + return PTR_ERR(l3t->base); + + l3t->nid = sccl_to_node_id(l3t->sccl_id); + + pr_info("sccl id: %d ccl id: %d, nid: %d, base: %#lx\n", l3t->sccl_id, + l3t->ccl_id, l3t->nid, (unsigned long)l3t->base); + + return 0; +} + +static int hisi_l3t_insert_to_sccl(struct hisi_sccl *sccl, struct hisi_l3t *l3t) +{ + void *tmp_l3t; + + if (sccl->ccl_cnt < l3t->ccl_id) + goto set; + + tmp_l3t = krealloc(sccl->l3t, + (l3t->ccl_id + 1) * sizeof(struct hisi_l3t *), + GFP_KERNEL); + if (!tmp_l3t) + return -ENOMEM; + + sccl->ccl_cnt = l3t->ccl_id + 1; + sccl->l3t = tmp_l3t; + +set: + sccl->l3t[l3t->ccl_id] = l3t; + return 0; +} + +/* + * Use xarray to store the mapping b/t nid to sccl + * all ccls belong be one sccl is store with vla in sccl->l3t + */ +static int hisi_l3t_init_mapping(struct device *dev, struct hisi_l3t *l3t) +{ + struct hisi_sccl *sccl; + int ret = -ENOMEM; + + mutex_lock(&l3t_mutex); + sccl = xa_load(&l3t_mapping, l3t->nid); + if (!sccl) { + sccl = devm_kzalloc(dev, sizeof(*sccl), GFP_KERNEL); + if (!sccl) + goto unlock; + sccl->nid = l3t->nid; + + xa_store(&l3t_mapping, l3t->nid, sccl, GFP_KERNEL); + } + + ret = hisi_l3t_insert_to_sccl(sccl, l3t); +unlock: + mutex_unlock(&l3t_mutex); + + return ret; +} + +static int hisi_l3t_probe(struct platform_device *pdev) +{ + struct hisi_l3t *l3t; + int ret = -ENOMEM; + + l3t = devm_kzalloc(&pdev->dev, sizeof(*l3t), GFP_KERNEL); + if (!l3t) + return -ENOMEM; + + platform_set_drvdata(pdev, l3t); + + ret = hisi_l3t_init_data(pdev, l3t); + if (!ret) { + l3t->dev = &pdev->dev; + ret = hisi_l3t_init_mapping(&pdev->dev, l3t); + } + + return ret; +} + +static struct platform_driver hisi_l3t_driver = { + .driver = { + .name = "hisi_l3t", + .acpi_match_table = ACPI_PTR(hisi_l3t_acpi_match), + .suppress_bind_attrs = true, + }, + .probe = hisi_l3t_probe, +}; + +struct l3t_lock_ctrl { + union { + unsigned int u32; + struct { + unsigned int lock_en : 1; + unsigned int lock_done : 1; + unsigned int unlock_en : 1; + unsigned int unlock_done : 1; + } bits; + }; +}; + +static void l3t_lock(void __iomem *addr, int slot_idx, unsigned long s_addr, + int size) +{ + struct l3t_lock_ctrl ctrl; + + if (!addr) { + pr_err("invalid lock addr\n"); + return; + } + + writeq(s_addr, addr + L3T_LOCK_START_L + slot_idx * L3T_LOCK_STEP); + writel(size, addr + L3T_LOCK_AREA + slot_idx * L3T_LOCK_STEP); + + ctrl.u32 = 0; + ctrl.bits.lock_en = 1; + ctrl.bits.unlock_en = 0; + writel(ctrl.u32, addr + L3T_LOCK_CTRL + slot_idx * L3T_LOCK_STEP); + + do { + ctrl.u32 = + readl(addr + L3T_LOCK_CTRL + slot_idx * L3T_LOCK_STEP); + } while (ctrl.bits.lock_done == 0); + + pr_debug("lock success. addr: %#lx, s_addr: %#lx, size: %#x\n", + (unsigned long)addr + L3T_LOCK_START_L + + slot_idx * L3T_LOCK_STEP, + s_addr, size); +} + +static void l3t_unlock(void __iomem *addr, int slot_idx) +{ + struct l3t_lock_ctrl ctrl; + + if (!addr) { + pr_err("invalid unlock addr\n"); + return; + } + + writeq(0, addr + L3T_LOCK_START_L + slot_idx * L3T_LOCK_STEP); + writel(0, addr + L3T_LOCK_AREA + slot_idx * L3T_LOCK_STEP); + + ctrl.u32 = 0; + + ctrl.bits.unlock_en = 1; + ctrl.bits.lock_en = 0; + writel(ctrl.u32, addr + L3T_LOCK_CTRL + slot_idx * L3T_LOCK_STEP); + + do { + ctrl.u32 = + readl(addr + L3T_LOCK_CTRL + slot_idx * L3T_LOCK_STEP); + } while (ctrl.bits.unlock_done == 0); + + pr_debug("unlock success. addr: %#lx\n", + (unsigned long)addr + L3T_LOCK_START_L + + slot_idx * L3T_LOCK_STEP); +} + +void hisi_l3t_unlock(struct hisi_l3t *l3t, int slot_idx) +{ + if (slot_idx < 0 || slot_idx >= L3T_REG_NUM) { + pr_err("slot index is invalid: %d\n", slot_idx); + return; + } + + l3t_unlock(l3t->base, slot_idx); +} + +static void hisi_l3t_read_inner(void __iomem *addr, int locksel, + unsigned long *s_addr, int *size) +{ + if (!addr) { + *s_addr = 0; + *size = 0; + pr_err("invalid unlock addr\n"); + return; + } + + *s_addr = readq(addr + L3T_LOCK_START_L + locksel * L3T_LOCK_STEP); + *size = readl(addr + L3T_LOCK_AREA + locksel * L3T_LOCK_STEP); +} + +void hisi_l3t_read(struct hisi_l3t *l3t, int slot_idx, unsigned long *s_addr, + int *size) +{ + if (slot_idx < 0 || slot_idx >= L3T_REG_NUM) { + pr_err("slot index is invalid: %d\n", slot_idx); + return; + } + + return hisi_l3t_read_inner(l3t->base, slot_idx, s_addr, size); +} + +void hisi_l3t_lock(struct hisi_l3t *l3t, int slot_idx, unsigned long s_addr, + int size) +{ + if (!s_addr) { + pr_err("invalid start addr\n"); + return; + } + + if (slot_idx < 0 || slot_idx >= L3T_REG_NUM) { + pr_err("invalid slot index: %d\n", slot_idx); + return; + } + + l3t_lock(l3t->base, slot_idx, s_addr, size); +} + +struct hisi_sccl *hisi_l3t_get_sccl(int nid) +{ + return xa_load(&l3t_mapping, nid); +} + +static int __init hisi_l3t_init(void) +{ + int ret; + + mutex_init(&l3t_mutex); + xa_init(&l3t_mapping); + + ret = platform_driver_register(&hisi_l3t_driver); + if (ret) + return ret; + + return 0; +} +module_init(hisi_l3t_init); + +static void hisi_l3t_destroy_sccl(void) +{ + struct hisi_sccl *sccl; + unsigned long nid; + + xa_for_each(&l3t_mapping, nid, sccl) + kfree(sccl->l3t); +} + +static void __exit hisi_l3t_exit(void) +{ + hisi_l3t_destroy_sccl(); + xa_destroy(&l3t_mapping); + + platform_driver_unregister(&hisi_l3t_driver); +} +module_exit(hisi_l3t_exit); + +MODULE_DESCRIPTION("HiSilicon SoC L3T driver"); +MODULE_LICENSE("GPL v2"); +MODULE_AUTHOR("Ma Wupeng mawupeng1@huawei.com");