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 | 45 +++++ drivers/soc/hisilicon/hisi_lockdown.c | 140 ++++++++++++++ drivers/soc/hisilicon/l3t.c | 263 ++++++++++++++++++++++++++ 5 files changed, 459 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..422e8a4b04bc --- /dev/null +++ b/drivers/soc/hisilicon/hisi_l3t.h @@ -0,0 +1,45 @@ +// 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 device *dev; + void __iomem *base; + int sccl_id; + int ccl_id; + 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..344e9ddaa7cd --- /dev/null +++ b/drivers/soc/hisilicon/hisi_lockdown.c @@ -0,0 +1,140 @@ +// 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" + +struct hisi_l3t_ops { + bool (*l3t_slot_test)(struct hisi_l3t *l3t, int slot_idx, + unsigned long addr, int size); + void (*l3t_slot_action)(struct hisi_l3t *l3t, int slot_idx, + unsigned long addr, int size); +}; + +static int hisi_l3t_sccl_action(struct hisi_sccl *sccl, unsigned long addr, + int size, struct hisi_l3t_ops *ops) +{ + struct hisi_l3t *l3t; + int i, slot_idx; + + /* + * in shared mode, all l3t belongs to one sccl is the same. + * use the first l3t to test. + */ + l3t = sccl->l3t[0]; + + mutex_lock(&l3t_mutex); + for (slot_idx = 0; slot_idx < L3T_REG_NUM; slot_idx++) { + if (ops->l3t_slot_test(l3t, slot_idx, addr, size)) + break; + } + + if (slot_idx >= L3T_REG_NUM) { + mutex_unlock(&l3t_mutex); + return -EINVAL; + } + + for (i = 0; i < sccl->ccl_cnt; i++) { + l3t = sccl->l3t[i]; + if (l3t) + ops->l3t_slot_action(l3t, slot_idx, addr, size); + } + mutex_unlock(&l3t_mutex); + + return 0; +} + +struct hisi_sccl *get_valid_sccl(int nid) +{ + struct hisi_sccl *sccl; + + sccl = hisi_l3t_get_sccl(nid); + if (!sccl || !sccl->ccl_cnt) + return NULL; + + if (!sccl->l3t || !sccl->l3t[0]) + return NULL; + + return sccl; +} + +static bool hisi_l3t_test_empty(struct hisi_l3t *l3t, int slot_idx, + unsigned long __always_unused addr, + int __always_unused size) +{ + unsigned long _addr; + int _size; + + hisi_l3t_read(l3t, slot_idx, &_addr, &_size); + + return _addr == 0; +} + +int l3t_shared_lock(int nid, unsigned long pfn, unsigned long size) +{ + struct hisi_l3t_ops ops = { + .l3t_slot_test = hisi_l3t_test_empty, + .l3t_slot_action = hisi_l3t_lock, + }; + struct hisi_sccl *sccl; + int ret; + + sccl = get_valid_sccl(nid); + if (!sccl) + return -ENODEV; + + ret = hisi_l3t_sccl_action(sccl, pfn << PAGE_SHIFT, size, &ops); + if (ret) + return -ENOMEM; + + return 0; +} +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; + int _size; + + hisi_l3t_read(l3t, slot_idx, &_addr, &_size); + + return (_addr == addr && _size == size); +} + +static void hisi_l3t_do_unlock(struct hisi_l3t *l3t, int slot_idx, + unsigned long __always_unused addr, + int __always_unused size) +{ + hisi_l3t_unlock(l3t, slot_idx); +} + +int l3t_shared_unlock(int nid, unsigned long pfn, unsigned long size) +{ + struct hisi_l3t_ops ops = { + .l3t_slot_test = hisi_l3t_test_equal, + .l3t_slot_action = hisi_l3t_do_unlock, + }; + struct hisi_sccl *sccl; + int ret; + + sccl = get_valid_sccl(nid); + if (!sccl) + return -ENODEV; + + ret = hisi_l3t_sccl_action(sccl, pfn << PAGE_SHIFT, size, &ops); + if (ret) + return -EINVAL; + + return 0; +} +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..1af1646ffbf8 --- /dev/null +++ b/drivers/soc/hisilicon/l3t.c @@ -0,0 +1,263 @@ +// 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 LOCK_EN BIT(0) +#define LOCK_DONE BIT(1) +#define UNLOCK_EN BIT(2) +#define UNLOCK_DONE BIT(3) + +DEFINE_MUTEX(l3t_mutex); +static DEFINE_XARRAY(l3t_mapping); + +static int sccl_to_node_id(int id) +{ + int sccl_id, cpu; + u64 mpidr; + + 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); + if (l3t->nid < 0) + return -EINVAL; + + 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; +} + +/* write bit b_update and wait bit b_wait to be zero */ +static void __l3t_update_and_wait(void __iomem *addr, u32 b_update, u32 b_wait) +{ + u32 val; + + writel(b_update, addr); + + do { + val = readl(addr); + } while ((val & b_wait) == 0); +} + +static void __l3t_maintain(void __iomem *addr, int slot_idx, + unsigned long s_addr, int size, bool lock) +{ + if (slot_idx < 0 || slot_idx >= L3T_REG_NUM) { + pr_err("slot index is invalid: %d\n", slot_idx); + return; + } + + if (!addr) { + pr_err("invalid unlock addr\n"); + return; + } + + addr += slot_idx * L3T_LOCK_STEP; + + writeq(s_addr, addr + L3T_LOCK_START_L); + writel(size, addr + L3T_LOCK_AREA); + + if (lock) + __l3t_update_and_wait(addr + L3T_LOCK_CTRL, LOCK_EN, LOCK_DONE); + else + __l3t_update_and_wait(addr + L3T_LOCK_CTRL, UNLOCK_EN, + UNLOCK_DONE); +} + +void hisi_l3t_lock(struct hisi_l3t *l3t, int slot_idx, unsigned long s_addr, + int size) +{ + __l3t_maintain(l3t->base, slot_idx, s_addr, size, true); + + pr_debug("lock success. addr: %#lx, slot: %d, s_addr: %#lx, size: %#x\n", + (unsigned long)l3t->base, slot_idx, s_addr, size); +} + +void hisi_l3t_unlock(struct hisi_l3t *l3t, int slot_idx) +{ + __l3t_maintain(l3t->base, slot_idx, 0, 0, false); + + pr_debug("unlock success. addr: %#lx, slot: %d\n", + (unsigned long)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); +} + +struct hisi_sccl *hisi_l3t_get_sccl(int nid) +{ + return xa_load(&l3t_mapping, nid); +} + +static const struct acpi_device_id hisi_l3t_acpi_match[] = { + { "HISI0501", }, + {} +}; +MODULE_DEVICE_TABLE(acpi, hisi_l3t_acpi_match); + +static int hisi_l3t_probe(struct platform_device *pdev) +{ + struct hisi_l3t *l3t; + int ret; + + 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, +}; + +static int __init hisi_l3t_init(void) +{ + mutex_init(&l3t_mutex); + xa_init(&l3t_mapping); + + return platform_driver_register(&hisi_l3t_driver); +} +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");