From 8c0e1be985bd26a102d07a837101b121012ddea9 Mon Sep 17 00:00:00 2001 From: Junlong Zheng Date: Thu, 18 Sep 2025 21:41:32 +0800 Subject: [PATCH 01/45] ub:ubus: Support for ub bus driver framework driver inclusion category: feature bugzilla: https://gitee.com/openeuler/kernel/issues/ID34DG CVE: NA ----------------------------------------------------------- Supports the UB bus driver framework, providing callback interfaces for match/probe/remove/uevent/shutdown, and supports device driver registration and unregistration interfaces. Signed-off-by: Junlong Zheng --- arch/arm64/configs/openeuler_defconfig | 3 + arch/x86/configs/openeuler_defconfig | 3 + drivers/Kconfig | 1 + drivers/Makefile | 1 + drivers/ub/Kconfig | 17 ++ drivers/ub/Makefile | 3 + drivers/ub/ubus/Kconfig | 29 +++ drivers/ub/ubus/Makefile | 7 + drivers/ub/ubus/ub-driver.c | 43 ++++ drivers/ub/ubus/ubus.h | 13 ++ drivers/ub/ubus/ubus_driver.c | 269 ++++++++++++++++++++++++ drivers/ub/ubus/ubus_inner.h | 10 + include/linux/mod_devicetable.h | 22 ++ include/uapi/ub/ubus/ubus_regs.h | 279 +++++++++++++++++++++++++ include/ub/ubus/ubus.h | 172 +++++++++++++++ include/ub/ubus/ubus_ids.h | 49 +++++ scripts/mod/devicetable-offsets.c | 9 + scripts/mod/file2alias.c | 44 ++++ 18 files changed, 974 insertions(+) create mode 100644 drivers/ub/Kconfig create mode 100644 drivers/ub/Makefile create mode 100644 drivers/ub/ubus/Kconfig create mode 100644 drivers/ub/ubus/Makefile create mode 100644 drivers/ub/ubus/ub-driver.c create mode 100644 drivers/ub/ubus/ubus.h create mode 100644 drivers/ub/ubus/ubus_driver.c create mode 100644 drivers/ub/ubus/ubus_inner.h create mode 100644 include/uapi/ub/ubus/ubus_regs.h create mode 100644 include/ub/ubus/ubus.h create mode 100644 include/ub/ubus/ubus_ids.h diff --git a/arch/arm64/configs/openeuler_defconfig b/arch/arm64/configs/openeuler_defconfig index bb3c44921c0f..da294863186b 100644 --- a/arch/arm64/configs/openeuler_defconfig +++ b/arch/arm64/configs/openeuler_defconfig @@ -8320,5 +8320,8 @@ CONFIG_KWORKER_NUMA_AFFINITY=y # # unified bus # +CONFIG_UB=y CONFIG_UB_UBL=m +CONFIG_UB_UBUS=y +CONFIG_UB_UBUS_BUS=m # end of unified bus diff --git a/arch/x86/configs/openeuler_defconfig b/arch/x86/configs/openeuler_defconfig index 677cff9308a4..0a1384f4af52 100644 --- a/arch/x86/configs/openeuler_defconfig +++ b/arch/x86/configs/openeuler_defconfig @@ -9508,5 +9508,8 @@ CONFIG_KWORKER_NUMA_AFFINITY=y # # unified bus # +CONFIG_UB=n CONFIG_UB_UBL=n +CONFIG_UB_UBUS=n +CONFIG_UB_UBUS_BUS=n # end of unified bus diff --git a/drivers/Kconfig b/drivers/Kconfig index da6544d0c108..35521e3200fa 100644 --- a/drivers/Kconfig +++ b/drivers/Kconfig @@ -7,6 +7,7 @@ source "drivers/amba/Kconfig" source "drivers/eisa/Kconfig" source "drivers/pci/Kconfig" source "drivers/cxl/Kconfig" +source "drivers/ub/Kconfig" source "drivers/pcmcia/Kconfig" source "drivers/rapidio/Kconfig" diff --git a/drivers/Makefile b/drivers/Makefile index 9af19fcf784c..f8e58f0ca2d1 100644 --- a/drivers/Makefile +++ b/drivers/Makefile @@ -23,6 +23,7 @@ obj-$(CONFIG_GPIOLIB) += gpio/ obj-y += pwm/ obj-y += pci/ +obj-y += ub/ obj-$(CONFIG_PARISC) += parisc/ obj-$(CONFIG_RAPIDIO) += rapidio/ diff --git a/drivers/ub/Kconfig b/drivers/ub/Kconfig new file mode 100644 index 000000000000..fc591be43597 --- /dev/null +++ b/drivers/ub/Kconfig @@ -0,0 +1,17 @@ +# SPDX-License-Identifier: GPL-2.0+ +# +# UnifiedBus configuration +# + +menuconfig UB + bool "UB (UnifiedBus) support" + depends on ARM64 + default n + help + Support for UB. + If you have a hardware that support UB protocol, + Say y here. By Default this option is closed. + +if UB +source "drivers/ub/ubus/Kconfig" +endif # UB diff --git a/drivers/ub/Makefile b/drivers/ub/Makefile new file mode 100644 index 000000000000..10428ed28774 --- /dev/null +++ b/drivers/ub/Makefile @@ -0,0 +1,3 @@ +# SPDX-License-Identifier: GPL-2.0+ + +obj-y += ubus/ diff --git a/drivers/ub/ubus/Kconfig b/drivers/ub/ubus/Kconfig new file mode 100644 index 000000000000..059616926ff4 --- /dev/null +++ b/drivers/ub/ubus/Kconfig @@ -0,0 +1,29 @@ +# SPDX-License-Identifier: GPL-2.0+ +# +# UB bus driver configuration +# + +menuconfig UB_UBUS + default n + bool "UBUS support" + help + This option enables support for UB bus device management + functionality, providing fundamental capabilities such as + UB bus registration, device registration, and driver registration. + It configures and enables devices, offering device capabilities + to the upper layers. + Say 'Y' here unless you know what you are doing. + +if UB_UBUS + +config UB_UBUS_BUS + default n + tristate "UB Bus Driver Modular Section" + depends on UB_UBUS + help + This option enables the modular part of the UB bus driver, + which works in conjunction with the built-in part to implement + the UB bus device management functionality. + Say 'M' here unless you know what you are doing. + +endif diff --git a/drivers/ub/ubus/Makefile b/drivers/ub/ubus/Makefile new file mode 100644 index 000000000000..b3cb5016bf2e --- /dev/null +++ b/drivers/ub/ubus/Makefile @@ -0,0 +1,7 @@ +# SPDX-License-Identifier: GPL-2.0+ + +obj-$(CONFIG_UB_UBUS) += ub-driver.o + +ubus-y := ubus_driver.o + +obj-$(CONFIG_UB_UBUS_BUS) += ubus.o diff --git a/drivers/ub/ubus/ub-driver.c b/drivers/ub/ubus/ub-driver.c new file mode 100644 index 000000000000..516aec010e16 --- /dev/null +++ b/drivers/ub/ubus/ub-driver.c @@ -0,0 +1,43 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (c) HiSilicon Technologies Co., Ltd. 2025. All rights reserved. + */ + +#include + +#include "ubus_inner.h" + +struct bus_type ub_bus_type = { + .name = "ub", +}; +EXPORT_SYMBOL_GPL(ub_bus_type); + +int __ub_register_driver(struct ub_driver *drv, struct module *owner, + const char *mod_name) +{ + if (!drv) + return -EINVAL; + + drv->driver.name = drv->name; + drv->driver.bus = &ub_bus_type; + drv->driver.owner = owner; + drv->driver.mod_name = mod_name; + + return driver_register(&drv->driver); +} +EXPORT_SYMBOL_GPL(__ub_register_driver); + +void ub_unregister_driver(struct ub_driver *drv) +{ + if (!drv) + return; + + driver_unregister(&drv->driver); +} +EXPORT_SYMBOL_GPL(ub_unregister_driver); + +static int __init ub_driver_init(void) +{ + return bus_register(&ub_bus_type); +} +postcore_initcall(ub_driver_init); diff --git a/drivers/ub/ubus/ubus.h b/drivers/ub/ubus/ubus.h new file mode 100644 index 000000000000..b82922a63de4 --- /dev/null +++ b/drivers/ub/ubus/ubus.h @@ -0,0 +1,13 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Copyright (c) HiSilicon Technologies Co., Ltd. 2025. All rights reserved. + */ +#ifndef __UBUS_H__ +#define __UBUS_H__ + +#include + +int ub_host_probe(void); +void ub_host_remove(void); + +#endif /* __UBUS_H__ */ diff --git a/drivers/ub/ubus/ubus_driver.c b/drivers/ub/ubus/ubus_driver.c new file mode 100644 index 000000000000..28303556807a --- /dev/null +++ b/drivers/ub/ubus/ubus_driver.c @@ -0,0 +1,269 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * (C) Copyright 2002-2004, 2007 Greg Kroah-Hartman + * (C) Copyright 2007 Novell Inc. + * Copyright (c) HiSilicon Technologies Co., Ltd. 2025. All rights reserved. + * + * Thanks to Greg Kroah-Hartman and Novell Inc. for their previous work. + * + */ + +#define pr_fmt(fmt) "ubus driver: " fmt + +#include +#include + +#include "ubus.h" + +struct ub_entity *ub_entity_get(struct ub_entity *dev) +{ + if (dev) + get_device(&dev->dev); + return dev; +} +EXPORT_SYMBOL_GPL(ub_entity_get); + +void ub_entity_put(struct ub_entity *dev) +{ + if (dev) + put_device(&dev->dev); +} +EXPORT_SYMBOL_GPL(ub_entity_put); + +static const struct ub_device_id ub_entity_id_any = { + .vendor = (__u32)UB_ANY_ID, + .device = (__u32)UB_ANY_ID, + .mod_vendor = (__u32)UB_ANY_ID, + .module = (__u32)UB_ANY_ID, +}; + +static inline const struct ub_device_id * +ub_match_one_device(const struct ub_device_id *id, const struct ub_entity *dev) +{ + if ((id->vendor == UB_ANY_ID || id->vendor == uent_vendor(dev)) && + (id->device == UB_ANY_ID || id->device == uent_device(dev)) && + (id->mod_vendor == UB_ANY_ID || id->mod_vendor == dev->mod_vendor) && + (id->module == UB_ANY_ID || id->module == dev->module) && + !((id->class_code ^ uent_class(dev)) & id->class_mask)) + return id; + return NULL; +} + +const struct ub_device_id *ub_match_id(const struct ub_device_id *ids, + struct ub_entity *dev) +{ + if (ids && dev) { + while (ids->vendor || ids->mod_vendor || ids->class_mask) { + if (ub_match_one_device(ids, dev)) + return ids; + ids++; + } + } + return NULL; +} + +static const struct ub_device_id *ub_match_device(struct ub_driver *drv, + struct ub_entity *dev) +{ + const struct ub_device_id *found_id = NULL, *ids; + + /* When driver_override is set, only bind to the matching driver */ + if (dev->driver_override && strcmp(dev->driver_override, drv->name)) + return NULL; + + for (ids = drv->id_table; (found_id = ub_match_id(ids, dev)); + ids = found_id + 1) { + if (found_id->override_only) { + if (dev->driver_override) + return found_id; + } else { + return found_id; + } + } + + /* driver_override will always match, send a dummy id */ + if (dev->driver_override) + return &ub_entity_id_any; + + return NULL; +} + +static int ub_bus_match(struct device *dev, struct device_driver *drv) +{ + struct ub_driver *ub_drv = to_ub_driver(drv); + struct ub_entity *ub_entity = to_ub_entity(dev); + const struct ub_device_id *found_id; + + if (!ub_entity->match_driver) + return 0; + + found_id = ub_match_device(ub_drv, ub_entity); + if (found_id) + return 1; + + return 0; +} + +static int ub_call_probe(struct ub_driver *drv, struct ub_entity *dev, + const struct ub_device_id *id) +{ + int ret; + + dev->driver = drv; + /* + * Probe function should return < 0 for failure, 0 for success + * Treat values > 0 as success, but warn. + */ + ret = drv->probe(dev, id); + if (ret < 0) { + dev->driver = NULL; + return ret; + } else if (ret > 0) { + ub_warn(dev, "Driver probe function unexpectedly, ret=%d\n", + ret); + } + + return 0; +} + +static int __ub_entity_probe(struct ub_driver *drv, struct ub_entity *dev) +{ + const struct ub_device_id *id; + int ret = 0; + + if (drv->probe) { + ret = -ENODEV; + + id = ub_match_device(drv, dev); + if (id) + ret = ub_call_probe(drv, dev, id); + } + + return ret; +} + +static int ub_entity_probe(struct device *dev) +{ + struct ub_driver *drv = to_ub_driver(dev->driver); + struct ub_entity *ub_entity = to_ub_entity(dev); + int ret; + + ub_entity_get(ub_entity); + ret = __ub_entity_probe(drv, ub_entity); + if (ret) + ub_entity_put(ub_entity); + return ret; +} + +static void ub_entity_remove(struct device *dev) +{ + struct ub_entity *ub_entity = to_ub_entity(dev); + struct ub_driver *drv = ub_entity->driver; + + if (drv->remove) + drv->remove(ub_entity); + + ub_entity->driver = NULL; + + ub_entity_put(ub_entity); +} + +static void ub_entity_shutdown(struct device *dev) +{ + struct ub_entity *uent = to_ub_entity(dev); + struct ub_driver *drv = uent->driver; + + ub_dbg(uent, "come shutdown\n"); + + pm_runtime_resume(dev); + + if (drv && drv->shutdown) + drv->shutdown(uent); +} + +static int ub_uevent(const struct device *dev, struct kobj_uevent_env *env) +{ + struct ub_entity *uent; + + if (!dev) + return -ENODEV; + + uent = to_ub_entity(dev); + + if (add_uevent_var(env, "UB_ID=%04X:%04X", uent_vendor(uent), + uent_device(uent))) + return -ENOMEM; + + if (add_uevent_var(env, "UB_MODULE=%04X:%04X", uent->mod_vendor, + uent->module)) + return -ENOMEM; + + if (add_uevent_var(env, "UB_TYPE=%01X", uent_type(uent))) + return -ENOMEM; + + if (add_uevent_var(env, "UB_CLASS=%04X", uent_class(uent))) + return -ENOMEM; + + if (add_uevent_var(env, "UB_VERSION=%01X", uent_version(uent))) + return -ENOMEM; + + if (add_uevent_var(env, "UB_SEQ_NUM=%016llX", uent_seq(uent))) + return -ENOMEM; + + if (add_uevent_var(env, "UB_ENTITY_NAME=%s", ub_name(uent))) + return -ENOMEM; + + if (add_uevent_var(env, "MODALIAS=ub:v%04Xd%04Xmv%04Xm%04Xc%04X", + uent_vendor(uent), uent_device(uent), + uent->mod_vendor, uent->module, uent_class(uent))) + return -ENOMEM; + + return 0; +} + +void ub_bus_type_init(void) +{ + ub_bus_type.match = ub_bus_match; + ub_bus_type.uevent = ub_uevent; + ub_bus_type.probe = ub_entity_probe; + ub_bus_type.remove = ub_entity_remove; + ub_bus_type.shutdown = ub_entity_shutdown; +} + +void ub_bus_type_uninit(void) +{ + ub_bus_type.match = NULL; + ub_bus_type.uevent = NULL; + ub_bus_type.probe = NULL; + ub_bus_type.remove = NULL; + ub_bus_type.shutdown = NULL; +} + +int ub_host_probe(void) +{ + ub_bus_type_init(); + return 0; +} +EXPORT_SYMBOL_GPL(ub_host_probe); + +void ub_host_remove(void) +{ + ub_bus_type_uninit(); +} +EXPORT_SYMBOL_GPL(ub_host_remove); + +static int __init ubus_driver_init(void) +{ + pr_info("Ubus driver init successfully.\n"); + return 0; +} + +static void __exit ubus_driver_exit(void) +{ + pr_info("Ubus driver exit successfully.\n"); +} +module_init(ubus_driver_init); +module_exit(ubus_driver_exit); +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("UB bus driver"); +MODULE_IMPORT_NS(UB_UBUS); diff --git a/drivers/ub/ubus/ubus_inner.h b/drivers/ub/ubus/ubus_inner.h new file mode 100644 index 000000000000..813cdcf70b16 --- /dev/null +++ b/drivers/ub/ubus/ubus_inner.h @@ -0,0 +1,10 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Copyright (c) HiSilicon Technologies Co., Ltd. 2025. All rights reserved. + */ +#ifndef __UBUS_INNER_H__ +#define __UBUS_INNER_H__ + +#include + +#endif /* __UBUS_INNER_H__ */ diff --git a/include/linux/mod_devicetable.h b/include/linux/mod_devicetable.h index 0f51bc24ae59..de678c6b4348 100644 --- a/include/linux/mod_devicetable.h +++ b/include/linux/mod_devicetable.h @@ -948,4 +948,26 @@ struct cdx_device_id { __u32 override_only; }; +#define UB_ANY_ID (~0) + +/** + * struct ub_device_id - UB device identifier + * @vendor: Vendor ID + * @device: Device ID + * @mod_vendor: Module Vendor ID + * @module: Module ID + * @class_code: Device class base code and sub code to match. See + * include/ub/ubus/ubus_ids.h for a full list of classes. + * @class_mask: Limit which sub-fields of the class code field are + * compared. + * @override_only: Match only when dev->driver_override is this driver. + */ +struct ub_device_id { + __u32 vendor, device; /* Vendor and device ID or UB_ANY_ID*/ + __u32 mod_vendor, module; /* Module ID's or UB_ANY_ID */ + __u16 class_code, class_mask; /* Base code and sub code */ + unsigned long driver_data; /* Data private to the driver */ + __u32 override_only; +}; + #endif /* LINUX_MOD_DEVICETABLE_H */ diff --git a/include/uapi/ub/ubus/ubus_regs.h b/include/uapi/ub/ubus/ubus_regs.h new file mode 100644 index 000000000000..9eed901fd205 --- /dev/null +++ b/include/uapi/ub/ubus/ubus_regs.h @@ -0,0 +1,279 @@ +/* SPDX-License-Identifier: GPL-2.0+ WITH Linux-syscall-note */ +/* + * Copyright (c) HiSilicon Technologies Co., Ltd. 2025. All rights reserved. + */ + +#ifndef _UAPI_UB_UBUS_UBUS_REGS_H_ +#define _UAPI_UB_UBUS_UBUS_REGS_H_ + +enum ub_cfg_cap_id { + UB_CFG0_BASIC_CAP = 0x000, + UB_SHP_CAP = 0x002, + UB_ERR_RECORD_CAP = 0x003, + UB_ERR_INFO_CAP = 0x004, + UB_EMQ_CAP = 0x005, + UB_CFG1_BASIC_CAP = 0x100, + UB_DECODER_CAP = 0x101, + UB_JETTY_CAP = 0x102, + UB_INT_TYPE1_CAP = 0x103, + UB_INT_TYPE2_CAP = 0x104, + UB_MEM_CAP = 0x106, +}; + +enum ub_port_cap_id { + UB_PORT_CAP15_QDLWS = 15, +}; + +#define UB_SLICE_SZ 0x00000400 +#define UB_CFG_BITMAP_SIZE 32 +#define UB_ROUTE_TABLE_SZ 0x40000000 +#define UB_CFG0_SUPPORT_FEATURE_SIZE 16 +#define UB_GUID_SIZE 16 +#define UB_EID_SIZE 16 +#define UB_FM_EID_SIZE 16 +#define UB_CFG1_SUPPORT_FEATURE_SIZE 16 +#define UB_PORT_SLICE_START 0x00080000 +#define UB_ROUTE_TABLE_SLICE_START 0x3c0000000 +#define UB_CFG_SAPCE_SLICE_END 0x400000000 +#define UB_ADDR_TO_POS(a) ((a) << 2) +#define UB_PORT_SLICE_SIZE UB_ADDR_TO_POS(0x10000) + +/* CFG0 BASIC */ +#define UB_CFG0_BASIC_SLICE 0x0 /* 0x0 */ +#define UB_TOTAL_NUMBER_PORT 0x4 /* 0x1 */ +#define UB_TOTAL_NUMBER_ENTITIES 0x6 +#define UB_CFG0_CAP_BITMAP 0x8 /* 0x2 */ +#define UB_FEATURE_SUPPORT_0 0x28 /* 0xA */ +#define UB_SW_SUPPORT BIT(7) +#define UB_CC_SUPPORT BIT(9) +#define UB_GUID 0x38 /* 0xE */ +#define UB_EID_0 0x48 /* 0x12 */ +#define UB_EID_3 0x54 /* 0x15 */ +#define UB_FM_EID_0 0x58 /* 0x16 */ +#define UB_PRIMARY_CNA 0x68 /* 0x1A */ +#define UB_PRIMARY_CNA_MASK GENMASK(23, 0) +#define UB_UPI 0x7c /* 0x1f */ +#define UB_MODULE 0x80 /* 0x20 */ +#define UB_MODULE_ID_MASK GENMASK(15, 0) +#define UB_ENTITY_RST 0x84 /* 0x21 */ +#define UB_ENTITY_RST_BIT 0x1 +#define UB_MTU_CFG 0x8c /* 0x23 */ +#define UB_MTU_CFG_BITS 0x7 +#define UB_CC_EN 0x90 /* 0x24 */ +#define UB_CC_EN_BIT 0x1 +#define UB_TH_EN 0x94 /* 0x25 */ +#define UB_TH_EN_BIT 0x1 +#define UB_FM_CNA 0x98 /* 0x26 */ +#define UB_FM_CNA_MASK GENMASK(23, 0) +#define UB_UEID_0 0x9c /* 0x27 */ +#define UB_UEID_1 0xa0 /* 0x28 */ +#define UB_UEID_2 0xa4 /* 0x29 */ +#define UB_UEID_3 0xa8 /* 0x2A */ +#define UB_UCNA 0xac /* 0x2B */ +#define UB_UCNA_MASK GENMASK(23, 0) + +/* SHP CAP */ +#define UB_SLOT_START UB_ADDR_TO_POS(0x200) +#define UB_SLOT_POS UB_ADDR_TO_POS(0x10) +#define UB_SLOT_NUM UB_ADDR_TO_POS(0x1) +#define UB_SLOT_CAP UB_ADDR_TO_POS(0x2) +#define UB_SLOT_PPS 0x1 +#define UB_SLOT_WLPS 0x2 +#define UB_SLOT_PLPS 0x4 +#define UB_SLOT_PDSS 0x8 +#define UB_SLOT_PORT UB_ADDR_TO_POS(0x3) +#define UB_SLOT_START_PORT 0x0000ffff +#define UB_SLOT_PP_CTRL UB_ADDR_TO_POS(0x4) +#define UB_SLOT_PP_CTRL_MASK BIT(0) +#define UB_SLOT_WL_CTRL UB_ADDR_TO_POS(0x5) +#define UB_SLOT_WL_CTRL_MASK GENMASK(1, 0) +#define UB_SLOT_PL_CTRL UB_ADDR_TO_POS(0x6) +#define UB_SLOT_PL_CTRL_MASK GENMASK(1, 0) +#define UB_SLOT_MS_CTRL UB_ADDR_TO_POS(0x7) +#define UB_SLOT_MS_CTRL_MASK BIT(0) +#define UB_SLOT_PD_CTRL UB_ADDR_TO_POS(0x8) +#define UB_SLOT_PD_CTRL_MASK BIT(0) +#define UB_SLOT_PDS_CTRL UB_ADDR_TO_POS(0x9) +#define UB_SLOT_PDS_CTRL_MASK BIT(0) +#define UB_SLOT_PW_CTRL UB_ADDR_TO_POS(0xA) +#define UB_SLOT_PP_STA UB_ADDR_TO_POS(0xB) +#define UB_SLOT_PP_STA_MASK BIT(0) +#define UB_SLOT_PD_STA UB_ADDR_TO_POS(0xC) +#define UB_SLOT_PD_STA_MASK BIT(0) +#define UB_SLOT_PDSC_STA UB_ADDR_TO_POS(0xD) +#define UB_SLOT_PDSC_STA_MASK BIT(0) + +/* CFG1 BASIC */ +#define UB_CFG1_BASIC 0x40000 +#define UB_CFG1_BASIC_SLICE (0x0 + UB_CFG1_BASIC) /* 0x0 */ +#define UB_CFG1_CAP_BITMAP (0x4 + UB_CFG1_BASIC) /* 0x1 */ +#define UB_DECODER_CAP_BIT 0x2 +#define UB_JETTY_CAP_BIT 0x4 +#define UB_INT_TYPE1_CAP_BIT 0x8 +#define UB_INT_TYPE2_CAP_BIT 0x10 +#define UB_MEM_CAP_BIT 0x40 +#define UB_CFG1_SUPPORT_FEATURE_L (0x24 + UB_CFG1_BASIC) /* 0x9 */ +#define UB_VDBS_SUPPORT 0x1 +#define UB_VDSS_SUPPORT 0x2 +#define UB_MGS_SUPPORT 0x4 +#define UB_UBBAS_SUPPORT 0x20 +#define UB_ERS0S_SUPPORT 0x40 +#define UB_ERS1S_SUPPORT 0x80 +#define UB_ERS2S_SUPPORT 0x100 +#define UB_CDMAS_SUPPORT 0x200 +#define UB_DECODER_JURIS 0x400 +#define UB_ERS0_SS (0x34 + UB_CFG1_BASIC) /* 0xD */ +#define UB_ERS1_SS (0x38 + UB_CFG1_BASIC) /* 0xE */ +#define UB_ERS2_SS (0x3c + UB_CFG1_BASIC) /* 0xF */ +#define UB_ERS0_SA_L (0x40 + UB_CFG1_BASIC) /* 0x10 */ +#define UB_ERS0_SA_H (0x44 + UB_CFG1_BASIC) /* 0x11 */ +#define UB_ERS1_SA_L (0x48 + UB_CFG1_BASIC) /* 0x12 */ +#define UB_ERS1_SA_H (0x4c + UB_CFG1_BASIC) /* 0x13 */ +#define UB_ERS2_SA_L (0x50 + UB_CFG1_BASIC) /* 0x14 */ +#define UB_ERS2_SA_H (0x54 + UB_CFG1_BASIC) /* 0x15 */ +#define UB_ERS0_UBBA_L (0x58 + UB_CFG1_BASIC) /* 0x16 */ +#define UB_ERS0_UBBA_H (0x5c + UB_CFG1_BASIC) /* 0x17 */ +#define UB_ERS1_UBBA_L (0x60 + UB_CFG1_BASIC) /* 0x18 */ +#define UB_ERS1_UBBA_H (0x64 + UB_CFG1_BASIC) /* 0x19 */ +#define UB_ERS2_UBBA_L (0x68 + UB_CFG1_BASIC) /* 0x1A */ +#define UB_ERS2_UBBA_H (0x6c + UB_CFG1_BASIC) /* 0x1B */ +#define UB_ELR (0x70 + UB_CFG1_BASIC) /* 0x1C */ +#define UB_ELR_BIT 0x1 +#define UB_ELR_DONE (0x74 + UB_CFG1_BASIC) /* 0x1D */ +#define UB_ELR_DONE_BIT 0x1 +#define UB_SYS_PGS (0x84 + UB_CFG1_BASIC) /* 0x21 */ +#define UB_SYS_PGS_SIZE 0x1 +#define UB_EU_TBA_L (0x88 + UB_CFG1_BASIC) /* 0x22 */ +#define UB_EU_TBA_H (0x8c + UB_CFG1_BASIC) /* 0x23 */ +#define UB_EU_TEN (0x90 + UB_CFG1_BASIC) /* 0x24 */ +#define UB_CLASS_CODE (0xa4 + UB_CFG1_BASIC) /* 0x29 */ +#define UB_BASE_CODE_MASK GENMASK(7, 0) +#define UB_SUB_CODE_MASK GENMASK(15, 8) +#define UB_ENTITY_TOKEN_ID (0xb4 + UB_CFG1_BASIC) /* 0x2D */ +#define UB_TOKEN_ID_MASK GENMASK(19, 0) +#define UB_BUS_ACCESS_EN (0xb8 + UB_CFG1_BASIC) /* 0x2E */ +#define UB_BUS_ACCESS_EN_BIT 0x1 +#define UB_ENTITY_RS_ACCESS_EN (0xbc + UB_CFG1_BASIC) /* 0x2F */ +#define UB_ENTITY_RS_ACCESS_EN_BIT 0x1 + +/* DECODER CAP */ +#define DECODER_CAP_START 0x00040400 +#define DECODER_POS(pos) (DECODER_CAP_START + ((pos) << 2)) +#define DECODER_CAP DECODER_POS(0x1) +#define DECODER_CTRL DECODER_POS(0x2) +#define DECODER_MATT_BA0 DECODER_POS(0x3) +#define DECODER_MATT_BA1 DECODER_POS(0x4) +#define DECODER_MMIO_BA0 DECODER_POS(0x5) +#define DECODER_MMIO_BA1 DECODER_POS(0x6) +#define DECODER_USI_IDX DECODER_POS(0x7) +#define DECODER_CMDQ_CFG DECODER_POS(0x10) +#define DECODER_CMDQ_PROD DECODER_POS(0x11) +#define DECODER_CMDQ_CONS DECODER_POS(0x12) +#define DECODER_CMDQ_BASE_ADDR0 DECODER_POS(0x13) +#define DECODER_CMDQ_BASE_ADDR1 DECODER_POS(0x14) +#define DECODER_EVENTQ_CFG DECODER_POS(0x20) +#define DECODER_EVENTQ_PROD DECODER_POS(0x21) +#define DECODER_EVENTQ_CONS DECODER_POS(0x22) +#define DECODER_EVENTQ_BASE_ADDR0 DECODER_POS(0x23) +#define DECODER_EVENTQ_BASE_ADDR1 DECODER_POS(0x24) + +/* INT_TYPE1_CAP */ +#define UB_CFG1_INT_TYPE1_CAP 0x40c00 +#define UB_INT_TYPE1_ENABLE (0x4 + UB_CFG1_INT_TYPE1_CAP) /* 0x1 */ +#define UB_INT_TYPE1_SUP_INT_NUM (0x8 + UB_CFG1_INT_TYPE1_CAP) /* 0x2 */ +#define UB_INT_TYPE1_EN_INT_NUM (0xC + UB_CFG1_INT_TYPE1_CAP) /* 0x3 */ +#define UB_INT_TYPE1_INT_DATA (0x10 + UB_CFG1_INT_TYPE1_CAP) /* 0x4 */ +#define UB_INT_TYPE1_INT_ADDR_L (0x14 + UB_CFG1_INT_TYPE1_CAP) /* 0x5 */ +#define UB_INT_TYPE1_INT_ADDR_H (0x18 + UB_CFG1_INT_TYPE1_CAP) /* 0x6 */ +#define UB_INT_TYPE1_INT_ID (0x1c + UB_CFG1_INT_TYPE1_CAP) /* 0x7 */ +#define UB_INT_TYPE1_INT_MASK (0x20 + UB_CFG1_INT_TYPE1_CAP) /* 0x8 */ +#define UB_INT_TYPE1_INT_PENDING (0x24 + UB_CFG1_INT_TYPE1_CAP) /* 0x9 */ + +/* INT_TYPE2_CAP */ +#define UB_CFG1_CAP_INT_CAP 0x41000 +#define UB_INT_CAP_SLICE (0x0 + UB_CFG1_CAP_INT_CAP) /* 0x0 */ +#define UB_INT_INFO (0x4 + UB_CFG1_CAP_INT_CAP) /* 0x1 */ +#define UB_NUM_OF_INTR_VECTOR_TBL (0x4 + UB_CFG1_CAP_INT_CAP) +#define UB_NUM_OF_INTR_ADDR_TBL (0x6 + UB_CFG1_CAP_INT_CAP) +#define UB_INT_VECTOR_TBL_SA_L (0x8 + UB_CFG1_CAP_INT_CAP) /* 0x2 */ +#define UB_INT_VECTOR_TBL_SA_H (0xc + UB_CFG1_CAP_INT_CAP) /* 0x3 */ +#define UB_INT_ADDR_TBL_SA_L (0x10 + UB_CFG1_CAP_INT_CAP) /* 0x4 */ +#define UB_INT_ADDR_TBL_SA_H (0x14 + UB_CFG1_CAP_INT_CAP) /* 0x5 */ +#define UB_INT_PENDING_TBL_SA_L (0x18 + UB_CFG1_CAP_INT_CAP) /* 0x6 */ +#define UB_INT_PENDING_TBL_SA_H (0x1c + UB_CFG1_CAP_INT_CAP) /* 0x7 */ +#define UB_INT_ID (0x20 + UB_CFG1_CAP_INT_CAP) /* 0x8 */ +#define UB_FUN_EN_BIT 0 +#define UB_FUN_MASK_BIT 1 +#define UB_INT_MASK (0x24 + UB_CFG1_CAP_INT_CAP) /* 0x9 */ +#define UB_INT_MASK_BIT 0x1 +#define UB_INT_EN (0x28 + UB_CFG1_CAP_INT_CAP) /* 0xA */ +#define UB_INT_EN_BIT 0x1 + +/* UB_MEM_CAP */ +#define UB_CFG1_CAP_UB_MEM 0x41800 +#define UB_MEM_USI_IDX (0x4 + UB_CFG1_CAP_UB_MEM) + +/* ub interrupt vector entry format */ +#define UB_INTR_VECTOR_ENTRY_SIZE 0x8 +#define UB_INTR_VECTOR_ID 0x0 +#define UB_INTR_VECTOR_ADDR_INDEX 0x4 +#define UB_INTR_VECTOR_ADDR_INDEX_MASK GENMASK(15, 0) +#define UB_INTR_VECTOR_MASK_MASK BIT(16) +/* ub interrupt addr entry format */ +#define UB_INTR_ADDR_ENTRY_SIZE 0x20 +#define UB_INTR_ADDR_ADDR_L 0x0 +#define UB_INTR_ADDR_ADDR_H 0x4 +#define UB_INTR_ADDR_TOKENID 0x8 +#define UB_INTR_ADDR_TOKENID_MASK GENMASK(19, 0) +#define UB_INTR_ADDR_VALID_BIT 20 +#define UB_INTR_ADDR_VALID_MASK BIT(20) +#define UB_INTR_ADDR_DSTEID_0 0xc +#define UB_INTR_ADDR_DSTEID_1 0x10 +#define UB_INTR_ADDR_DSTEID_2 0x14 +#define UB_INTR_ADDR_DSTEID_3 0x18 + +/* PORT BASIC */ +#define UB_CFG0_PORT_SLICE 0x0 +#define UB_CFG0_PORT_BITMAP 0x4 /* 0x1 */ +#define UB_PORT_INFO 0x24 /* 0x9 */ +#define UB_PORT_TYPE 0x26 /* 0x9 */ +#define UB_PORT_TYPE_MASK BIT(0) +#define UB_NEIGHBOR_PORT_INFO 0x28 /* 0xA */ +#define UB_UB_NEIGHBOR_PORT_INFO_SIZE 0x14 +#define UB_PORT_NET_ADDR 0x3c /* 0xF */ +#define UB_PORT_NET_ADDR_MASK GENMASK(23, 0) +#define UB_PORT_RST 0x40 /* 0x10 */ +#define UB_PORT_RST_BIT 0x1 + +/* PORT CAP1 LINK */ +/* Physical Port Link Status */ +#define UB_PORT_PHYSICAL_PORT_LINK_STATUS (0x1c0 << 2) +#define UB_PORT_LINK_STATE 0x1 + +/* PORT CAP15 QDLWS */ +#define PORT_CAP15_QDLWS_CAP (0xf01 << 2) +#define PORT_CAP15_QDLWS_CTRL (0xf02 << 2) +#define PORT_CAP15_QDLWS_STATE (0xf03 << 2) +#define ASY_LINK_WIDTH_MASK BIT(0) +#define GLB_QDLWS_MASK BIT(0) +#define GLB_QDLWS_ENABLE_MASK BIT(0) +#define GLB_QDLWS_DISABLE_MASK GENMASK(31, 1) +#define TX_QDLWS_MASK BIT(1) +#define TX_QDLWS_ENABLE_MASK BIT(1) +#define TX_QDLWS_DISABLE_MASK 0xFFFFFFFD +#define RX_QDLWS_MASK BIT(2) +#define RX_QDLWS_ENABLE_MASK BIT(2) +#define RX_QDLWS_DISABLE_MASK 0xFFFFFFFB +#define TX_WIDTH_MASK GENMASK(21, 16) +#define TX_WIDTH_OFFSET 16 +#define RX_WIDTH_MASK GENMASK(29, 24) +#define RX_WIDTH_OFFSET 24 +#define QDLWS_EXEC_STATUS_MASK GENMASK(2, 0) +#define QDLWS_EXEC_STATUS_MAX 4 + +/* Error Message Queue Capability */ +#define EMQ_CAP_START 0x00001400 +#define UB_CAP_ERR_MSG_QUE_CTL 0x8 +#define UB_CAP_INTERRUPT_GEN_ENA 0x100 + +#endif /* _UAPI_UB_UBUS_UBUS_REGS_H_ */ diff --git a/include/ub/ubus/ubus.h b/include/ub/ubus/ubus.h new file mode 100644 index 000000000000..68cbc445a853 --- /dev/null +++ b/include/ub/ubus/ubus.h @@ -0,0 +1,172 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Copyright (c) HiSilicon Technologies Co., Ltd. 2025. All rights reserved. + */ + +#ifndef _UB_UBUS_UBUS_H_ +#define _UB_UBUS_UBUS_H_ + +#include +#include +#include +#include +#include +#include +#include + +#define UB_ENTITY(v, d) \ + .vendor = (v), .device = (d), \ + .mod_vendor = (u32)UB_ANY_ID, .module = (u32)UB_ANY_ID + +#define UB_ENTITY_MODULE(v, d, m_v, m) \ + .vendor = (v), .device = (d), \ + .mod_vendor = (m_v), .module = (m) + +#define UB_ENTITY_CLASS(dev_class, dev_class_mask) \ + .vendor = (u32)UB_ANY_ID, .device = (u32)UB_ANY_ID, \ + .mod_vendor = (u32)UB_ANY_ID, .module = (u32)UB_ANY_ID, \ + .class_code = (dev_class), .class_mask = (dev_class_mask) + +#define UB_GUID_DW_NUM SZ_4 + +struct ub_guid { + union { + struct { + unsigned long long seq_num; + unsigned int reserved : 24; + unsigned int type : 4; + unsigned int version : 4; + unsigned int device : 16; + unsigned int vendor : 16; + } bits; + + guid_t id; + + unsigned int dw[UB_GUID_DW_NUM]; + }; +}; + +#define uent_vendor(uent) ((uent)->guid.bits.vendor) +#define uent_type(uent) ((uent)->guid.bits.type) +#define uent_version(uent) ((uent)->guid.bits.version) +#define uent_device(uent) ((uent)->guid.bits.device) +#define uent_class(uent) ((uent)->class_code) +#define uent_base_code(uent) ((uent)->class_code & UB_BASE_CODE_MASK) +#define uent_seq(uent) ((uent)->guid.bits.seq_num) + +struct ub_entity { + /* Driver framework base info */ + struct device dev; + struct ub_driver *driver; + bool match_driver; /* Skip attaching driver before dev ready */ + const char *driver_override; /* Driver name to force a match */ + + /* entity base info */ + struct ub_guid guid; + u16 class_code; + u16 mod_vendor; /* entity's module vendor and module id */ + u16 module; +}; + +#define to_ub_entity(n) container_of(n, struct ub_entity, dev) + +/** + * struct ub_driver - UB driver structure + * @node: List of driver structures. + * @name: Driver name. + * @id_table: Pointer to table of device IDs the driver is + * interested in. Most drivers should export this + * table using MODULE_DEVICE_TABLE(ub,...). + * @probe: This probing function gets called (during execution + * of ub_register_driver() for already existing + * entities or later if a new entity gets inserted) for + * all UB entities which match the ID table and are not + * "owned" by the other drivers yet. This function gets + * passed a "struct ub_entity *" for each entity whose + * entry in the ID table matches the entity. The probe + * function returns zero when the driver chooses to + * take "ownership" of the entity or an error code + * (negative number) otherwise. + * The probe function always gets called from process + * context, so it can sleep. + * @remove: The remove() function gets called whenever an entity + * being handled by this driver is removed (either during + * deregistration of the driver or when it's manually + * removed from a hot-pluggable slot). + * The remove function always gets called from process + * context, so it can sleep. + * @shutdown: Hook into reboot_notifier_list (kernel/sys.c). + * Intended to stop any idling operations. + * @driver: Driver model structure. + */ +struct ub_driver { + struct list_head node; + const char *name; + const struct ub_device_id *id_table; /* Must be non-NULL for probe to be called */ + /* New entity inserted */ + int (*probe)(struct ub_entity *uent, const struct ub_device_id *id); + /* entity removed (NULL if not a hot-plug capable driver) */ + void (*remove)(struct ub_entity *uent); + void (*shutdown)(struct ub_entity *uent); + struct device_driver driver; +}; + +static inline struct ub_driver *to_ub_driver(struct device_driver *drv) +{ + return drv ? container_of(drv, struct ub_driver, driver) : NULL; +} + +#define ub_err(pue, fmt, arg...) dev_err(&(pue)->dev, fmt, ##arg) +#define ub_info(pue, fmt, arg...) dev_info(&(pue)->dev, fmt, ##arg) +#define ub_warn(pue, fmt, arg...) dev_warn(&(pue)->dev, fmt, ##arg) +#define ub_dbg(pue, fmt, arg...) dev_dbg(&(pue)->dev, fmt, ##arg) + +static inline const char *ub_name(const struct ub_entity *pue) +{ + return dev_name(&pue->dev); +} + +#ifdef CONFIG_UB_UBUS +extern struct bus_type ub_bus_type; +#define dev_is_ub(d) ((d)->bus == &ub_bus_type) + +/** + * ub_entity_get() - Atomically increment the reference count for the entity. + * @uent: UB entity pointer. + * + * Context: Any context. + * Return: uent, or NULL if @uent is NULL. + */ +struct ub_entity *ub_entity_get(struct ub_entity *uent); + +/** + * ub_entity_put() - decrement the reference count for the entity. + * @uent: UB entity pointer. + * + * Context: Any context. + */ +void ub_entity_put(struct ub_entity *uent); + +/* Proper probing supporting hot-pluggable entities */ +int __ub_register_driver(struct ub_driver *drv, struct module *owner, + const char *mod_name); +/* ub_register_driver() must be a macro so KBUILD_MODNAME can be expanded */ +#define ub_register_driver(driver) \ + __ub_register_driver(driver, THIS_MODULE, KBUILD_MODNAME) +void ub_unregister_driver(struct ub_driver *drv); + +#else /* CONFIG_UB_UBUS is not enabled */ +#define dev_is_ub(d) (false) +static inline struct ub_entity *ub_entity_get(struct ub_entity *uent) +{ return NULL; } +static inline void ub_entity_put(struct ub_entity *uent) {} +static inline int +__ub_register_driver(struct ub_driver *drv, struct module *owner, + const char *mod_name) +{ return 0; } +static inline int ub_register_driver(struct ub_driver *drv) +{ return 0; } +static inline void ub_unregister_driver(struct ub_driver *drv) {} +#endif /* CONFIG_UB_UBUS */ + +#endif /* _UB_UBUS_UBUS_H_ */ diff --git a/include/ub/ubus/ubus_ids.h b/include/ub/ubus/ubus_ids.h new file mode 100644 index 000000000000..5e5158ba5527 --- /dev/null +++ b/include/ub/ubus/ubus_ids.h @@ -0,0 +1,49 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Copyright (c) HiSilicon Technologies Co., Ltd. 2025. All rights reserved. + */ +#ifndef _UB_UBUS_UBUS_IDS_H_ +#define _UB_UBUS_UBUS_IDS_H_ + +/* + * Device Class base code and sub code + * +----------+-----------+ + * | sub code | base code | + * | [15:8] | [7:0] | + * +----------+-----------+ + */ + +#define UB_BASE_CODE_BUS_CONTROLLER 0x00 +#define UB_CLASS_BUS_CONTROLLER 0x0000 + +#define UB_BASE_CODE_STORAGE 0x01 +#define UB_CLASS_STORAGE_LPC 0x0001 +#define UB_CLASS_STORAGE_LBC 0x0101 +#define UB_CLASS_STORAGE_RAID 0x0201 + +#define UB_BASE_CODE_NETWORK 0x02 +#define UB_CLASS_NETWORK_UB 0x0002 +#define UB_CLASS_NETWORK_ETH 0x0102 + +#define UB_BASE_CODE_DISPLAY 0x03 + +#define UB_BASE_CODE_SWITCH 0x04 +#define UB_CLASS_SWITCH_UB 0x0004 + +#define UB_BASE_CODE_VIRTIO 0x05 +#define UB_CLASS_LEGACY_VIRTIO_NETWORK 0x0005 +#define UB_CLASS_LEGACY_VIRTIO_BLOCK 0x0105 +#define UB_CLASS_LEGACY_VIRTIO_SCSI 0x0205 +#define UB_CLASS_LEGACY_VIRTIO_GRAPHIC 0x0305 +#define UB_CLASS_LEGACY_VIRTIO_SOCKET 0x0405 +#define UB_CLASS_LEGACY_VIRTIO_FS 0x0505 + +#define UB_BASE_CODE_VIRTUAL 0x06 + +#define UB_BASE_CODE_NPU 0x07 +#define UB_CLASS_NPU_UB 0x0007 + +#define UB_BASE_CODE_UNKNOWN 0xFF +#define UB_CLASS_UNKNOWN 0x00FF + +#endif /* _UB_UBUS_UBUS_IDS_H_ */ diff --git a/scripts/mod/devicetable-offsets.c b/scripts/mod/devicetable-offsets.c index abe65f8968dd..3bf2df76f644 100644 --- a/scripts/mod/devicetable-offsets.c +++ b/scripts/mod/devicetable-offsets.c @@ -267,5 +267,14 @@ int main(void) DEVID_FIELD(cdx_device_id, device); DEVID_FIELD(cdx_device_id, override_only); + DEVID(ub_device_id); + DEVID_FIELD(ub_device_id, vendor); + DEVID_FIELD(ub_device_id, device); + DEVID_FIELD(ub_device_id, mod_vendor); + DEVID_FIELD(ub_device_id, module); + DEVID_FIELD(ub_device_id, class_code); + DEVID_FIELD(ub_device_id, class_mask); + DEVID_FIELD(ub_device_id, override_only); + return 0; } diff --git a/scripts/mod/file2alias.c b/scripts/mod/file2alias.c index ea498eff1f2a..84b6cad4693c 100644 --- a/scripts/mod/file2alias.c +++ b/scripts/mod/file2alias.c @@ -1475,6 +1475,49 @@ static int do_cdx_entry(const char *filename, void *symval, return 1; } +/* Looks like: ub:vNdNmvNmNcN. */ +static int do_ub_entry(const char *filename, void *symval, char *alias) +{ + /* Class code field can be divided into these two. */ + unsigned char basecode_mask, subcode_mask; + + DEF_FIELD(symval, ub_device_id, vendor); + DEF_FIELD(symval, ub_device_id, device); + DEF_FIELD(symval, ub_device_id, mod_vendor); + DEF_FIELD(symval, ub_device_id, module); + DEF_FIELD(symval, ub_device_id, class_code); + DEF_FIELD(symval, ub_device_id, class_mask); + DEF_FIELD(symval, ub_device_id, override_only); + + switch (override_only) { + case 0: + strcpy(alias, "ub:"); + break; + default: + warn("Unknown UB driver_override alias %08X\n", + override_only); + return 0; + } + + ADD(alias, "v", vendor != UB_ANY_ID, (__u16)vendor); + ADD(alias, "d", device != UB_ANY_ID, (__u16)device); + ADD(alias, "mv", mod_vendor != UB_ANY_ID, (__u16)mod_vendor); + ADD(alias, "m", module != UB_ANY_ID, (__u16)module); + + basecode_mask = class_mask; + subcode_mask = (class_mask) >> 8; + + if ((basecode_mask != 0 && basecode_mask != 0xFF) + || (subcode_mask != 0 && subcode_mask != 0xFF)) { + warn("Can't handle masks in %s:%04X\n", + filename, class_mask); + return 0; + } + + ADD(alias, "c", subcode_mask == 0xFF && basecode_mask == 0xFF, class_code); + return 1; +} + /* Does namelen bytes of name exactly match the symbol? */ static bool sym_is(const char *name, unsigned namelen, const char *symbol) { @@ -1555,6 +1598,7 @@ static const struct devtable devtable[] = { {"dfl", SIZE_dfl_device_id, do_dfl_entry}, {"ishtp", SIZE_ishtp_device_id, do_ishtp_entry}, {"cdx", SIZE_cdx_device_id, do_cdx_entry}, + {"ub", SIZE_ub_device_id, do_ub_entry}, }; /* Create MODULE_ALIAS() statements. -- Gitee From ce3c31b2ef19f69c7ce03cf767682df9138a82a1 Mon Sep 17 00:00:00 2001 From: Yuhao Xiang Date: Thu, 18 Sep 2025 22:31:41 +0800 Subject: [PATCH 02/45] ub:ubfi: Support ub firmware interface basic functions driver inclusion category: feature bugzilla: https://gitee.com/openeuler/kernel/issues/ID34DG CVE: NA ----------------------------------------------------------- Support UnifiedBus firmware interface basic functions, set bios mode, get ub root table by dts or acpi, handle dts-ubrt or acpi-ubrt Signed-off-by: Yuhao Xiang --- arch/arm64/configs/openeuler_defconfig | 1 + arch/x86/configs/openeuler_defconfig | 1 + drivers/ub/Kconfig | 1 + drivers/ub/Makefile | 1 + drivers/ub/ubfi/Kconfig | 18 ++++ drivers/ub/ubfi/Makefile | 4 + drivers/ub/ubfi/ub_fi.c | 127 ++++++++++++++++++++++ drivers/ub/ubfi/ub_fi.h | 17 +++ drivers/ub/ubfi/ubrt.c | 143 +++++++++++++++++++++++++ drivers/ub/ubfi/ubrt.h | 63 +++++++++++ drivers/ub/ubus/ubus_driver.c | 1 + 11 files changed, 377 insertions(+) create mode 100644 drivers/ub/ubfi/Kconfig create mode 100644 drivers/ub/ubfi/Makefile create mode 100644 drivers/ub/ubfi/ub_fi.c create mode 100644 drivers/ub/ubfi/ub_fi.h create mode 100644 drivers/ub/ubfi/ubrt.c create mode 100644 drivers/ub/ubfi/ubrt.h diff --git a/arch/arm64/configs/openeuler_defconfig b/arch/arm64/configs/openeuler_defconfig index da294863186b..c990a8f3cce0 100644 --- a/arch/arm64/configs/openeuler_defconfig +++ b/arch/arm64/configs/openeuler_defconfig @@ -8324,4 +8324,5 @@ CONFIG_UB=y CONFIG_UB_UBL=m CONFIG_UB_UBUS=y CONFIG_UB_UBUS_BUS=m +CONFIG_UB_UBFI=m # end of unified bus diff --git a/arch/x86/configs/openeuler_defconfig b/arch/x86/configs/openeuler_defconfig index 0a1384f4af52..ba8e51661637 100644 --- a/arch/x86/configs/openeuler_defconfig +++ b/arch/x86/configs/openeuler_defconfig @@ -9512,4 +9512,5 @@ CONFIG_UB=n CONFIG_UB_UBL=n CONFIG_UB_UBUS=n CONFIG_UB_UBUS_BUS=n +CONFIG_UB_UBFI=n # end of unified bus diff --git a/drivers/ub/Kconfig b/drivers/ub/Kconfig index fc591be43597..d876a10ca180 100644 --- a/drivers/ub/Kconfig +++ b/drivers/ub/Kconfig @@ -14,4 +14,5 @@ menuconfig UB if UB source "drivers/ub/ubus/Kconfig" +source "drivers/ub/ubfi/Kconfig" endif # UB diff --git a/drivers/ub/Makefile b/drivers/ub/Makefile index 10428ed28774..7860f8e74a47 100644 --- a/drivers/ub/Makefile +++ b/drivers/ub/Makefile @@ -1,3 +1,4 @@ # SPDX-License-Identifier: GPL-2.0+ obj-y += ubus/ +obj-y += ubfi/ diff --git a/drivers/ub/ubfi/Kconfig b/drivers/ub/ubfi/Kconfig new file mode 100644 index 000000000000..4cb8a264fe9a --- /dev/null +++ b/drivers/ub/ubfi/Kconfig @@ -0,0 +1,18 @@ +# SPDX-License-Identifier: GPL-2.0+ +# +# UnifiedBus firmware interface driver configuration +# + +if UB_UBUS + +config UB_UBFI + tristate "UnifiedBus firmware interface driver" + depends on UB_UBUS + default n + help + This option support for UnifiedBus firmware interface driver. + If you have ub function say y and it will be accessible from + within Linux. To compile this driver as a module, choose M here. + Say 'M' here unless you know what you are doing + +endif diff --git a/drivers/ub/ubfi/Makefile b/drivers/ub/ubfi/Makefile new file mode 100644 index 000000000000..f06b3fbfddb1 --- /dev/null +++ b/drivers/ub/ubfi/Makefile @@ -0,0 +1,4 @@ +# SPDX-License-Identifier: GPL-2.0+ + +ubfi-y := ub_fi.o ubrt.o +obj-$(CONFIG_UB_UBFI) += ubfi.o diff --git a/drivers/ub/ubfi/ub_fi.c b/drivers/ub/ubfi/ub_fi.c new file mode 100644 index 000000000000..4d62c74ced00 --- /dev/null +++ b/drivers/ub/ubfi/ub_fi.c @@ -0,0 +1,127 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (c) HiSilicon Technologies Co., Ltd. 2025. All rights reserved. + */ + +#define pr_fmt(fmt) "ubfi driver: " fmt + +#include +#include +#include + +#include "ubrt.h" +#include "ub_fi.h" + +#define ACPI_SIG_UBRT "UBRT" /* UB Root Table */ +#define UBIOS_INFO_TABLE "linux,ubios-information-table" + +enum bios_report_mode bios_mode = UNKNOWN; + +static void ub_bios_mode_init(void) +{ + if (acpi_disabled) + bios_mode = DTS; + else + bios_mode = ACPI; + + pr_info("Starting with mode: %d\n", bios_mode); +} + +static int ubfi_get_acpi_ubrt(void) +{ + struct acpi_table_header *header; + acpi_status status; + + status = acpi_get_table(ACPI_SIG_UBRT, 0, &header); + if (ACPI_FAILURE(status)) { + pr_err("ACPI failed to get UBRT.\n"); + if (status != AE_NOT_FOUND) + pr_err("ACPI failed msg: %s\n", + acpi_format_exception(status)); + return -ENODEV; + } + acpi_table = (struct acpi_table_ubrt *)header; + pr_info("get ubrt by acpi success\n"); + return 0; +} + +static int ubfi_get_dts_ubrt(void) +{ + struct device_node *node; + u64 phys_addr; + + node = of_find_node_by_path("/chosen"); + if (!node) { + pr_err("Failed to get device tree chosen node\n"); + return -EINVAL; + } + + if (of_property_read_u64(node, UBIOS_INFO_TABLE, &phys_addr)) { + pr_err("Failed to get %s node\n", UBIOS_INFO_TABLE); + return -EINVAL; + } + + ubios_table = (struct ubios_root_table *)ub_table_get(phys_addr); + if (!ubios_table) + return -ENOMEM; + + pr_info("ubfi get ubrt by device tree success\n"); + return 0; +} + +static int ubfi_get_ubrt(void) +{ + if (bios_mode == ACPI) + return ubfi_get_acpi_ubrt(); + else if (bios_mode == DTS) + return ubfi_get_dts_ubrt(); + return -EINVAL; +} + +static int handle_ubrt(void) +{ + if (bios_mode == ACPI) + return handle_acpi_ubrt(); + else if (bios_mode == DTS) + return handle_dts_ubrt(); + + return -EINVAL; +} + +static void ubfi_put_ubrt(void) +{ + if (bios_mode == ACPI) { + acpi_put_table((struct acpi_table_header *)acpi_table); + acpi_table = NULL; + } else if (bios_mode == DTS) { + ub_table_put(ubios_table); + ubios_table = NULL; + } +} + +static int __init ubfi_init(void) +{ + int ret; + + ub_bios_mode_init(); + + ret = ubfi_get_ubrt(); + if (ret) { + pr_info("can't get ub information from bios, ret=%d\n", ret); + return 0; + } + + return handle_ubrt(); +} + +static void __exit ubfi_exit(void) +{ + uninit_ub_nodes(); + ubfi_put_ubrt(); +} + +module_init(ubfi_init); +module_exit(ubfi_exit); +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("UnifiedBus firmware interface driver"); +MODULE_IMPORT_NS(UB_UBFI); diff --git a/drivers/ub/ubfi/ub_fi.h b/drivers/ub/ubfi/ub_fi.h new file mode 100644 index 000000000000..3edf8dd6de4e --- /dev/null +++ b/drivers/ub/ubfi/ub_fi.h @@ -0,0 +1,17 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Copyright (c) HiSilicon Technologies Co., Ltd. 2025. All rights reserved. + */ + +#ifndef __UB_FI_H__ +#define __UB_FI_H__ + +enum bios_report_mode { + ACPI = 0, + DTS = 1, + UBIOS = 3, + UNKNOWN = 4, +}; + +extern enum bios_report_mode bios_mode; +#endif /* __UB_FI_H__ */ diff --git a/drivers/ub/ubfi/ubrt.c b/drivers/ub/ubfi/ubrt.c new file mode 100644 index 000000000000..dd33b188bbb0 --- /dev/null +++ b/drivers/ub/ubfi/ubrt.c @@ -0,0 +1,143 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (c) HiSilicon Technologies Co., Ltd. 2025. All rights reserved. + */ + +#define pr_fmt(fmt) "ubfi ubrt: " fmt + +#include +#include +#include + +#include "ubrt.h" + +#define UBIOS_SIG_UBC "ubc" + +struct acpi_table_ubrt *acpi_table; +struct ubios_root_table *ubios_table; + +/* + * ummu max count is 32, max size is 40 + 32 * 128 = 4640 + * ubc max count is 32, max size is 40 + 88 + 32 * 256 + 32 * 4 = 8448 + */ +#define UBIOS_TABLE_TOTLE_SIZE_MAX 8448 + +/* remember to use ub_table_put to release memory alloced by ub_table_get */ +void *ub_table_get(u64 pa) +{ + void __iomem *va; + u32 total_size; + void *ret; + + if (!pa) + return NULL; + + va = ioremap(pa, sizeof(struct ub_table_header)); + if (!va) { + pr_err("ioremap ub table header fail\n"); + return NULL; + } + + total_size = readl(va + UB_TABLE_HEADER_NAME_LEN); + pr_debug("ub table size is[0x%x]\n", total_size); + if (total_size == 0 || total_size > UBIOS_TABLE_TOTLE_SIZE_MAX) { + pr_err("ubios table size is invalid\n"); + iounmap(va); + return NULL; + } + iounmap(va); + + va = ioremap(pa, total_size); + if (!va) { + pr_err("ioremap full ub table fail\n"); + return NULL; + } + + ret = kzalloc(total_size, GFP_KERNEL); + if (!ret) { + iounmap(va); + return NULL; + } + + memcpy_fromio(ret, va, total_size); + iounmap(va); + return ret; +} + +void ub_table_put(void *va) +{ + kfree(va); +} + +void uninit_ub_nodes(void) +{ +} + +int handle_acpi_ubrt(void) +{ + struct ubrt_sub_table *sub_table; + int ret = 0; + u32 i; + + pr_info("acpi ubrt sub table count is %u\n", acpi_table->count); + + for (i = 0; i < acpi_table->count; i++) { + sub_table = &acpi_table->sub_table[i]; + switch (sub_table->type) { + case UB_BUS_CONTROLLER_TABLE: + break; + default: + pr_warn("Ignore sub table: type %u\n", sub_table->type); + break; + } + if (ret) { + pr_err("parse ubrt sub table type %u fail\n", + sub_table->type); + goto fail; + } + } + return ret; +fail: + uninit_ub_nodes(); + return ret; +} + +int handle_dts_ubrt(void) +{ + char name[UB_TABLE_HEADER_NAME_LEN] = {}; + struct ub_table_header *header; + int ret = 0, i; + + if (ubios_table->count == 0) { + pr_err("ubios root table has no sub tables.\n"); + return 0; + } + pr_info("ubios sub table count is %u\n", ubios_table->count); + + for (i = 0; i < ubios_table->count; i++) { + header = (struct ub_table_header *)ub_table_get( + ubios_table->sub_tables[i]); + if (!header) + continue; + + memcpy(name, header->name, UB_TABLE_HEADER_NAME_LEN - 1); + pr_info("ubrt sub table name is %s\n", name); + ub_table_put(header); + + if (!strncmp(name, UBIOS_SIG_UBC, strlen(UBIOS_SIG_UBC))) + ret = 0; + else + pr_warn("Ignore sub table: %s\n", name); + + if (ret) { + pr_err("Create %s device ret=%d\n", name, ret); + goto out; + } + } + + return 0; + +out: + uninit_ub_nodes(); + return ret; +} diff --git a/drivers/ub/ubfi/ubrt.h b/drivers/ub/ubfi/ubrt.h new file mode 100644 index 000000000000..378740a4a31b --- /dev/null +++ b/drivers/ub/ubfi/ubrt.h @@ -0,0 +1,63 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Copyright (c) HiSilicon Technologies Co., Ltd. 2025. All rights reserved. + */ + +#ifndef __UBRT_H__ +#define __UBRT_H__ + +#include +#include + +#define UB_TABLE_HEADER_NAME_LEN 16 + +struct ub_table_header { + char name[UB_TABLE_HEADER_NAME_LEN]; + u32 total_size; + u8 version; + u8 reserved[3]; + u32 remaining_size; + u32 checksum; +}; + +struct ubios_root_table { + struct ub_table_header header; + u16 count; + u8 reserved[6]; + u64 sub_tables[]; +}; + +struct ubrt_sub_table { + u8 type; + u8 reserved[7]; + u64 pointer; +}; + +struct acpi_table_ubrt { + struct acpi_table_header head; + u32 count; + struct ubrt_sub_table sub_table[]; +}; + +enum ubrt_sub_table_type { + UB_BUS_CONTROLLER_TABLE = 0, + UMMU_TABLE = 1, + UB_RESERVED_MEMORY_TABLE = 2, + VIRTUAL_BUS_TABLE = 3, + CALL_ID_SERVICE_TABLE = 4, + UB_ENTITY = 5, + UB_TOPOLOGY = 6, +}; + +extern struct acpi_table_ubrt *acpi_table; +extern struct ubios_root_table *ubios_table; + +void *ub_table_get(u64 pa); +void ub_table_put(void *va); + +int handle_acpi_ubrt(void); +int handle_dts_ubrt(void); + +void uninit_ub_nodes(void); + +#endif /* __UBRT_H__ */ diff --git a/drivers/ub/ubus/ubus_driver.c b/drivers/ub/ubus/ubus_driver.c index 28303556807a..5448bbfa3717 100644 --- a/drivers/ub/ubus/ubus_driver.c +++ b/drivers/ub/ubus/ubus_driver.c @@ -266,4 +266,5 @@ module_init(ubus_driver_init); module_exit(ubus_driver_exit); MODULE_LICENSE("GPL"); MODULE_DESCRIPTION("UB bus driver"); +MODULE_IMPORT_NS(UB_UBFI); MODULE_IMPORT_NS(UB_UBUS); -- Gitee From 00331a0a3422e74ac0c28078549de862538c4190 Mon Sep 17 00:00:00 2001 From: Junlong Zheng Date: Thu, 18 Sep 2025 22:35:59 +0800 Subject: [PATCH 03/45] ub:ubus: Support UB Bus Attribute Group driver inclusion category: feature bugzilla: https://gitee.com/openeuler/kernel/issues/ID34DG CVE: NA ----------------------------------------------------------- Add support for UB bus attribute groups, including device attribute groups, bus attribute groups, and driver attribute groups. Signed-off-by: Junlong Zheng --- drivers/ub/ubus/Makefile | 2 +- drivers/ub/ubus/sysfs.c | 145 +++++++++++++++++++++++++++++ drivers/ub/ubus/sysfs.h | 11 +++ drivers/ub/ubus/ub-driver.c | 22 +++++ drivers/ub/ubus/ubus_driver.c | 170 ++++++++++++++++++++++++++++++++++ include/ub/ubus/ubus.h | 20 ++++ 6 files changed, 369 insertions(+), 1 deletion(-) create mode 100644 drivers/ub/ubus/sysfs.c create mode 100644 drivers/ub/ubus/sysfs.h diff --git a/drivers/ub/ubus/Makefile b/drivers/ub/ubus/Makefile index b3cb5016bf2e..0d05ad501ea1 100644 --- a/drivers/ub/ubus/Makefile +++ b/drivers/ub/ubus/Makefile @@ -2,6 +2,6 @@ obj-$(CONFIG_UB_UBUS) += ub-driver.o -ubus-y := ubus_driver.o +ubus-y := ubus_driver.o sysfs.o obj-$(CONFIG_UB_UBUS_BUS) += ubus.o diff --git a/drivers/ub/ubus/sysfs.c b/drivers/ub/ubus/sysfs.c new file mode 100644 index 000000000000..84d32e36249b --- /dev/null +++ b/drivers/ub/ubus/sysfs.c @@ -0,0 +1,145 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (c) HiSilicon Technologies Co., Ltd. 2025. All rights reserved. + */ + +#define pr_fmt(fmt) "ubus sysfs: " fmt + +#include "ubus.h" +#include "sysfs.h" + +#define ub_config_attr(field, format_string) \ +static ssize_t field##_show(struct device *dev, struct device_attribute *attr, char *buf) \ +{ \ + struct ub_entity *uent; \ + \ + uent = to_ub_entity(dev); \ + return sysfs_emit(buf, format_string, uent->guid.bits.field); \ +} \ +static DEVICE_ATTR_RO(field) + +ub_config_attr(device, "%#06x\n"); +ub_config_attr(type, "%#x\n"); +ub_config_attr(vendor, "%#06x\n"); + +static ssize_t class_code_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct ub_entity *uent; + + uent = to_ub_entity(dev); + return sysfs_emit(buf, "%#06x\n", uent->class_code); +} +static DEVICE_ATTR_RO(class_code); + +static ssize_t guid_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct ub_entity *uent = to_ub_entity(dev); + int count; + + count = ub_show_guid(&uent->guid, buf); + + return count + sysfs_emit_at(buf, count, "\n"); +} +static DEVICE_ATTR_RO(guid); + +static ssize_t kref_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + return sysfs_emit(buf, "%u\n", kref_read(&dev->kobj.kref)); +} +static DEVICE_ATTR_RO(kref); + +static ssize_t driver_override_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct ub_entity *uent = to_ub_entity(dev); + int ret; + + ret = driver_set_override(dev, &uent->driver_override, buf, count); + if (ret) + return ret; + + return count; +} + +static ssize_t driver_override_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct ub_entity *uent = to_ub_entity(dev); + ssize_t len; + + device_lock(dev); + len = sysfs_emit(buf, "%s\n", uent->driver_override); + device_unlock(dev); + return len; +} +static DEVICE_ATTR_RW(driver_override); + +static ssize_t match_driver_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct ub_entity *uent = to_ub_entity(dev); + unsigned long val; + ssize_t result; + + result = kstrtoul(buf, 0, &val); + if (result < 0) + return result; + if (!(val == 0 || val == 1)) + return -EINVAL; + + device_lock(dev); + uent->match_driver = !!val; + device_unlock(dev); + + return count; +} + +static ssize_t match_driver_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct ub_entity *uent = to_ub_entity(dev); + ssize_t len; + + device_lock(dev); + len = sysfs_emit(buf, "%s\n", uent->match_driver ? "true" : "false"); + device_unlock(dev); + + return len; +} +static DEVICE_ATTR_RW(match_driver); + +static struct attribute *ub_entity_attrs[] = { + &dev_attr_vendor.attr, + &dev_attr_device.attr, + &dev_attr_class_code.attr, + &dev_attr_type.attr, + &dev_attr_driver_override.attr, + &dev_attr_match_driver.attr, + &dev_attr_guid.attr, + &dev_attr_kref.attr, + NULL +}; + +static const struct attribute_group ub_entity_group = { + .attrs = ub_entity_attrs, +}; + +const struct attribute_group *ub_entity_groups[] = { + &ub_entity_group, + NULL +}; + +static struct attribute *ub_bus_attrs[] = { + NULL +}; + +static const struct attribute_group ub_bus_group = { + .attrs = ub_bus_attrs, +}; + +const struct attribute_group *ub_bus_groups[] = { + &ub_bus_group, + NULL +}; diff --git a/drivers/ub/ubus/sysfs.h b/drivers/ub/ubus/sysfs.h new file mode 100644 index 000000000000..8c8fc5d3fa72 --- /dev/null +++ b/drivers/ub/ubus/sysfs.h @@ -0,0 +1,11 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Copyright (c) HiSilicon Technologies Co., Ltd. 2025. All rights reserved. + */ +#ifndef __SYSFS_H__ +#define __SYSFS_H__ + +extern const struct attribute_group *ub_entity_groups[]; +extern const struct attribute_group *ub_bus_groups[]; + +#endif /* __SYSFS_H__ */ diff --git a/drivers/ub/ubus/ub-driver.c b/drivers/ub/ubus/ub-driver.c index 516aec010e16..1922b5062327 100644 --- a/drivers/ub/ubus/ub-driver.c +++ b/drivers/ub/ubus/ub-driver.c @@ -12,6 +12,23 @@ struct bus_type ub_bus_type = { }; EXPORT_SYMBOL_GPL(ub_bus_type); +struct ub_dynid { + struct list_head node; + struct ub_device_id id; +}; + +static void ub_free_dynids(struct ub_driver *drv) +{ + struct ub_dynid *dynid, *n; + + spin_lock(&drv->dynids.lock); + list_for_each_entry_safe(dynid, n, &drv->dynids.list, node) { + list_del(&dynid->node); + kfree(dynid); + } + spin_unlock(&drv->dynids.lock); +} + int __ub_register_driver(struct ub_driver *drv, struct module *owner, const char *mod_name) { @@ -22,6 +39,10 @@ int __ub_register_driver(struct ub_driver *drv, struct module *owner, drv->driver.bus = &ub_bus_type; drv->driver.owner = owner; drv->driver.mod_name = mod_name; + drv->driver.groups = drv->groups; + + spin_lock_init(&drv->dynids.lock); + INIT_LIST_HEAD(&drv->dynids.list); return driver_register(&drv->driver); } @@ -33,6 +54,7 @@ void ub_unregister_driver(struct ub_driver *drv) return; driver_unregister(&drv->driver); + ub_free_dynids(drv); } EXPORT_SYMBOL_GPL(ub_unregister_driver); diff --git a/drivers/ub/ubus/ubus_driver.c b/drivers/ub/ubus/ubus_driver.c index 5448bbfa3717..83f32f2a5bdf 100644 --- a/drivers/ub/ubus/ubus_driver.c +++ b/drivers/ub/ubus/ubus_driver.c @@ -13,6 +13,7 @@ #include #include +#include "sysfs.h" #include "ubus.h" struct ub_entity *ub_entity_get(struct ub_entity *dev) @@ -30,6 +31,11 @@ void ub_entity_put(struct ub_entity *dev) } EXPORT_SYMBOL_GPL(ub_entity_put); +struct ub_dynid { + struct list_head node; + struct ub_device_id id; +}; + static const struct ub_device_id ub_entity_id_any = { .vendor = (__u32)UB_ANY_ID, .device = (__u32)UB_ANY_ID, @@ -66,11 +72,25 @@ static const struct ub_device_id *ub_match_device(struct ub_driver *drv, struct ub_entity *dev) { const struct ub_device_id *found_id = NULL, *ids; + struct ub_dynid *dynid; /* When driver_override is set, only bind to the matching driver */ if (dev->driver_override && strcmp(dev->driver_override, drv->name)) return NULL; + /* Look at the dynamic ids first, before the static ones */ + spin_lock(&drv->dynids.lock); + list_for_each_entry(dynid, &drv->dynids.list, node) { + if (ub_match_one_device(&dynid->id, dev)) { + found_id = &dynid->id; + break; + } + } + spin_unlock(&drv->dynids.lock); + + if (found_id) + return found_id; + for (ids = drv->id_table; (found_id = ub_match_id(ids, dev)); ids = found_id + 1) { if (found_id->override_only) { @@ -221,6 +241,150 @@ static int ub_uevent(const struct device *dev, struct kobj_uevent_env *env) return 0; } +static int ub_add_dynid(struct ub_driver *drv, + unsigned int vendor, unsigned int device, + unsigned int mod_vendor, unsigned int module, + unsigned int class_code, unsigned int class_mask, + unsigned long driver_data) +{ + struct ub_dynid *dynid; + + dynid = kzalloc(sizeof(*dynid), GFP_KERNEL); + if (!dynid) + return -ENOMEM; + + dynid->id.vendor = vendor; + dynid->id.device = device; + dynid->id.mod_vendor = mod_vendor; + dynid->id.module = module; + dynid->id.class_code = class_code; + dynid->id.class_mask = class_mask; + dynid->id.driver_data = driver_data; + + spin_lock(&drv->dynids.lock); + list_add_tail(&dynid->node, &drv->dynids.list); + spin_unlock(&drv->dynids.lock); + + return driver_attach(&drv->driver); +} + +static int ub_check_id_exist(struct ub_driver *udrv, u32 vendor, u32 device, + u32 mod_vendor, u32 module, u32 class_code) +{ + struct ub_entity *uent; + int ret = 0; + + uent = kzalloc(sizeof(struct ub_entity), GFP_KERNEL); + if (!uent) + return -ENOMEM; + + uent_vendor(uent) = vendor; + uent_device(uent) = device; + uent_class(uent) = class_code; + uent->mod_vendor = mod_vendor; + uent->module = module; + + if (ub_match_device(udrv, uent)) + ret = -EEXIST; + + kfree(uent); + + return ret; +} + +static int ub_check_driver_data_valid(const struct ub_device_id *ids, + unsigned long driver_data) +{ + /* Only accept driver_data values that match an exiting id_table entry */ + while (ids->vendor || ids->mod_vendor || ids->class_mask) { + if (driver_data == ids->driver_data) + return 0; + ids++; + } + + return -EINVAL; +} + +static ssize_t new_id_store(struct device_driver *driver, const char *buf, + size_t count) +{ + u32 vendor, device, class_code = 0, class_mask = 0; + u32 mod_vendor = UB_ANY_ID, module = UB_ANY_ID; + struct ub_driver *udrv = to_ub_driver(driver); + unsigned long driver_data = 0; + int fileds, ret; + + fileds = sscanf(buf, "%x %x %x %x %x %x %lx", &vendor, &device, + &mod_vendor, &module, + &class_code, &class_mask, &driver_data); + if (fileds < 2) /* 2 parameter for vender & device id */ + return -EINVAL; + + if (fileds != 7) { /* 7 for driver_data input */ + ret = ub_check_id_exist(udrv, vendor, device, mod_vendor, + module, class_code); + if (ret) + return ret; + } + + if (udrv->id_table) { + ret = ub_check_driver_data_valid(udrv->id_table, driver_data); + if (ret) + return ret; + } + + ret = ub_add_dynid(udrv, vendor, device, mod_vendor, module, + class_code, class_mask, driver_data); + if (ret) + return ret; + + return count; +} +static DRIVER_ATTR_WO(new_id); + +static ssize_t remove_id_store(struct device_driver *driver, const char *buf, + size_t count) +{ + u32 vendor, device, class_code = 0, class_mask = 0; + u32 mod_vendor = UB_ANY_ID, module = UB_ANY_ID; + struct ub_driver *udrv = to_ub_driver(driver); + struct ub_dynid *dynid, *n; + ssize_t ret = -ENODEV; + int fields; + + fields = sscanf(buf, "%x %x %x %x %x %x", &vendor, &device, &mod_vendor, + &module, &class_code, &class_mask); + if (fields < 2) /* 2 parameter for vender & device id */ + return -EINVAL; + + spin_lock(&udrv->dynids.lock); + list_for_each_entry_safe(dynid, n, &udrv->dynids.list, node) { + struct ub_device_id *id = &dynid->id; + + if ((id->vendor == vendor) && + (id->device == device) && + (mod_vendor == UB_ANY_ID || id->mod_vendor == mod_vendor) && + (module == UB_ANY_ID || id->module == module) && + !((id->class_code ^ class_code) & class_mask)) { + list_del(&dynid->node); + kfree(dynid); + ret = (ssize_t)count; + break; + } + } + spin_unlock(&udrv->dynids.lock); + + return ret; +} +static DRIVER_ATTR_WO(remove_id); + +static struct attribute *ub_drv_attrs[] = { + &driver_attr_new_id.attr, + &driver_attr_remove_id.attr, + NULL +}; +ATTRIBUTE_GROUPS(ub_drv); + void ub_bus_type_init(void) { ub_bus_type.match = ub_bus_match; @@ -228,6 +392,9 @@ void ub_bus_type_init(void) ub_bus_type.probe = ub_entity_probe; ub_bus_type.remove = ub_entity_remove; ub_bus_type.shutdown = ub_entity_shutdown; + ub_bus_type.dev_groups = ub_entity_groups; + ub_bus_type.bus_groups = ub_bus_groups; + ub_bus_type.drv_groups = ub_drv_groups; } void ub_bus_type_uninit(void) @@ -237,6 +404,9 @@ void ub_bus_type_uninit(void) ub_bus_type.probe = NULL; ub_bus_type.remove = NULL; ub_bus_type.shutdown = NULL; + ub_bus_type.dev_groups = NULL; + ub_bus_type.bus_groups = NULL; + ub_bus_type.drv_groups = NULL; } int ub_host_probe(void) diff --git a/include/ub/ubus/ubus.h b/include/ub/ubus/ubus.h index 68cbc445a853..9910ce808647 100644 --- a/include/ub/ubus/ubus.h +++ b/include/ub/ubus/ubus.h @@ -54,6 +54,14 @@ struct ub_guid { #define uent_base_code(uent) ((uent)->class_code & UB_BASE_CODE_MASK) #define uent_seq(uent) ((uent)->guid.bits.seq_num) +static inline int ub_show_guid(struct ub_guid *guid, char *buf) +{ + return sprintf(buf, "%04x-%04x-%01x-%01x-%06x-%016llx", + guid->bits.vendor, guid->bits.device, + guid->bits.version, guid->bits.type, + guid->bits.reserved, guid->bits.seq_num); +} + struct ub_entity { /* Driver framework base info */ struct device dev; @@ -68,6 +76,11 @@ struct ub_entity { u16 module; }; +struct ub_dynids { + spinlock_t lock; /* Protects list, index */ + struct list_head list; /* For IDs added at runtime */ +}; + #define to_ub_entity(n) container_of(n, struct ub_entity, dev) /** @@ -97,7 +110,11 @@ struct ub_entity { * context, so it can sleep. * @shutdown: Hook into reboot_notifier_list (kernel/sys.c). * Intended to stop any idling operations. + * @groups: Sysfs attribute groups. + * @dev_groups: Attributes attached to the device that will be + * created once it is bound to the driver. * @driver: Driver model structure. + * @dynids: List of dynamically added device IDs. */ struct ub_driver { struct list_head node; @@ -108,7 +125,10 @@ struct ub_driver { /* entity removed (NULL if not a hot-plug capable driver) */ void (*remove)(struct ub_entity *uent); void (*shutdown)(struct ub_entity *uent); + const struct attribute_group **groups; + const struct attribute_group **dev_groups; struct device_driver driver; + struct ub_dynids dynids; }; static inline struct ub_driver *to_ub_driver(struct device_driver *drv) -- Gitee From 275e250f6ef19ff7095a9112e352a53c697d3f1a Mon Sep 17 00:00:00 2001 From: Yuhao Xiang Date: Thu, 16 Oct 2025 17:13:57 +0800 Subject: [PATCH 04/45] ub:ubfi: ubfi driver parse ubc information from ubrt driver inclusion category: feature bugzilla: https://gitee.com/openeuler/kernel/issues/ID34DG CVE: NA ----------------------------------------------------------- ubfi driver parse ubc information from ubrt table, by acpi upload or dts upload Signed-off-by: Yuhao Xiang --- drivers/ub/ubfi/Makefile | 2 +- drivers/ub/ubfi/ubc.c | 140 +++++++++++++++++++++++++++++++++++++++ drivers/ub/ubfi/ubc.h | 96 +++++++++++++++++++++++++++ drivers/ub/ubfi/ubrt.c | 5 +- include/ub/ubfi/ubfi.h | 18 +++++ include/ub/ubus/ubus.h | 5 ++ 6 files changed, 264 insertions(+), 2 deletions(-) create mode 100644 drivers/ub/ubfi/ubc.c create mode 100644 drivers/ub/ubfi/ubc.h create mode 100644 include/ub/ubfi/ubfi.h diff --git a/drivers/ub/ubfi/Makefile b/drivers/ub/ubfi/Makefile index f06b3fbfddb1..55bdaf0bb24f 100644 --- a/drivers/ub/ubfi/Makefile +++ b/drivers/ub/ubfi/Makefile @@ -1,4 +1,4 @@ # SPDX-License-Identifier: GPL-2.0+ -ubfi-y := ub_fi.o ubrt.o +ubfi-y := ub_fi.o ubrt.o ubc.o obj-$(CONFIG_UB_UBFI) += ubfi.o diff --git a/drivers/ub/ubfi/ubc.c b/drivers/ub/ubfi/ubc.c new file mode 100644 index 000000000000..40bd3c1f69b4 --- /dev/null +++ b/drivers/ub/ubfi/ubc.c @@ -0,0 +1,140 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (c) HiSilicon Technologies Co., Ltd. 2025. All rights reserved. + */ + +#define pr_fmt(fmt) "ubfi ubc: " fmt + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "ubrt.h" +#include "ub_fi.h" +#include "ubc.h" + +struct list_head ubc_list; +EXPORT_SYMBOL_GPL(ubc_list); + +u32 ubc_eid_start; +EXPORT_SYMBOL_GPL(ubc_eid_start); + +u32 ubc_eid_end; +EXPORT_SYMBOL_GPL(ubc_eid_end); + +u32 ubc_cna_start; +EXPORT_SYMBOL_GPL(ubc_cna_start); + +u32 ubc_cna_end; +EXPORT_SYMBOL_GPL(ubc_cna_end); + +u8 ubc_feature; +EXPORT_SYMBOL_GPL(ubc_feature); + +static bool cluster_mode; + +static int acpi_update_ubc_msi_domain(void) +{ + return 0; +} + +static int dts_update_ubc_msi_domain(void) +{ + return 0; +} + +static int create_ubc(struct ubc_node *node, u32 ctl_no) +{ + return 0; +} + +static int parse_ubc_table(void *info_node) +{ + struct ubrt_ubc_table *ubc_table = info_node; + struct ubc_node *node = ubc_table->ubcs; + u32 count, i; + int ret; + + count = ubc_table->ubc_count; + if (!count) { + pr_warn("ubc table has no ubc.\n"); + return 0; + } + + /* get ubc common attribute */ + ubc_cna_start = ubc_table->cna_start; + ubc_cna_end = ubc_table->cna_end; + ubc_eid_start = ubc_table->eid_start; + ubc_eid_end = ubc_table->eid_end; + ubc_feature = ubc_table->feature; + cluster_mode = ubc_table->cluster_mode; + + pr_info("cna_start=%u, cna_end=%u\n", ubc_cna_start, ubc_cna_end); + pr_info("eid_start=%u, eid_end=%u\n", ubc_eid_start, ubc_eid_end); + pr_info("ubc_count=%u, bios_cluster_mode=%u, feature=%u\n", count, + cluster_mode, ubc_feature); + if (ubc_cna_start > ubc_cna_end || ubc_eid_start > ubc_eid_end) { + pr_err("eid or cna range is incorrect\n"); + return -EINVAL; + } + + for (i = 0; i < count; i++) { + ret = create_ubc(node, i); + if (ret) { + pr_err("Create No.%u ubc failed, ret=%d\n", i, ret); + return ret; + } + node++; + } + + return 0; +} + +static void ub_destroy_bus_controllers(void) +{ +} + +void destroy_ubc(void) +{ + ub_destroy_bus_controllers(); +} + +int handle_ubc_table(u64 pointer) +{ + void *info_node = ub_table_get(pointer); + int ret; + + if (!info_node) + return -EINVAL; + + INIT_LIST_HEAD(&ubc_list); + + ret = parse_ubc_table(info_node); + if (ret) + goto err_handle; + + pr_info("Update msi domain for ub bus controller\n"); + /* Update msi domain for ub bus controller */ + if (bios_mode == ACPI) + ret = acpi_update_ubc_msi_domain(); + else + ret = dts_update_ubc_msi_domain(); + + if (ret) + goto err_handle; + + ub_table_put(info_node); + return 0; + +err_handle: + ub_table_put(info_node); + destroy_ubc(); + return ret; +} diff --git a/drivers/ub/ubfi/ubc.h b/drivers/ub/ubfi/ubc.h new file mode 100644 index 000000000000..966c1a152810 --- /dev/null +++ b/drivers/ub/ubfi/ubc.h @@ -0,0 +1,96 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Copyright (c) HiSilicon Technologies Co., Ltd. 2025. All rights reserved. + */ + +#ifndef __UBC_H__ +#define __UBC_H__ + +#include "ubrt.h" + +#define UBC_VENDOR_INFO_SIZE 256 + +/** + * struct ubc_node - Information about a single UBC(UB Controller) + * @int_id_start: Start value of the assignable Interrupt ID range + * @int_id_end: End value (inclusive) of the range of assignable Interrupt IDs + * @hpa_base: Available Host Physical Address Space Base Address + * @hpa_size: Size of available Host Physical Address space for allocation + * @mem_size_limit: Maximum addressable bit width + * @dma_cca: Indicates UBC DMA CCA (Cache Coherent Attribute) capability + * 0: Supports DMA but does not support CCA + * 1: Supports both DMA and CCA + * Others: Does not support DMA + * @ummu_mapping: Indicates the association between this UBC and UMMU, + * using the UMMU Index, which is represented by the UMMU + * serial number in the UMMU information table. + * @proximity_domain: Indicates the NUMA Domain number to which this UBC belongs. + * @msg_queue_base: UBC MSG Queue Register Base Address + * @msg_queue_size: UBC MSG Queue Register Space Size + * @msg_queue_depth: UBC MSG Queue Interrupt Depth + * @msg_int: UBC MSG Queue Interrupt Number + * @msg_int_attr: UBC MSG Queue Interrupt Attributes + * bit0: Level OR Edge Trigger + * 0: Level + * 1: Edge + * bit1: High level (rising edge) OR Low level (falling edge) + * 0: High level (rising edge) + * 1: Low level (falling edge) + * @vendor_info: Vendor-defined information + */ +struct ubc_node { + u32 int_id_start; + u32 int_id_end; + u64 hpa_base; + u64 hpa_size; + u8 mem_size_limit; + u8 dma_cca; + u16 ummu_mapping; + u16 proximity_domain; + u16 reserved; + u64 msg_queue_base; + u64 msg_queue_size; + u16 msg_queue_depth; + u16 msg_int; + u8 msg_int_attr; + u8 reserved2[59]; + u64 ubc_guid_low; + u64 ubc_guid_high; + u8 vendor_info[UBC_VENDOR_INFO_SIZE]; +}; + +/** + * struct ubrt_ubc_table - all ubcs in ubrt + * @header: Public header of the UBRT table + * @cna_start: Starting CNA number available for this UBPU + * @cna_end: End CNA number available for this UBPU (inclusive) + * @eid_start: Starting EID number available for this UBPU + * @eid_end: End EID number available for this UBPU (inclusive) + * @feature: Features enabled by UBCs: + * bit0: MMIO Token Value, a security feature for MMIO; + * 0 indicates disabled, 1 indicates enabled. + * bit1: MCTP over UB, an MCTP over UB feature; + * 0 indicates disabled, 1 indicates enabled. + * Other bits: reserved. + * @cluster_mode: System Operating Mode + * 0: Standalone mode, the OS can perform UB enumeration. + * 1: Super Node mode, the OS cannot perform active UB enumeration operations. + * @ubc_count: Number of UBCs + */ +struct ubrt_ubc_table { + struct ub_table_header header; + u32 cna_start; + u32 cna_end; + u32 eid_start; + u32 eid_end; + u8 feature; + u8 reserved[3]; + u16 cluster_mode; + u16 ubc_count; + struct ubc_node ubcs[]; +}; + +int handle_ubc_table(u64 pointer); +void destroy_ubc(void); + +#endif /* __UBC_H__ */ diff --git a/drivers/ub/ubfi/ubrt.c b/drivers/ub/ubfi/ubrt.c index dd33b188bbb0..ffd985861572 100644 --- a/drivers/ub/ubfi/ubrt.c +++ b/drivers/ub/ubfi/ubrt.c @@ -9,6 +9,7 @@ #include #include +#include "ubc.h" #include "ubrt.h" #define UBIOS_SIG_UBC "ubc" @@ -71,6 +72,7 @@ void ub_table_put(void *va) void uninit_ub_nodes(void) { + destroy_ubc(); } int handle_acpi_ubrt(void) @@ -85,6 +87,7 @@ int handle_acpi_ubrt(void) sub_table = &acpi_table->sub_table[i]; switch (sub_table->type) { case UB_BUS_CONTROLLER_TABLE: + ret = handle_ubc_table(sub_table->pointer); break; default: pr_warn("Ignore sub table: type %u\n", sub_table->type); @@ -125,7 +128,7 @@ int handle_dts_ubrt(void) ub_table_put(header); if (!strncmp(name, UBIOS_SIG_UBC, strlen(UBIOS_SIG_UBC))) - ret = 0; + ret = handle_ubc_table(ubios_table->sub_tables[i]); else pr_warn("Ignore sub table: %s\n", name); diff --git a/include/ub/ubfi/ubfi.h b/include/ub/ubfi/ubfi.h new file mode 100644 index 000000000000..9131e1da0888 --- /dev/null +++ b/include/ub/ubfi/ubfi.h @@ -0,0 +1,18 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Copyright (c) HiSilicon Technologies Co., Ltd. 2025. All rights reserved. + */ + +#ifndef _UB_UBFI_UBFI_H_ +#define _UB_UBFI_UBFI_H_ + +#include + +extern struct list_head ubc_list; +extern u32 ubc_eid_start; +extern u32 ubc_eid_end; +extern u32 ubc_cna_start; +extern u32 ubc_cna_end; +extern u8 ubc_feature; + +#endif /* _UB_UBFI_UBFI_H_ */ diff --git a/include/ub/ubus/ubus.h b/include/ub/ubus/ubus.h index 9910ce808647..2e11e9f861cb 100644 --- a/include/ub/ubus/ubus.h +++ b/include/ub/ubus/ubus.h @@ -131,6 +131,11 @@ struct ub_driver { struct ub_dynids dynids; }; +struct ub_bus_controller { + struct device dev; + struct list_head node; +}; + static inline struct ub_driver *to_ub_driver(struct device_driver *drv) { return drv ? container_of(drv, struct ub_driver, driver) : NULL; -- Gitee From d0691cbf52284463916b5000d8f5df3708949d33 Mon Sep 17 00:00:00 2001 From: Jianquan Lin Date: Fri, 19 Sep 2025 09:40:21 +0800 Subject: [PATCH 05/45] ub:ubus: add ubus controller framework driver inclusion category: feature bugzilla: https://gitee.com/openeuler/kernel/issues/ID34DG CVE: NA ----------------------------------------------------------- Add ubus controller framework, providing probe and remove controllers and find ubus controller by clan network address functions Signed-off-by: Jianquan Lin --- drivers/ub/ubus/Makefile | 4 +- drivers/ub/ubus/controller.c | 21 +++++++ drivers/ub/ubus/ubus.h | 10 ++++ drivers/ub/ubus/ubus_controller.c | 63 ++++++++++++++++++++ drivers/ub/ubus/ubus_controller.h | 17 ++++++ drivers/ub/ubus/ubus_driver.c | 96 +++++++++++++++++++++++++++++++ drivers/ub/ubus/ubus_inner.h | 3 + include/ub/ubus/ubus.h | 44 ++++++++++++++ 8 files changed, 256 insertions(+), 2 deletions(-) create mode 100644 drivers/ub/ubus/controller.c create mode 100644 drivers/ub/ubus/ubus_controller.c create mode 100644 drivers/ub/ubus/ubus_controller.h diff --git a/drivers/ub/ubus/Makefile b/drivers/ub/ubus/Makefile index 0d05ad501ea1..f5d3848ad60e 100644 --- a/drivers/ub/ubus/Makefile +++ b/drivers/ub/ubus/Makefile @@ -1,7 +1,7 @@ # SPDX-License-Identifier: GPL-2.0+ -obj-$(CONFIG_UB_UBUS) += ub-driver.o +obj-$(CONFIG_UB_UBUS) += ub-driver.o controller.o -ubus-y := ubus_driver.o sysfs.o +ubus-y := ubus_driver.o sysfs.o ubus_controller.o obj-$(CONFIG_UB_UBUS_BUS) += ubus.o diff --git a/drivers/ub/ubus/controller.c b/drivers/ub/ubus/controller.c new file mode 100644 index 000000000000..72a7b9a0d12d --- /dev/null +++ b/drivers/ub/ubus/controller.c @@ -0,0 +1,21 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (c) HiSilicon Technologies Co., Ltd. 2025. All rights reserved. + */ + +#include "ubus_inner.h" + +struct ub_bus_controller *ub_ubc_get(struct ub_bus_controller *ubc) +{ + if (ubc) + get_device(&ubc->dev); + return ubc; +} +EXPORT_SYMBOL_GPL(ub_ubc_get); + +void ub_ubc_put(struct ub_bus_controller *ubc) +{ + if (ubc) + put_device(&ubc->dev); +} +EXPORT_SYMBOL_GPL(ub_ubc_put); diff --git a/drivers/ub/ubus/ubus.h b/drivers/ub/ubus/ubus.h index b82922a63de4..3f455ab68c11 100644 --- a/drivers/ub/ubus/ubus.h +++ b/drivers/ub/ubus/ubus.h @@ -5,9 +5,19 @@ #ifndef __UBUS_H__ #define __UBUS_H__ +#include #include int ub_host_probe(void); void ub_host_remove(void); +struct ub_manage_subsystem_ops { + u32 vendor; + int (*controller_probe)(struct ub_bus_controller *ubc); + void (*controller_remove)(struct ub_bus_controller *ubc); +}; +int register_ub_manage_subsystem_ops(const struct ub_manage_subsystem_ops *ops); +void unregister_ub_manage_subsystem_ops(const struct ub_manage_subsystem_ops *ops); +const struct ub_manage_subsystem_ops *get_ub_manage_subsystem_ops(void); + #endif /* __UBUS_H__ */ diff --git a/drivers/ub/ubus/ubus_controller.c b/drivers/ub/ubus/ubus_controller.c new file mode 100644 index 000000000000..5bd29cbbe79d --- /dev/null +++ b/drivers/ub/ubus/ubus_controller.c @@ -0,0 +1,63 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (c) HiSilicon Technologies Co., Ltd. 2025. All rights reserved. + */ + +#define pr_fmt(fmt) "ubus controller: " fmt + +#include + +#include "ubus.h" +#include "ubus_controller.h" + +int ub_ubc_to_node(struct ub_bus_controller *ubc) +{ + return dev_to_node(&ubc->dev); +} + +void ub_bus_controllers_remove(void) +{ + const struct ub_manage_subsystem_ops *manage_subsystem_ops; + struct ub_bus_controller *ubc; + + manage_subsystem_ops = get_ub_manage_subsystem_ops(); + if (!manage_subsystem_ops || !manage_subsystem_ops->controller_remove) + return; + + list_for_each_entry_reverse(ubc, &ubc_list, node) + if (ubc->ops) + manage_subsystem_ops->controller_remove(ubc); +} + +int ub_bus_controllers_probe(void) +{ + const struct ub_manage_subsystem_ops *manage_subsystem_ops; + struct ub_bus_controller *ubc; + int ret; + + manage_subsystem_ops = get_ub_manage_subsystem_ops(); + if (!manage_subsystem_ops || !manage_subsystem_ops->controller_probe) + return -EINVAL; + + list_for_each_entry(ubc, &ubc_list, node) { + ret = manage_subsystem_ops->controller_probe(ubc); + if (ret) + goto out; + } + + return 0; +out: + ub_bus_controllers_remove(); + return ret; +} + +struct ub_bus_controller *ub_find_bus_controller_by_cna(u32 cna) +{ + struct ub_bus_controller *ubc; + + list_for_each_entry(ubc, &ubc_list, node) + if (ubc->uent->cna == cna) + return ubc; + + return NULL; +} diff --git a/drivers/ub/ubus/ubus_controller.h b/drivers/ub/ubus/ubus_controller.h new file mode 100644 index 000000000000..532307abbff0 --- /dev/null +++ b/drivers/ub/ubus/ubus_controller.h @@ -0,0 +1,17 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Copyright (c) HiSilicon Technologies Co., Ltd. 2025. All rights reserved. + */ + +#ifndef __UBUS_CONTROLLER_H__ +#define __UBUS_CONTROLLER_H__ + +struct ub_bus_controller; +struct ub_bus_controller_ops {}; + +struct ub_bus_controller *ub_find_bus_controller_by_cna(u32 cna); +void ub_bus_controllers_remove(void); +int ub_bus_controllers_probe(void); +int ub_ubc_to_node(struct ub_bus_controller *ubc); + +#endif /* __UBUS_CONTROLLER_H__ */ diff --git a/drivers/ub/ubus/ubus_driver.c b/drivers/ub/ubus/ubus_driver.c index 83f32f2a5bdf..fc39dd1cd534 100644 --- a/drivers/ub/ubus/ubus_driver.c +++ b/drivers/ub/ubus/ubus_driver.c @@ -15,6 +15,102 @@ #include "sysfs.h" #include "ubus.h" +#include "ubus_controller.h" + +static DEFINE_MUTEX(manage_subsystem_ops_mutex); +static const struct ub_manage_subsystem_ops *manage_subsystem_ops; + +int register_ub_manage_subsystem_ops(const struct ub_manage_subsystem_ops *ops) +{ + if (!ops) + return -EINVAL; + + mutex_lock(&manage_subsystem_ops_mutex); + if (!manage_subsystem_ops) { + manage_subsystem_ops = ops; + mutex_unlock(&manage_subsystem_ops_mutex); + pr_info("ub manage subsystem ops register successfully\n"); + return 0; + } + + pr_warn("ub manage subsystem ops has been registered\n"); + mutex_unlock(&manage_subsystem_ops_mutex); + + return -EINVAL; +} +EXPORT_SYMBOL_GPL(register_ub_manage_subsystem_ops); + +void unregister_ub_manage_subsystem_ops(const struct ub_manage_subsystem_ops *ops) +{ + if (!ops) + return; + + mutex_lock(&manage_subsystem_ops_mutex); + if (manage_subsystem_ops == ops) { + manage_subsystem_ops = NULL; + pr_info("ub manage subsystem ops unregister successfully\n"); + } else { + pr_warn("ub manage subsystem ops is not registered by this vendor\n"); + } + mutex_unlock(&manage_subsystem_ops_mutex); +} +EXPORT_SYMBOL_GPL(unregister_ub_manage_subsystem_ops); + +const struct ub_manage_subsystem_ops *get_ub_manage_subsystem_ops(void) +{ + return manage_subsystem_ops; +} + +struct ub_bus_controller *ub_find_bus_controller(u32 ctl_no) +{ + struct ub_bus_controller *ubc; + + list_for_each_entry(ubc, &ubc_list, node) + if (ubc->ctl_no == ctl_no) + return ubc; + + return NULL; +} +EXPORT_SYMBOL_GPL(ub_find_bus_controller); + +int ub_get_bus_controller(struct ub_entity *ubc_dev[], unsigned int max_num, + unsigned int *real_num) +{ + struct ub_bus_controller *ubc; + unsigned int ubc_num = 0; + + if (!real_num || !ubc_dev) { + pr_err("%s: input parameters invalid\n", __func__); + return -EINVAL; + } + + list_for_each_entry(ubc, &ubc_list, node) { + if (ubc_num >= max_num) { + pr_err("ubc list num over max num %u\n", max_num); + ub_put_bus_controller(ubc_dev, max_num); + return -ENOMEM; + } + + ubc_dev[ubc_num] = ub_entity_get(ubc->uent); + ubc_num++; + } + *real_num = ubc_num; + + return 0; +} +EXPORT_SYMBOL_GPL(ub_get_bus_controller); + +void ub_put_bus_controller(struct ub_entity *ubc_dev[], unsigned int num) +{ + unsigned int i; + + if (ubc_dev) { + for (i = 0; i < num; i++) + ub_entity_put(ubc_dev[i]); + memset(ubc_dev, 0, sizeof(struct ub_entity *) * num); + } +} +EXPORT_SYMBOL_GPL(ub_put_bus_controller); struct ub_entity *ub_entity_get(struct ub_entity *dev) { diff --git a/drivers/ub/ubus/ubus_inner.h b/drivers/ub/ubus/ubus_inner.h index 813cdcf70b16..86a9c4130a1a 100644 --- a/drivers/ub/ubus/ubus_inner.h +++ b/drivers/ub/ubus/ubus_inner.h @@ -7,4 +7,7 @@ #include +struct ub_bus_controller *ub_ubc_get(struct ub_bus_controller *ubc); +void ub_ubc_put(struct ub_bus_controller *ubc); + #endif /* __UBUS_INNER_H__ */ diff --git a/include/ub/ubus/ubus.h b/include/ub/ubus/ubus.h index 2e11e9f861cb..c3cc8818b32c 100644 --- a/include/ub/ubus/ubus.h +++ b/include/ub/ubus/ubus.h @@ -74,6 +74,10 @@ struct ub_entity { u16 class_code; u16 mod_vendor; /* entity's module vendor and module id */ u16 module; + unsigned int cna; + + /* entity topology info */ + struct ub_bus_controller *ubc; }; struct ub_dynids { @@ -133,7 +137,13 @@ struct ub_driver { struct ub_bus_controller { struct device dev; + struct ub_entity *uent; + + u32 ctl_no; + struct message_device *mdev; struct list_head node; + struct list_head devs; + struct ub_bus_controller_ops *ops; }; static inline struct ub_driver *to_ub_driver(struct device_driver *drv) @@ -172,6 +182,34 @@ struct ub_entity *ub_entity_get(struct ub_entity *uent); */ void ub_entity_put(struct ub_entity *uent); +/** + * ub_get_bus_controller() - Obtains the ub bus controller entity list. + * @uents: Output buffer for the UBC entities list. + * @max_num: Buffer size. + * @real_num: Real entities num. + * + * All ub bus controllers in the system are returned. Increase the reference + * counting of all entities by 1. Remember to call ub_put_bus_controller() after + * using it. + * + * Context: Any context. + * Return: 0 if success, or %-EINVAL if input parameter is NULL, + * or %-ENOMEM if buffer is too small. + */ +int ub_get_bus_controller(struct ub_entity *uents[], unsigned int max_num, + unsigned int *real_num); + +/** + * ub_put_bus_controller() - Free the ub bus controller device list. + * @uents: UBC entities list + * @num: entities num + * + * Decrement the reference counting of all entities by 1. + * + * Context: Any context. + */ +void ub_put_bus_controller(struct ub_entity *uents[], unsigned int num); + /* Proper probing supporting hot-pluggable entities */ int __ub_register_driver(struct ub_driver *drv, struct module *owner, const char *mod_name); @@ -185,6 +223,12 @@ void ub_unregister_driver(struct ub_driver *drv); static inline struct ub_entity *ub_entity_get(struct ub_entity *uent) { return NULL; } static inline void ub_entity_put(struct ub_entity *uent) {} +static inline int ub_get_bus_controller(struct ub_entity *uents[], + unsigned int max_num, + unsigned int *real_num) +{ return -ENODEV; } +static inline void +ub_put_bus_controller(struct ub_entity *uents[], unsigned int num) {} static inline int __ub_register_driver(struct ub_driver *drv, struct module *owner, const char *mod_name) -- Gitee From c998484ec5535cb44863bdd71b1af01d1d2e1d99 Mon Sep 17 00:00:00 2001 From: Jianquan Lin Date: Fri, 19 Sep 2025 10:26:30 +0800 Subject: [PATCH 06/45] ub:ubus: Support for ubus messaging communication framework driver inclusion category: feature bugzilla: https://gitee.com/openeuler/kernel/issues/ID34DG CVE: NA ----------------------------------------------------------- Support for ubus messaging communication framework, create message device and add device probe and remove messages Signed-off-by: Jianquan Lin --- drivers/ub/ubus/Makefile | 2 +- drivers/ub/ubus/msg.c | 118 +++++++++++++++++++ drivers/ub/ubus/msg.h | 244 +++++++++++++++++++++++++++++++++++++++ include/ub/ubus/ubus.h | 2 + 4 files changed, 365 insertions(+), 1 deletion(-) create mode 100644 drivers/ub/ubus/msg.c create mode 100644 drivers/ub/ubus/msg.h diff --git a/drivers/ub/ubus/Makefile b/drivers/ub/ubus/Makefile index f5d3848ad60e..8f75a1860121 100644 --- a/drivers/ub/ubus/Makefile +++ b/drivers/ub/ubus/Makefile @@ -2,6 +2,6 @@ obj-$(CONFIG_UB_UBUS) += ub-driver.o controller.o -ubus-y := ubus_driver.o sysfs.o ubus_controller.o +ubus-y := ubus_driver.o sysfs.o ubus_controller.o msg.o obj-$(CONFIG_UB_UBUS_BUS) += ubus.o diff --git a/drivers/ub/ubus/msg.c b/drivers/ub/ubus/msg.c new file mode 100644 index 000000000000..5bdad62050b1 --- /dev/null +++ b/drivers/ub/ubus/msg.c @@ -0,0 +1,118 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (c) HiSilicon Technologies Co., Ltd. 2025. All rights reserved. + */ + +#define pr_fmt(fmt) "ubus msg: " fmt + +#include +#include +#include + +#include "msg.h" + +u8 err_to_msg_rsp(int err) +{ + if (err >= 0) + return err; + + switch (err) { + case -ENOMEM: + return UB_MSG_RSP_EXEC_ENOMEM; + case -EACCES: + return UB_MSG_RSP_EXEC_EACCES; + case -EFAULT: + return UB_MSG_RSP_EXEC_EFAULT; + case -EBUSY: + return UB_MSG_RSP_EXEC_EBUSY; + case -ENODEV: + return UB_MSG_RSP_EXEC_ENODEV; + case -EINVAL: + return UB_MSG_RSP_EXEC_EINVAL; + case -ENOEXEC: + return UB_MSG_RSP_EXEC_ENOEXEC; + default: + return UB_MSG_RSP_UNKNOWN; + } +} + +static LIST_HEAD(message_device_list); +static DEFINE_SPINLOCK(message_device_lock); + +int message_device_register(struct message_device *mdev) +{ + spin_lock(&message_device_lock); + list_add_tail(&mdev->list, &message_device_list); + spin_unlock(&message_device_lock); + + return 0; +} +EXPORT_SYMBOL_GPL(message_device_register); + +void message_device_unregister(struct message_device *mdev) +{ + spin_lock(&message_device_lock); + list_del(&mdev->list); + spin_unlock(&message_device_lock); +} +EXPORT_SYMBOL_GPL(message_device_unregister); + +static struct dev_message *dev_message_get(struct ub_entity *uent) +{ + struct dev_message *msg = uent->message; + + if (msg) + return msg; + + msg = kzalloc(sizeof(*msg), GFP_KERNEL); + if (!msg) + return NULL; + + uent->message = msg; + + return msg; +} + +static void dev_message_put(struct ub_entity *uent) +{ + kfree(uent->message); + uent->message = NULL; +} + +int message_probe_device(struct ub_entity *uent) +{ + const struct message_ops *ops = uent->ubc->mdev->ops; + int ret; + + if (!dev_message_get(uent)) + return -ENOMEM; + + if (uent->message->mdev) + return 0; + + if (ops->probe_dev) { + ret = ops->probe_dev(uent); + if (ret) + goto err_probe; + } + + uent->message->mdev = uent->ubc->mdev; + + return 0; + +err_probe: + dev_message_put(uent); + return ret; +} + +void message_remove_device(struct ub_entity *uent) +{ + const struct message_ops *ops = uent->ubc->mdev->ops; + + if (!uent->message) + return; + + if (ops->remove_dev) + ops->remove_dev(uent); + dev_message_put(uent); +} diff --git a/drivers/ub/ubus/msg.h b/drivers/ub/ubus/msg.h new file mode 100644 index 000000000000..86e69abf3fb8 --- /dev/null +++ b/drivers/ub/ubus/msg.h @@ -0,0 +1,244 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Copyright (c) HiSilicon Technologies Co., Ltd. 2025. All rights reserved. + */ + +#ifndef __MSG_H__ +#define __MSG_H__ + +#include "ubus.h" + +struct ub_link_header { + u32 plen : 14; + u32 rt : 2; + u32 cfg : 4; + u32 rsvd1 : 1; + u32 vl : 4; + u32 rsvd0 : 1; + u32 crd_vl : 4; + u32 ack : 1; + u32 crd : 1; +}; +#define UB_COMPACT_LINK_CFG 6 + +struct compact_network_header { + /* DW0 */ + u32 dcna : 16; + u32 scna : 16; + /* DW1 */ +#define NTH_NLP_WITH_TPH 0 +#define NTH_NLP_WITHOUT_TPH 1 + u32 nth_nlp : 3; + u32 mgmt : 1; + u32 sl : 4; + u32 lb : 8; + u32 cc : 16; +}; + +/* message */ +#define seid_high(eid) (((eid) >> 12) & 0xff) +#define seid_low(eid) ((eid) & 0xfff) +#define eid_gen(eid_h, eid_l) ((eid_h) << 12 | (eid_l)) +#define msg_type(code) ((code) & 0x1) +#define msg_code(code) (((code) >> 1) & 0x7) +#define sub_msg_code(code) (((code) >> 4) & 0xf) +#define code_gen(msg, sub_msg, type) ((sub_msg) << 4 | ((msg) << 1 | (type))) +#define ubba_gen(ubba_h, ubba_l) ((u64)(ubba_h) << 32 | (ubba_l)) + +#define PLD_SIZE_MAX SZ_1K + +enum ub_msg_type { + MSG_REQ = 0, + MSG_RSP = 1 +}; + +enum ub_msg_code { + UB_MSG_CODE_RAS = 0, + UB_MSG_CODE_LINK = 1, + UB_MSG_CODE_CFG = 2, + UB_MSG_CODE_VDM = 3, + UB_MSG_CODE_EXCH = 4, + UB_MSG_CODE_SEC = 5, + UB_MSG_CODE_POOL = 6, + UB_MSG_CODE_MAX = 7 +}; + +#define UB_MSG_CODE_NUM 8 +#define UB_SUB_MSG_CODE_NUM 16 + +enum ub_sec_sub_msg_code { + UB_TOKEN_CHECK_CFG = 2, +}; + +enum ub_exch_sub_msg_code { + UB_OBTAIN_ENTITY_INFO = 2, + UB_LINK_NEIGHBOR_QUERY = 7, +}; + +enum ub_pool_sub_msg_code { + UB_BI_CREATE = 2, + UB_BI_DESTROY = 3, +}; + +enum ub_vdm_sub_msg_code { + UB_VENDOR_MSG = 0xf +}; + +enum ub_msg_rsp_status_code { + UB_MSG_RSP_SUCCESS = 0x00, + UB_MSG_RSP_CMD_EACCESS = 0x01, + UB_MSG_RSP_CMD_ENODEV = 0x02, + UB_MSG_RSP_CMD_CODE_ERR = 0x03, + UB_MSG_RSP_CMD_LEN_ERR = 0x04, + /* 0x05~0x1f reserved */ + UB_MSG_RSP_EXEC_ENOMEM = 0x20, + UB_MSG_RSP_EXEC_EACCES = 0x21, + UB_MSG_RSP_EXEC_EFAULT = 0x22, + UB_MSG_RSP_EXEC_EBUSY = 0x23, + UB_MSG_RSP_EXEC_EEXIST = 0x24, + UB_MSG_RSP_EXEC_ENODEV = 0x25, + UB_MSG_RSP_EXEC_EINVAL = 0x26, + UB_MSG_RSP_EXEC_ENOEXEC = 0x27, + /* 0x28~0xfe reserved */ + UB_MSG_RSP_UNKNOWN = 0xff +}; + +u8 err_to_msg_rsp(int err); + +struct msg_extended_header { + u32 plen : 12; + u32 rsvd : 4; + u32 rsp_status : 8; + union { + struct { + u8 type : 1; + u8 msg_code : 3; + u8 sub_msg_code : 4; + }; + u8 code; + }; +}; + +struct msg_pkt_header { + /* DW0 */ + struct ub_link_header ulh; + /* DW1-DW2 */ + struct compact_network_header nth; + /* DW3 */ + u32 seid_h : 8; + u32 upi : 16; +#define CTPH_NLP_UPI_40BITS_UEID 2 + u32 ctph_nlp : 4; /* tp header */ + u32 pad : 2; +#define CTPH_OPCODE_NOT_CNP 0 + u32 tp_opcode : 2; + /* DW4 */ + u32 deid : 20; + u32 seid_l : 12; + /* DW5 */ + u32 src_tassn : 16; + u32 taver : 3; + u32 tk_vld : 1; + u32 udf : 4; +#define TAH_OPCODE_MSG 0x10 + u32 ta_opcode : 8; + /* DW6 */ + u32 msgq_id : 8; + u32 rsvd0 : 24; + /* DW7 */ + struct msg_extended_header msgetah; + + /* DW8~DW11 */ + char payload[]; +}; +#define MSG_PKT_HEADER_SIZE 32 + +#define MSG_CFG_PKT_SIZE 48 /* header 32bytes, pld 16bytes */ + +/** + * struct msg_info - Collection of per message + * + * @ubc: This cfg request source ub bus controller + * @uent: This cfg request target ub_entity + * @req_packet: Request Message packet buffer + * @req_pkt_size: Size of the request packet + * @rsp_packet: Response Message packet buffer + * @rsp_pkt_size: Size of the rsponse packet + * @actual_rsp_size: Size of the actual rsponse packet + * + * vendor can define and parse structure in @req_packet and @rsp_packet + */ +struct msg_info { + struct ub_entity *ubc; + struct ub_entity *uent; + void *req_packet; + u16 req_pkt_size; + void *rsp_packet; + u16 rsp_pkt_size; + u16 actual_rsp_size; +}; +#define MSG_REQ_SIZE_OFFSET 16 + +enum message_tx_type { + MESSAGE_TX_TYPE_MSG, + MESSAGE_TX_TYPE_ENUM +}; + +#define msg_size_gen(req_size, rsp_size) ((u32)((req_size) << 16) | (rsp_size)) + +/** + * struct message_ops - message ops and capabilities + * @probe_dev: probe ub_entity to init message + * @remove_dev: remove ub_entity to uninit message + * @owner: Driver module providing these ops + */ +struct message_ops { + int (*probe_dev)(struct ub_entity *uent); + void (*remove_dev)(struct ub_entity *uent); + struct module *owner; +}; + +/** + * struct message_device - Message core representation of one message-dev + * hardware instance + * @list: Used by the message-core to keep a list of registered message-devs + * @ops: Message-ops for talking to this message-dev + * @fwnode: Firmware representation of one message-dev + */ +struct message_device { + struct list_head list; + const struct message_ops *ops; + struct fwnode_handle *fwnode; +}; + +/** + * struct dev_message - Collection of per-device Message-dev data + * + * @mdev: Message device this device is linked to + * @priv: Message Driver private data + */ +struct dev_message { + struct message_device *mdev; + void *priv; +}; + +static inline void message_info_init(struct msg_info *info, struct ub_entity *uent, + void *req, void *rsp, u32 size) +{ + if (uent) { + info->ubc = uent->ubc->uent; + info->uent = uent; + } + info->req_packet = req; + info->rsp_packet = rsp; + /* High 16bits for req size, low 16bits for rsp size */ + info->req_pkt_size = (u16)(size >> MSG_REQ_SIZE_OFFSET); + info->rsp_pkt_size = (u16)size; +} + +int message_device_register(struct message_device *mdev); +void message_device_unregister(struct message_device *mdev); +int message_probe_device(struct ub_entity *uent); +void message_remove_device(struct ub_entity *uent); + +#endif /* __MSG_H__ */ diff --git a/include/ub/ubus/ubus.h b/include/ub/ubus/ubus.h index c3cc8818b32c..b935c713e368 100644 --- a/include/ub/ubus/ubus.h +++ b/include/ub/ubus/ubus.h @@ -78,6 +78,8 @@ struct ub_entity { /* entity topology info */ struct ub_bus_controller *ubc; + + struct dev_message *message; }; struct ub_dynids { -- Gitee From 823f5e745378154fd4e5ad6611d50bb8e9983b90 Mon Sep 17 00:00:00 2001 From: Yuhao Xiang Date: Thu, 16 Oct 2025 17:16:27 +0800 Subject: [PATCH 07/45] ub:ubfi: ubfi driver create ubc device driver inclusion category: feature bugzilla: https://gitee.com/openeuler/kernel/issues/ID34DG CVE: NA ----------------------------------------------------------- ubfi driver create ubc device, and initialize some ubc device resources(irq, mmio,...). Declare these resources to the kernel. Signed-off-by: Yuhao Xiang --- drivers/ub/ubfi/Makefile | 2 + drivers/ub/ubfi/irq.c | 43 ++++ drivers/ub/ubfi/ubc.c | 418 +++++++++++++++++++++++++++++++++++++++ include/ub/ubfi/ubfi.h | 25 +++ include/ub/ubus/ubus.h | 25 +++ 5 files changed, 513 insertions(+) create mode 100644 drivers/ub/ubfi/irq.c diff --git a/drivers/ub/ubfi/Makefile b/drivers/ub/ubfi/Makefile index 55bdaf0bb24f..966355af26fd 100644 --- a/drivers/ub/ubfi/Makefile +++ b/drivers/ub/ubfi/Makefile @@ -1,4 +1,6 @@ # SPDX-License-Identifier: GPL-2.0+ +obj-y += irq.o + ubfi-y := ub_fi.o ubrt.o ubc.o obj-$(CONFIG_UB_UBFI) += ubfi.o diff --git a/drivers/ub/ubfi/irq.c b/drivers/ub/ubfi/irq.c new file mode 100644 index 000000000000..3c26ae362893 --- /dev/null +++ b/drivers/ub/ubfi/irq.c @@ -0,0 +1,43 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (c) HiSilicon Technologies Co., Ltd. 2025. All rights reserved. + */ + +#include +#include +#include +#include + +int ubrt_register_gsi(u32 hwirq, int trigger, int polarity, const char *name, + struct resource *res) +{ +#ifdef CONFIG_ACPI + int irq; + + if (!res) { + pr_err("ub register gsi, res is null\n"); + return -EINVAL; + } + + irq = acpi_register_gsi(NULL, hwirq, trigger, polarity); + if (irq <= 0) { + pr_err("could not register ub gsi hwirq %u\n", hwirq); + return -EINVAL; + } + + res->name = name; + res->start = irq; + res->end = irq; + res->flags = IORESOURCE_IRQ; +#endif + return 0; +} +EXPORT_SYMBOL_GPL(ubrt_register_gsi); + +void ubrt_unregister_gsi(u32 hwirq) +{ +#ifdef CONFIG_ACPI + acpi_unregister_gsi(hwirq); +#endif +} +EXPORT_SYMBOL_GPL(ubrt_unregister_gsi); diff --git a/drivers/ub/ubfi/ubc.c b/drivers/ub/ubfi/ubc.c index 40bd3c1f69b4..03fe0ead069a 100644 --- a/drivers/ub/ubfi/ubc.c +++ b/drivers/ub/ubfi/ubc.c @@ -14,12 +14,20 @@ #include #include #include +#include #include #include "ubrt.h" #include "ub_fi.h" #include "ubc.h" +#define UB_MSGQ_INT_TRIGGER_MASK BIT(0) +#define UB_MSGQ_INT_POLARITY_MASK BIT(1) + +#define ACPI_UBC_DEVICE_HID "HISI0541" + +#define to_ub_ubc(n) container_of(n, struct ub_bus_controller, dev) + struct list_head ubc_list; EXPORT_SYMBOL_GPL(ubc_list); @@ -40,19 +48,423 @@ EXPORT_SYMBOL_GPL(ubc_feature); static bool cluster_mode; +static acpi_status acpi_processor_ubc(acpi_handle handle, u32 lvl, + void *context, void **rv) +{ + return AE_OK; +} + static int acpi_update_ubc_msi_domain(void) { + struct ub_bus_controller *ubc; + acpi_status status; + + list_for_each_entry(ubc, &ubc_list, node) { + /* Get UB Bus Controller from DSDT */ + status = acpi_get_devices(ACPI_UBC_DEVICE_HID, + acpi_processor_ubc, + ubc, NULL); + if (ACPI_FAILURE(status)) + return -ENODEV; + } + return 0; } static int dts_update_ubc_msi_domain(void) { + struct ub_bus_controller *ubc; + struct device_node *np; + u32 ctl_no; + bool find; + int ret; + + for_each_compatible_node(np, NULL, "ub,ubc") { + ret = of_property_read_u32(np, "index", &ctl_no); + if (ret) { + pr_err("dts can't find ubc index\n"); + continue; + } + + find = false; + list_for_each_entry(ubc, &ubc_list, node) { + if (ubc->ctl_no == ctl_no) { + find = true; + break; + } + } + if (!find) { + pr_err("can't find ubc no=%u\n", ctl_no); + continue; + } + } return 0; } +static int parse_ubc_info(struct ubc_node *node, struct ub_bus_controller *ubc) +{ + ubc->attr.int_id_start = node->int_id_start; + ubc->attr.int_id_end = node->int_id_end; + ubc->attr.hpa_base = node->hpa_base; + ubc->attr.hpa_size = node->hpa_size; + ubc->attr.mem_size_limit = node->mem_size_limit; + ubc->attr.dma_cca = node->dma_cca; + ubc->attr.ummu_map = node->ummu_mapping; + ubc->attr.proximity_domain = node->proximity_domain; + ubc->attr.queue_addr = node->msg_queue_base; + ubc->attr.queue_size = node->msg_queue_size; + ubc->attr.queue_depth = node->msg_queue_depth; + ubc->attr.msg_int = node->msg_int; + ubc->attr.msg_int_attr = node->msg_int_attr; + ubc->attr.ubc_guid_low = node->ubc_guid_low; + ubc->attr.ubc_guid_high = node->ubc_guid_high; + pr_info("ubc interrupt id[0x%x-0x%x], ubc_guid[%llx-%llx], msg_int[0x%x]\n", + ubc->attr.int_id_start, ubc->attr.int_id_end, + ubc->attr.ubc_guid_high, ubc->attr.ubc_guid_low, + ubc->attr.msg_int); + + ubc->data = kzalloc(UBC_VENDOR_INFO_SIZE, GFP_KERNEL); + if (!ubc->data) + return -ENOMEM; + memcpy(ubc->data, node->vendor_info, UBC_VENDOR_INFO_SIZE); + + ubc->cluster = cluster_mode; + pr_info("ubc real cluster mode is %d\n", ubc->cluster); + INIT_LIST_HEAD(&ubc->resources); + (void)snprintf(ubc->name, sizeof(ubc->name), "UB_BUS_CTL%u", ubc->ctl_no); + pr_info("create ubc success, ubc name=%s\n", ubc->name); + return 0; +} + +static int ubc_dev_new_resource_entry(struct resource *res, + struct list_head *resources) +{ + struct resource_entry *rentry; + + rentry = resource_list_create_entry(NULL, 0); + if (!rentry) + return -ENOMEM; + + *rentry->res = *res; + rentry->offset = 0; + resource_list_add_tail(rentry, resources); + return 0; +} + +static int dts_register_irq(u32 ctl_no, int irq_type, const char *name, + struct resource *res) +{ + struct device_node *np; + u32 irq = 0, index; + int ret; + + for_each_compatible_node(np, NULL, "ub,ubc") { + ret = of_property_read_u32(np, "index", &index); + if (ret) { + pr_err("dts can't find ubc index\n"); + continue; + } + if (ctl_no != index) + continue; + + irq = irq_of_parse_and_map(np, irq_type); + if (!irq) + continue; + } + + if (!irq) { + pr_err("irq_type %d parse and map fail\n", irq_type); + return -EINVAL; + } + pr_info("irq_type[%d] register success, irq=%u\n", irq_type, irq); + + res->name = name; + res->start = irq; + res->end = irq; + res->flags = IORESOURCE_IRQ; + return 0; +} + +static void remove_ubc_resource(struct ub_bus_controller *ubc) +{ + struct resource_entry *entry; + struct resource *res; + + resource_list_for_each_entry(entry, &ubc->resources) { + res = entry->res; + if (res->parent && (res->flags & IORESOURCE_MEM)) + release_resource(res); + if ((res->flags & IORESOURCE_IRQ) + && !strcmp(res->name, "UBUS") + && !ubc->ctl_no) { + if (bios_mode == ACPI) + ubrt_unregister_gsi(ubc->attr.msg_int); + else if (bios_mode == DTS) + irq_dispose_mapping(ubc->queue_virq); + } + } + + resource_list_free(&ubc->resources); +} + +static int add_ubc_mmio_resource(struct ubc_node *node, + struct ub_bus_controller *ubc) +{ + struct resource res = {}; + int ret; + + res.start = node->hpa_base; + res.end = node->hpa_base + node->hpa_size - 1; + res.flags = IORESOURCE_MEM; + res.name = ubc->name; + + ret = ubc_dev_new_resource_entry(&res, &ubc->resources); + if (!ret) + pr_info("add %s resource success, %pR\n", res.name, &res); + + return ret; +} + +static int add_ubc_irq_resource(struct ubc_node *node, + struct ub_bus_controller *ubc) +{ + int hwirq, trigger, polarity, ret; + struct resource res = {}; + + hwirq = ubc->attr.msg_int; + trigger = !!(ubc->attr.msg_int_attr & UB_MSGQ_INT_TRIGGER_MASK); + polarity = !!(ubc->attr.msg_int_attr & UB_MSGQ_INT_POLARITY_MASK); + + if (bios_mode == ACPI) + ret = ubrt_register_gsi(hwirq, trigger, polarity, "UBUS", &res); + else if (bios_mode == DTS) + ret = dts_register_irq(ubc->ctl_no, 0, "UBUS", &res); + else + ret = -EINVAL; + + if (ret) { + pr_err("register irq fail, ret=%d\n", ret); + return ret; + } + + ret = ubc_dev_new_resource_entry(&res, &ubc->resources); + if (ret) + goto out; + + ubc->queue_virq = res.start; + + pr_info("ubc msgq irq register success\n"); + return 0; + +out: + if (bios_mode == ACPI) + ubrt_unregister_gsi(hwirq); + else if (bios_mode == DTS) + irq_dispose_mapping(res.start); + + return ret; +} + +static void ub_release_ubc_dev(struct device *dev) +{ + struct ub_bus_controller *ubc = to_ub_ubc(dev); + struct device_node *usi_np; + + pr_info("%s release ub bus controller device.\n", ubc->name); + + if (bios_mode == DTS) { + usi_np = irq_domain_get_of_node(dev->msi.domain); + if (usi_np) + of_node_put(usi_np); + } + + remove_ubc_resource(ubc); + kfree(ubc->data); + list_del(&ubc->node); + kfree(ubc); +} + +static int init_ubc(struct ub_bus_controller *ubc) +{ + struct device *dev = &ubc->dev; + int ret; + + INIT_LIST_HEAD(&ubc->devs); + device_initialize(dev); + set_dev_node(dev, pxm_to_node(ubc->attr.proximity_domain)); + + dev->release = ub_release_ubc_dev; + dev->coherent_dma_mask = GENMASK_ULL(31, 0); + dev_set_name(dev, "ub_bus_controller%u", ubc->ctl_no); + + ret = device_add(dev); + if (ret) { + pr_err("Add ub bus controller device failed.\n"); + put_device(&ubc->dev); + } + + return ret; +} + +static void ubc_validate_resources(struct list_head *resources, + unsigned long type) +{ + struct resource_entry *tmp, *entry, *entry2; + struct resource *res1, *res2, *root = NULL; + LIST_HEAD(list); + + WARN_ON((type & IORESOURCE_MEM) == 0); + root = &iomem_resource; + + list_splice_init(resources, &list); + resource_list_for_each_entry_safe(entry, tmp, &list) { + bool can_free = false; + resource_size_t end; + + res1 = entry->res; + if (!(type & res1->flags)) + goto next; + + end = min(res1->end, root->end); + if (end <= res1->start) { + pr_info("ub bus controller resources %pR (ignored, not CPU addressable)\n", + res1); + can_free = true; + goto next; + } else if (res1->end != end) { + pr_info("ub bus controller resources %pR ([%#llx-%#llx] ignored, not CPU addressable)\n", + res1, (unsigned long long)end + 1, + (unsigned long long)res1->end); + res1->end = end; + } + + resource_list_for_each_entry(entry2, resources) { + res2 = entry2->res; + if (!(type & res2->flags)) + continue; + + if (resource_overlaps(res1, res2)) { + res2->start = min(res1->start, res2->start); + res2->end = max(res1->end, res2->end); + pr_warn("ub bus controller resources expanded to %pR; %pR ignored\n", + res2, res1); + can_free = true; + goto next; + } + } + +next: + resource_list_del(entry); + if (can_free) + resource_list_free_entry(entry); + else + resource_list_add_tail(entry, resources); + } +} + +static void ubc_device_add_resources(struct list_head *resources) +{ + struct resource *res, *root = NULL; + struct resource_entry *entry, *tmp; + int ret; + + resource_list_for_each_entry_safe(entry, tmp, resources) { + res = entry->res; + if (!(res->flags & IORESOURCE_MEM)) + continue; + + root = &iomem_resource; + if (res == root) + continue; + + ret = insert_resource(root, res); + if (ret) { + pr_warn("conflict, ignoring controller resources %pR\n", + res); + resource_list_destroy_entry(entry); + } + } +} + +static void ubc_declare_resources(struct ub_bus_controller *ubc) +{ + struct resource_entry *window; + resource_size_t offset; + char addr[SZ_64], *fmt; + struct resource *res; + + /* Show ub bus controller's resources */ + resource_list_for_each_entry(window, &ubc->resources) { + offset = window->offset; + res = window->res; + if (offset) { + fmt = " (bus address [0x%#010llx-0x%#010llx])"; + snprintf(addr, sizeof(addr), fmt, + (unsigned long long)(res->start - offset), + (unsigned long long)(res->end - offset)); + } else { + addr[0] = '\0'; + } + + pr_info("ubc resource %pR%s\n", res, addr); + } +} + +static void ubc_check_and_add_resources(struct ub_bus_controller *ubc) +{ + struct resource_entry *entry, *tmp; + + resource_list_for_each_entry_safe(entry, tmp, &ubc->resources) + if (entry->res->flags & IORESOURCE_DISABLED) + resource_list_destroy_entry(entry); + + ubc_validate_resources(&ubc->resources, IORESOURCE_MEM); + + /* Insert resources into kernel */ + ubc_device_add_resources(&ubc->resources); + + ubc_declare_resources(ubc); +} + static int create_ubc(struct ubc_node *node, u32 ctl_no) { + struct ub_bus_controller *ubc; + int ret; + + ubc = kzalloc(sizeof(*ubc), GFP_KERNEL); + if (!ubc) + return -ENOMEM; + ubc->ctl_no = ctl_no; + + ret = parse_ubc_info(node, ubc); + if (ret) + goto free_ubc; + + ret = add_ubc_mmio_resource(node, ubc); + if (ret) + goto free_vendor; + + ret = add_ubc_irq_resource(node, ubc); + if (ret) + goto free_resource; + + /* after init_ubc, ubc resources will be released in the dev->release */ + ret = init_ubc(ubc); + if (ret) + return ret; + + ubc_check_and_add_resources(ubc); + list_add_tail(&ubc->node, &ubc_list); + return 0; + +free_resource: + remove_ubc_resource(ubc); +free_vendor: + kfree(ubc->data); +free_ubc: + kfree(ubc); + return ret; } static int parse_ubc_table(void *info_node) @@ -99,6 +511,12 @@ static int parse_ubc_table(void *info_node) static void ub_destroy_bus_controllers(void) { + struct ub_bus_controller *ubc, *tmp; + + list_for_each_entry_safe_reverse(ubc, tmp, &ubc_list, node) + device_unregister(&ubc->dev); + + pr_info("ubc destroy success\n"); } void destroy_ubc(void) diff --git a/include/ub/ubfi/ubfi.h b/include/ub/ubfi/ubfi.h index 9131e1da0888..cdaaa1e29bc3 100644 --- a/include/ub/ubfi/ubfi.h +++ b/include/ub/ubfi/ubfi.h @@ -6,13 +6,38 @@ #ifndef _UB_UBFI_UBFI_H_ #define _UB_UBFI_UBFI_H_ +#include +#include +#include +#include #include +/** + * ubrt_register_gsi() - Registering UBC's interrupt into the kernel via ACPI + * @hwirq: GSI IRQ number reported by BIOS + * @trigger: trigger type of the GSI number to be mapped + * @polarity: polarity of the GSI to be mapped + * @name: GSI IRQ name + * @res: Record the soft interrupt number obtained after registration into res + * + * Return: 0 if success or other if failed + */ +int ubrt_register_gsi(u32 hwirq, int trigger, int polarity, const char *name, + struct resource *res); + +/** + * ubrt_unregister_gsi() - Unregistering UBC's interrupt into the kernel via ACPI + * @hwirq: GSI IRQ number reported by BIOS + */ +void ubrt_unregister_gsi(u32 hwirq); + +#if IS_ENABLED(CONFIG_UB_UBFI) extern struct list_head ubc_list; extern u32 ubc_eid_start; extern u32 ubc_eid_end; extern u32 ubc_cna_start; extern u32 ubc_cna_end; extern u8 ubc_feature; +#endif /* CONFIG_UB_UBFI */ #endif /* _UB_UBFI_UBFI_H_ */ diff --git a/include/ub/ubus/ubus.h b/include/ub/ubus/ubus.h index b935c713e368..55013d6f1ac8 100644 --- a/include/ub/ubus/ubus.h +++ b/include/ub/ubus/ubus.h @@ -137,15 +137,40 @@ struct ub_driver { struct ub_dynids dynids; }; +struct ubc_common_attr { + u32 int_id_start; + u32 int_id_end; + u64 hpa_base; + u64 hpa_size; + u8 mem_size_limit; + u8 dma_cca; + u16 ummu_map; + u16 proximity_domain; + u64 queue_addr; + u64 queue_size; + u16 queue_depth; + u16 msg_int; + u8 msg_int_attr; + u64 ubc_guid_low; + u64 ubc_guid_high; +}; + struct ub_bus_controller { struct device dev; struct ub_entity *uent; + struct ubc_common_attr attr; + u32 queue_virq; + char name[16]; u32 ctl_no; struct message_device *mdev; + struct list_head resources; struct list_head node; struct list_head devs; struct ub_bus_controller_ops *ops; + bool cluster; + + void *data; }; static inline struct ub_driver *to_ub_driver(struct device_driver *drv) -- Gitee From 73425e2ce0952627659b471f07cd79a6a8833c67 Mon Sep 17 00:00:00 2001 From: Junlong Zheng Date: Fri, 19 Sep 2025 09:51:10 +0800 Subject: [PATCH 08/45] ub:ubus: Support for UB Bus DMA Configuration Function driver inclusion category: feature bugzilla: https://gitee.com/openeuler/kernel/issues/ID34DG CVE: NA ----------------------------------------------------------- Support for UB bus dma_configure and dma_cleanup callbacks, to enable UB bus device DMA data path. Signed-off-by: Junlong Zheng --- drivers/ub/ubus/ub-driver.c | 168 ++++++++++++++++++++++++++++++++++++ include/ub/ubus/ubus.h | 15 ++++ 2 files changed, 183 insertions(+) diff --git a/drivers/ub/ubus/ub-driver.c b/drivers/ub/ubus/ub-driver.c index 1922b5062327..e17cec991445 100644 --- a/drivers/ub/ubus/ub-driver.c +++ b/drivers/ub/ubus/ub-driver.c @@ -4,11 +4,179 @@ */ #include +#include +#include +#include #include "ubus_inner.h" +static const struct iommu_ops *ub_bus_type_iommu_ops; +void ub_bus_type_iommu_ops_set(const struct iommu_ops *ops) +{ + ub_bus_type_iommu_ops = ops; +} +EXPORT_SYMBOL_GPL(ub_bus_type_iommu_ops_set); + +const struct iommu_ops *ub_bus_type_iommu_ops_get(void) +{ + return ub_bus_type_iommu_ops; +} +EXPORT_SYMBOL_GPL(ub_bus_type_iommu_ops_get); + +static void ubct_dma_setup(struct device *dev) +{ + struct ub_entity *uent = to_ub_entity(dev); + u64 end, mask, size; + u8 addr_limit; + + addr_limit = uent->ubc->attr.mem_size_limit; + if (!addr_limit) { + pr_warn(FW_BUG "ub bus controller missing memory address limit\n"); + return; + } + + size = (addr_limit >= SZ_64) ? U64_MAX : (1ULL << addr_limit); + end = size - 1; + mask = DMA_BIT_MASK(ilog2(end) + 1); + dev->bus_dma_limit = end; + dev->coherent_dma_mask = min(dev->coherent_dma_mask, mask); + *dev->dma_mask = min(*dev->dma_mask, mask); +} + +static inline const struct iommu_ops *ubct_iommu_fwspec_ops(struct device *dev) +{ + struct iommu_fwspec *fwspec = dev_iommu_fwspec_get(dev); + + return fwspec ? fwspec->ops : NULL; +} + +static int ubct_iommu_fwspec_init(struct device *dev, u32 id, + struct fwnode_handle *fwnode, + const struct iommu_ops *ops) +{ + int ret = iommu_fwspec_init(dev, fwnode, ops); + + if (ret) + return ret; + + return iommu_fwspec_add_ids(dev, &id, 1); +} + +static int ubct_iommu_configure(struct device *dev) +{ + if (!ub_bus_type_iommu_ops) { + dev_err(dev, "ubus's iommu_ops not ready\n"); + return -ENODEV; + } + + /* input id is 0, fwnode is NULL, ummu not care */ + return ubct_iommu_fwspec_init(dev, 0, NULL, ub_bus_type_iommu_ops); +} + +static const struct iommu_ops *handle_iommu_configure_error(struct device *dev, int err) +{ + if (err == -EPROBE_DEFER) + return ERR_PTR(-EPROBE_DEFER); + + dev_warn(dev, "Adding to IOMMU failed: %d\n", err); + return NULL; +} + +static const struct iommu_ops *ub_hybrid_iommu_configure(struct device *dev) +{ + const struct iommu_ops *ops; + int err; + + ops = ubct_iommu_fwspec_ops(dev); + if (ops) + return ops; + + err = ubct_iommu_configure(dev); + if (err) { + if (err != -EPROBE_DEFER) + dev_err(dev, "viot iommu configure: %d\n", err); + + return handle_iommu_configure_error(dev, err); + } + + if (dev->bus && !device_iommu_mapped(dev)) { + err = iommu_probe_device(dev); + if (err) + return handle_iommu_configure_error(dev, err); + } + + return ubct_iommu_fwspec_ops(dev); +} + +static int ub_hybrid_dma_configure(struct device *dev, enum dev_dma_attr attr) +{ + const struct iommu_ops *iommu; + + if (attr == DEV_DMA_NOT_SUPPORTED) { + set_dma_ops(dev, &dma_dummy_ops); + return 0; + } + + ubct_dma_setup(dev); + + iommu = ub_hybrid_iommu_configure(dev); + if (PTR_ERR(iommu) == -EPROBE_DEFER) + return -EPROBE_DEFER; + + arch_setup_dma_ops(dev, 0, U64_MAX, attr == DEV_DMA_COHERENT); + + return 0; +} + +/* Translate from FW */ +static enum dev_dma_attr ub_dma_attr_trans(u16 dma_attr) +{ + switch (dma_attr) { + case 0: + return DEV_DMA_NON_COHERENT; + case 1: + return DEV_DMA_COHERENT; + default: + return DEV_DMA_NOT_SUPPORTED; + } +} + +static int ub_dma_configure(struct device *dev) +{ + struct ub_driver *udrv = to_ub_driver(dev->driver); + struct ub_entity *uent = to_ub_entity(dev); + struct ub_bus_controller *ubc; + int ret; + + ubc = ub_ubc_get(uent->ubc); + + ret = ub_hybrid_dma_configure(dev, + ub_dma_attr_trans(ubc->attr.dma_cca)); + if (!ret && !udrv->driver_managed_dma) { + ret = iommu_device_use_default_domain(dev); + if (ret) + arch_teardown_dma_ops(dev); + } + + ub_ubc_put(ubc); + dev_info(dev, "dma_configure ret = %d\n", ret); + return ret; +} + +static void ub_dma_cleanup(struct device *dev) +{ + struct ub_driver *driver = to_ub_driver(dev->driver); + + if (!driver->driver_managed_dma) + iommu_device_unuse_default_domain(dev); + + dev_dbg(dev, "dma_cleanup\n"); +} + struct bus_type ub_bus_type = { .name = "ub", + .dma_configure = ub_dma_configure, + .dma_cleanup = ub_dma_cleanup, }; EXPORT_SYMBOL_GPL(ub_bus_type); diff --git a/include/ub/ubus/ubus.h b/include/ub/ubus/ubus.h index 55013d6f1ac8..836727f0fc77 100644 --- a/include/ub/ubus/ubus.h +++ b/include/ub/ubus/ubus.h @@ -8,6 +8,7 @@ #include #include +#include #include #include #include @@ -121,6 +122,13 @@ struct ub_dynids { * created once it is bound to the driver. * @driver: Driver model structure. * @dynids: List of dynamically added device IDs. + * @driver_managed_dma: Device driver doesn't use kernel DMA API for DMA. + * For most device drivers, no need to care about this flag + * as long as all DMAs are handled through the kernel DMA API. + * For some special ones, for example VFIO drivers, they know + * how to manage the DMA themselves and set this flag so that + * the IOMMU layer will allow them to setup and manage their + * own I/O address space. */ struct ub_driver { struct list_head node; @@ -135,6 +143,7 @@ struct ub_driver { const struct attribute_group **dev_groups; struct device_driver driver; struct ub_dynids dynids; + bool driver_managed_dma; }; struct ubc_common_attr { @@ -192,6 +201,9 @@ static inline const char *ub_name(const struct ub_entity *pue) extern struct bus_type ub_bus_type; #define dev_is_ub(d) ((d)->bus == &ub_bus_type) +void ub_bus_type_iommu_ops_set(const struct iommu_ops *ops); +const struct iommu_ops *ub_bus_type_iommu_ops_get(void); + /** * ub_entity_get() - Atomically increment the reference count for the entity. * @uent: UB entity pointer. @@ -256,6 +268,9 @@ static inline int ub_get_bus_controller(struct ub_entity *uents[], { return -ENODEV; } static inline void ub_put_bus_controller(struct ub_entity *uents[], unsigned int num) {} +static inline void ub_bus_type_iommu_ops_set(const struct iommu_ops *ops) {} +static inline const struct iommu_ops *ub_bus_type_iommu_ops_get(void) +{ return NULL; } static inline int __ub_register_driver(struct ub_driver *drv, struct module *owner, const char *mod_name) -- Gitee From 33afa73423699c6c09d7aa080cf07dbacf40a12d Mon Sep 17 00:00:00 2001 From: Jianquan Lin Date: Fri, 19 Sep 2025 11:21:26 +0800 Subject: [PATCH 09/45] ub:ubus: Add Ubus setting configuration space function driver inclusion category: feature bugzilla: https://gitee.com/openeuler/kernel/issues/ID34DG CVE: NA ----------------------------------------------------------- Add Ubus setting configuration space function, including basic function Signed-off-by: Jianquan Lin --- drivers/ub/ubus/Makefile | 4 +- drivers/ub/ubus/config.c | 103 +++++++++++++++++++++++++++++++++ drivers/ub/ubus/msg.c | 18 ++++++ drivers/ub/ubus/msg.h | 13 +++++ drivers/ub/ubus/ubus.h | 2 + drivers/ub/ubus/ubus_config.c | 105 ++++++++++++++++++++++++++++++++++ drivers/ub/ubus/ubus_config.h | 9 +++ drivers/ub/ubus/ubus_driver.c | 2 + drivers/ub/ubus/ubus_inner.h | 10 ++++ include/ub/ubus/ubus.h | 3 + 10 files changed, 267 insertions(+), 2 deletions(-) create mode 100644 drivers/ub/ubus/config.c create mode 100644 drivers/ub/ubus/ubus_config.c create mode 100644 drivers/ub/ubus/ubus_config.h diff --git a/drivers/ub/ubus/Makefile b/drivers/ub/ubus/Makefile index 8f75a1860121..fb5edf76f9eb 100644 --- a/drivers/ub/ubus/Makefile +++ b/drivers/ub/ubus/Makefile @@ -1,7 +1,7 @@ # SPDX-License-Identifier: GPL-2.0+ -obj-$(CONFIG_UB_UBUS) += ub-driver.o controller.o +obj-$(CONFIG_UB_UBUS) += ub-driver.o controller.o config.o -ubus-y := ubus_driver.o sysfs.o ubus_controller.o msg.o +ubus-y := ubus_driver.o sysfs.o ubus_controller.o msg.o ubus_config.o obj-$(CONFIG_UB_UBUS_BUS) += ubus.o diff --git a/drivers/ub/ubus/config.c b/drivers/ub/ubus/config.c new file mode 100644 index 000000000000..721a9c62027b --- /dev/null +++ b/drivers/ub/ubus/config.c @@ -0,0 +1,103 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (c) HiSilicon Technologies Co., Ltd. 2025. All rights reserved. + */ + +#include "ubus_inner.h" + +static const struct cfg_ops { + read_byte_f cfg_read_byte; + read_word_f cfg_read_word; + read_dword_f cfg_read_dword; + write_byte_f cfg_write_byte; + write_word_f cfg_write_word; + write_dword_f cfg_write_dword; +} cfg_ops_t; + +static struct cfg_ops ub_cfg_ops = {}; + +int register_ub_cfg_read_ops(read_byte_f rb, read_word_f rw, read_dword_f rdw) +{ + if (rb && rw && rdw) { + ub_cfg_ops.cfg_read_byte = rb; + ub_cfg_ops.cfg_read_word = rw; + ub_cfg_ops.cfg_read_dword = rdw; + return 0; + } + + return -EINVAL; +} +EXPORT_SYMBOL_GPL(register_ub_cfg_read_ops); + +int register_ub_cfg_write_ops(write_byte_f wb, write_word_f ww, write_dword_f wdw) +{ + if (wb && ww && wdw) { + ub_cfg_ops.cfg_write_byte = wb; + ub_cfg_ops.cfg_write_word = ww; + ub_cfg_ops.cfg_write_dword = wdw; + return 0; + } + + return -EINVAL; +} +EXPORT_SYMBOL_GPL(register_ub_cfg_write_ops); + +void unregister_ub_cfg_ops(void) +{ + memset(&ub_cfg_ops, 0, sizeof(struct cfg_ops)); +} +EXPORT_SYMBOL_GPL(unregister_ub_cfg_ops); + +int ub_cfg_read_byte(struct ub_entity *uent, u64 pos, u8 *val) +{ + if (!ub_cfg_ops.cfg_read_byte) + return -ENODEV; + + return ub_cfg_ops.cfg_read_byte(uent, pos, val); +} +EXPORT_SYMBOL_GPL(ub_cfg_read_byte); + +int ub_cfg_read_word(struct ub_entity *uent, u64 pos, u16 *val) +{ + if (!ub_cfg_ops.cfg_read_word) + return -ENODEV; + + return ub_cfg_ops.cfg_read_word(uent, pos, val); +} +EXPORT_SYMBOL_GPL(ub_cfg_read_word); + +int ub_cfg_read_dword(struct ub_entity *uent, u64 pos, u32 *val) +{ + if (!ub_cfg_ops.cfg_read_dword) + return -ENODEV; + + return ub_cfg_ops.cfg_read_dword(uent, pos, val); +} +EXPORT_SYMBOL_GPL(ub_cfg_read_dword); + +int ub_cfg_write_byte(struct ub_entity *uent, u64 pos, u8 val) +{ + if (!ub_cfg_ops.cfg_write_byte) + return -ENODEV; + + return ub_cfg_ops.cfg_write_byte(uent, pos, val); +} +EXPORT_SYMBOL_GPL(ub_cfg_write_byte); + +int ub_cfg_write_word(struct ub_entity *uent, u64 pos, u16 val) +{ + if (!ub_cfg_ops.cfg_write_word) + return -ENODEV; + + return ub_cfg_ops.cfg_write_word(uent, pos, val); +} +EXPORT_SYMBOL_GPL(ub_cfg_write_word); + +int ub_cfg_write_dword(struct ub_entity *uent, u64 pos, u32 val) +{ + if (!ub_cfg_ops.cfg_write_dword) + return -ENODEV; + + return ub_cfg_ops.cfg_write_dword(uent, pos, val); +} +EXPORT_SYMBOL_GPL(ub_cfg_write_dword); diff --git a/drivers/ub/ubus/msg.c b/drivers/ub/ubus/msg.c index 5bdad62050b1..c7be05f350bb 100644 --- a/drivers/ub/ubus/msg.c +++ b/drivers/ub/ubus/msg.c @@ -116,3 +116,21 @@ void message_remove_device(struct ub_entity *uent) ops->remove_dev(uent); dev_message_put(uent); } + +int message_sync_request(struct message_device *mdev, struct msg_info *info, + u8 code) +{ + if (mdev->ops->sync_request) + return mdev->ops->sync_request(mdev, info, code); + + return -ENOTTY; +} + +int message_send(struct message_device *mdev, struct msg_info *info, + u8 code) +{ + if (mdev->ops->send) + return mdev->ops->send(mdev, info, code); + + return -ENOTTY; +} diff --git a/drivers/ub/ubus/msg.h b/drivers/ub/ubus/msg.h index 86e69abf3fb8..fd1d9df7d20c 100644 --- a/drivers/ub/ubus/msg.h +++ b/drivers/ub/ubus/msg.h @@ -190,11 +190,17 @@ enum message_tx_type { * struct message_ops - message ops and capabilities * @probe_dev: probe ub_entity to init message * @remove_dev: remove ub_entity to uninit message + * @sync_request: send message to target ub_entity and wait response + * @send: send message to target ub_entity but not wait response * @owner: Driver module providing these ops */ struct message_ops { int (*probe_dev)(struct ub_entity *uent); void (*remove_dev)(struct ub_entity *uent); + int (*sync_request)(struct message_device *mdev, struct msg_info *info, + u8 code); + int (*send)(struct message_device *mdev, struct msg_info *info, + u8 code); struct module *owner; }; @@ -236,9 +242,16 @@ static inline void message_info_init(struct msg_info *info, struct ub_entity *ue info->rsp_pkt_size = (u16)size; } +void ub_msg_pkt_header_init(struct msg_pkt_header *header, struct ub_entity *uent, + u16 plen, u8 code, bool flag); + int message_device_register(struct message_device *mdev); void message_device_unregister(struct message_device *mdev); int message_probe_device(struct ub_entity *uent); void message_remove_device(struct ub_entity *uent); +int message_sync_request(struct message_device *mdev, struct msg_info *info, + u8 code); +int message_send(struct message_device *mdev, struct msg_info *info, + u8 code); #endif /* __MSG_H__ */ diff --git a/drivers/ub/ubus/ubus.h b/drivers/ub/ubus/ubus.h index 3f455ab68c11..51828b3c9806 100644 --- a/drivers/ub/ubus/ubus.h +++ b/drivers/ub/ubus/ubus.h @@ -8,6 +8,8 @@ #include #include +#define UB_CP_UPI 0x7FFF + int ub_host_probe(void); void ub_host_remove(void); diff --git a/drivers/ub/ubus/ubus_config.c b/drivers/ub/ubus/ubus_config.c new file mode 100644 index 000000000000..698e465607aa --- /dev/null +++ b/drivers/ub/ubus/ubus_config.c @@ -0,0 +1,105 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (c) HiSilicon Technologies Co., Ltd. 2025. All rights reserved. + */ + +#define pr_fmt(fmt) "ubus config: " fmt + +#include "ubus.h" +#include "msg.h" +#include "ubus_config.h" +#include "ubus_inner.h" + +struct cfg_msg_pld_req { + /* DW0 */ + u32 rsvd0 : 4; + u32 byte_enable : 4; + u32 rsvd1 : 8; + u32 entity_idx : 16; + /* DW1 */ + u32 req_addr; + /* DW2 */ + u32 rsvd2; + /* DW3 */ + u32 write_data; +}; + +struct cfg_msg_pld_rsp { + /* DW0 */ + u32 read_data; + /* DW1 */ + u32 rsvd1; + /* DW2 */ + u32 rsvd2; + /* DW3 */ + u32 rsvd3; +}; + +struct cfg_msg_req_pkt { + struct msg_pkt_header header; + struct cfg_msg_pld_req req_payload; +}; + +struct cfg_msg_rsp_pkt { + struct msg_pkt_header header; + struct cfg_msg_pld_rsp rsp_payload; +}; + +enum ub_cfg_sub_msg_code { + UB_CFG0_READ = 0, + UB_CFG0_WRITE = 1, + UB_CFG1_READ = 2, + UB_CFG1_WRITE = 3 +}; + +#define CFG_MSG_PLD_SIZE 16 + +struct ub_cfg { + u8 size; + u8 pos_mask; + u8 byte_mask; +}; + +static void ub_msg_extended_header_init(struct msg_extended_header *msgetah, + u16 plen, u8 code) +{ + msgetah->plen = plen; + msgetah->msg_code = msg_code(code); + msgetah->type = msg_type(code); + msgetah->sub_msg_code = sub_msg_code(code); +} + +void ub_msg_pkt_header_init(struct msg_pkt_header *header, struct ub_entity *uent, + u16 plen, u8 code, bool flag) +{ + struct compact_network_header *cnth = &header->nth; + struct ub_link_header *ulh = &header->ulh; + struct ub_entity *ubc_uent = uent->ubc->uent; + u32 seid = ubc_uent->eid; + u16 scna = (u16)ubc_uent->cna; + u32 deid = uent->eid; + u16 dcna = (u16)uent->cna; + + if (flag) { + dcna = scna; + deid = seid; + } + + ulh->cfg = UB_COMPACT_LINK_CFG; + + cnth->nth_nlp = NTH_NLP_WITH_TPH; + cnth->scna = scna; + cnth->dcna = dcna; + + header->ctph_nlp = CTPH_NLP_UPI_40BITS_UEID; + header->tp_opcode = CTPH_OPCODE_NOT_CNP; + header->pad = 0; + header->upi = uent->ubc->cluster ? uent->upi : UB_CP_UPI; + header->seid_h = seid_high(seid); + header->seid_l = seid_low(seid); + header->deid = deid; + header->ta_opcode = TAH_OPCODE_MSG; + + ub_msg_extended_header_init(&header->msgetah, plen, code); +} +EXPORT_SYMBOL_GPL(ub_msg_pkt_header_init); diff --git a/drivers/ub/ubus/ubus_config.h b/drivers/ub/ubus/ubus_config.h new file mode 100644 index 000000000000..b7f733ed6549 --- /dev/null +++ b/drivers/ub/ubus/ubus_config.h @@ -0,0 +1,9 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Copyright (c) HiSilicon Technologies Co., Ltd. 2025. All rights reserved. + */ + +#ifndef __UBUS_CONFIG_H__ +#define __UBUS_CONFIG_H__ + +#endif /* __UBUS_CONFIG_H__ */ diff --git a/drivers/ub/ubus/ubus_driver.c b/drivers/ub/ubus/ubus_driver.c index fc39dd1cd534..a14cadf75725 100644 --- a/drivers/ub/ubus/ubus_driver.c +++ b/drivers/ub/ubus/ubus_driver.c @@ -15,7 +15,9 @@ #include "sysfs.h" #include "ubus.h" +#include "ubus_config.h" #include "ubus_controller.h" +#include "ubus_inner.h" static DEFINE_MUTEX(manage_subsystem_ops_mutex); static const struct ub_manage_subsystem_ops *manage_subsystem_ops; diff --git a/drivers/ub/ubus/ubus_inner.h b/drivers/ub/ubus/ubus_inner.h index 86a9c4130a1a..dd576a6c41a3 100644 --- a/drivers/ub/ubus/ubus_inner.h +++ b/drivers/ub/ubus/ubus_inner.h @@ -7,6 +7,16 @@ #include +typedef int (*read_byte_f)(struct ub_entity *uent, u64 pos, u8 *val); +typedef int (*read_word_f)(struct ub_entity *uent, u64 pos, u16 *val); +typedef int (*read_dword_f)(struct ub_entity *uent, u64 pos, u32 *val); +typedef int (*write_byte_f)(struct ub_entity *uent, u64 pos, u8 val); +typedef int (*write_word_f)(struct ub_entity *uent, u64 pos, u16 val); +typedef int (*write_dword_f)(struct ub_entity *uent, u64 pos, u32 val); + +int register_ub_cfg_read_ops(read_byte_f rb, read_word_f rw, read_dword_f rdw); +int register_ub_cfg_write_ops(write_byte_f wb, write_word_f ww, write_dword_f wdw); +void unregister_ub_cfg_ops(void); struct ub_bus_controller *ub_ubc_get(struct ub_bus_controller *ubc); void ub_ubc_put(struct ub_bus_controller *ubc); diff --git a/include/ub/ubus/ubus.h b/include/ub/ubus/ubus.h index 836727f0fc77..779569fd46c2 100644 --- a/include/ub/ubus/ubus.h +++ b/include/ub/ubus/ubus.h @@ -76,11 +76,14 @@ struct ub_entity { u16 mod_vendor; /* entity's module vendor and module id */ u16 module; unsigned int cna; + unsigned int eid; /* entity topology info */ struct ub_bus_controller *ubc; struct dev_message *message; + + u16 upi; }; struct ub_dynids { -- Gitee From eb70c25f8d9d28c91aac784e4f59a5f977a14a0b Mon Sep 17 00:00:00 2001 From: Junlong Zheng Date: Fri, 19 Sep 2025 10:24:55 +0800 Subject: [PATCH 10/45] ub:ubus: Support for UB Bus Port Management Framework driver inclusion category: feature bugzilla: https://gitee.com/openeuler/kernel/issues/ID34DG CVE: NA ----------------------------------------------------------- Provide basic interface capabilities for UB port application, release, initialization, and connection. Signed-off-by: Junlong Zheng --- drivers/ub/ubus/Makefile | 2 +- drivers/ub/ubus/port.c | 199 +++++++++++++++++++++++++++++++++++++++ drivers/ub/ubus/port.h | 21 +++++ include/ub/ubus/ubus.h | 26 +++++ 4 files changed, 247 insertions(+), 1 deletion(-) create mode 100644 drivers/ub/ubus/port.c create mode 100644 drivers/ub/ubus/port.h diff --git a/drivers/ub/ubus/Makefile b/drivers/ub/ubus/Makefile index fb5edf76f9eb..90ab6925210e 100644 --- a/drivers/ub/ubus/Makefile +++ b/drivers/ub/ubus/Makefile @@ -2,6 +2,6 @@ obj-$(CONFIG_UB_UBUS) += ub-driver.o controller.o config.o -ubus-y := ubus_driver.o sysfs.o ubus_controller.o msg.o ubus_config.o +ubus-y := ubus_driver.o sysfs.o ubus_controller.o msg.o ubus_config.o port.o obj-$(CONFIG_UB_UBUS_BUS) += ubus.o diff --git a/drivers/ub/ubus/port.c b/drivers/ub/ubus/port.c new file mode 100644 index 000000000000..6b70c229463b --- /dev/null +++ b/drivers/ub/ubus/port.c @@ -0,0 +1,199 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (c) HiSilicon Technologies Co., Ltd. 2025. All rights reserved. + */ + +#define pr_fmt(fmt) "ubus port: " fmt + +#include "ubus.h" +#include "port.h" + +struct ub_port_attribute { + struct attribute attr; + ssize_t (*show)(struct ub_port *port, char *buf); + ssize_t (*store)(struct ub_port *port, const char *buf, size_t count); +}; + +#define to_ub_port(o) container_of(o, struct ub_port, kobj) +#define to_ub_port_attr(a) container_of(a, struct ub_port_attribute, attr) + +static const struct attribute_group *ub_port_groups[] = { + NULL +}; + +static ssize_t ub_port_attr_show(struct kobject *kobj, struct attribute *attr, + char *buf) +{ + struct ub_port_attribute *attribute = to_ub_port_attr(attr); + struct ub_port *port = to_ub_port(kobj); + + if (!attribute->show) + return -EIO; + + return attribute->show(port, buf); +} + +static ssize_t ub_port_attr_store(struct kobject *kobj, struct attribute *attr, + const char *buf, size_t count) +{ + struct ub_port_attribute *attribute = to_ub_port_attr(attr); + struct ub_port *port = to_ub_port(kobj); + + if (!attribute->store) + return -EIO; + + return attribute->store(port, buf, count); +} + +static const struct sysfs_ops ub_port_sysfs_ops = { + .show = ub_port_attr_show, + .store = ub_port_attr_store, +}; + +static const struct kobj_type ub_port_ktype = { + .sysfs_ops = &ub_port_sysfs_ops, + .default_groups = ub_port_groups, +}; + +void ub_port_disconnect(struct ub_port *port) +{ + struct ub_port *r_port; + + if (!port || !port->r_uent) + return; + + r_port = port->r_uent->ports + port->r_index; + r_port->r_uent = NULL; + r_port->r_guid = guid_null; + + port->r_uent = NULL; + port->r_guid = guid_null; +} + +/* connect two ports, caller should make sure that the ports match */ +void ub_port_connect(struct ub_port *port, struct ub_port *r_port) +{ + if (!port || !r_port) + return; + + port->r_index = r_port->index; + port->r_uent = r_port->uent; + r_port->r_index = port->index; + r_port->r_uent = port->uent; +} + +bool ub_check_and_connect(struct ub_port *port, struct ub_entity *r_uent) +{ + struct ub_port *r_port; + + if (!port || !r_uent) + return false; + + if (port->r_index >= r_uent->port_nums) { + pr_err("port%u should connect to port%u, but remote device has only %u ports\n", + port->index, port->r_index, r_uent->port_nums); + return false; + } + + r_port = r_uent->ports + port->r_index; + + if (r_port->r_uent) { + pr_err("port%u should connect to port%u, which is already connected\n", + port->index, port->r_index); + return false; + } + + ub_port_connect(port, r_port); + + return true; +} + +static int ub_port_config(struct ub_port *port) +{ + return 0; +} + +int ub_ports_add(struct ub_entity *uent) +{ + struct ub_port *port; + int ret; + + if (!uent) + return -EINVAL; + + for_each_uent_port(port, uent) { + ret = ub_port_config(port); + if (ret) { + ub_err(uent, + "config port%u failed, stop adding ports\n", + port->index); + return ret; + } + + ret = kobject_add(&port->kobj, &uent->dev.kobj, "port%u", + port->index); + if (ret) { + ub_warn(uent, "cannot add port%u, stop adding ports\n", + port->index); + return ret; + } + } + + return 0; +} + +void ub_ports_del(struct ub_entity *uent) +{ + struct ub_port *port; + + if (!uent) + return; + + for_each_uent_port(port, uent) + kobject_put(&port->kobj); +} + +static void ub_port_init(struct ub_entity *uent, struct ub_port *port) +{ + port->uent = uent; + port->type = PHYSICAL; + port->cna = 0; + port->r_uent = NULL; + port->r_index = 0; + port->r_guid = guid_null; + bitmap_zero(port->cna_maps, UB_MAX_CNA_NUM); + bitmap_zero(port->cap_map, UB_PORT_CAP_NUM); + kobject_init(&port->kobj, &ub_port_ktype); +} + +int ub_ports_setup(struct ub_entity *uent) +{ + struct ub_port *port; + + if (!uent || !uent->port_nums) + return -EINVAL; + + if (uent->ports) + return 0; + + uent->ports = kvcalloc(uent->port_nums, sizeof(*port), GFP_KERNEL); + if (!uent->ports) + return -ENOMEM; + + for_each_uent_port(port, uent) { + port->index = port - uent->ports; + ub_port_init(uent, port); + } + + return 0; +} + +void ub_ports_unset(struct ub_entity *uent) +{ + if (!uent) + return; + + kvfree(uent->ports); + uent->ports = NULL; + pr_debug("port release\n"); +} diff --git a/drivers/ub/ubus/port.h b/drivers/ub/ubus/port.h new file mode 100644 index 000000000000..8f8c1d78c2be --- /dev/null +++ b/drivers/ub/ubus/port.h @@ -0,0 +1,21 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Copyright (c) HiSilicon Technologies Co., Ltd. 2025. All rights reserved. + */ +#ifndef __PORT_H__ +#define __PORT_H__ + +#define for_each_uent_port(p, d) \ + for ((p) = (d)->ports; ((p) - (d)->ports) < (d)->port_nums; (p)++) + +struct ub_port; +struct ub_entity; +void ub_port_disconnect(struct ub_port *port); +void ub_port_connect(struct ub_port *port, struct ub_port *r_port); +bool ub_check_and_connect(struct ub_port *port, struct ub_entity *r_uent); +int ub_ports_add(struct ub_entity *uent); +void ub_ports_del(struct ub_entity *uent); +int ub_ports_setup(struct ub_entity *uent); +void ub_ports_unset(struct ub_entity *uent); + +#endif /* __PORT_H__ */ diff --git a/include/ub/ubus/ubus.h b/include/ub/ubus/ubus.h index 779569fd46c2..9a90f9b3866b 100644 --- a/include/ub/ubus/ubus.h +++ b/include/ub/ubus/ubus.h @@ -63,6 +63,28 @@ static inline int ub_show_guid(struct ub_guid *guid, char *buf) guid->bits.reserved, guid->bits.seq_num); } +enum ub_port_type { + PHYSICAL, + VIRTUAL, +}; + +#define UB_MAX_CNA_NUM SZ_64K +#define UB_PORT_CAP_NUM SZ_256 + +struct ub_port { + struct ub_entity *uent; + u16 index; + enum ub_port_type type; + u32 cna; + struct ub_entity *r_uent; /* If valid, also represent link up */ + u16 r_index; + guid_t r_guid; + struct kobject kobj; + DECLARE_BITMAP(cna_maps, UB_MAX_CNA_NUM); + /* cap cache */ + DECLARE_BITMAP(cap_map, UB_PORT_CAP_NUM); +}; + struct ub_entity { /* Driver framework base info */ struct device dev; @@ -81,6 +103,10 @@ struct ub_entity { /* entity topology info */ struct ub_bus_controller *ubc; + /* entity port info */ + u16 port_nums; + struct ub_port *ports; + struct dev_message *message; u16 upi; -- Gitee From 144ead1b259119f7b2a2cd608b801222dec40055 Mon Sep 17 00:00:00 2001 From: Jianquan Lin Date: Fri, 19 Sep 2025 13:58:00 +0800 Subject: [PATCH 11/45] ub:ubus: Support Ubus read/write configuration functions driver inclusion category: feature bugzilla: https://gitee.com/openeuler/kernel/issues/ID34DG CVE: NA ----------------------------------------------------------- Support Ubus read/write configuration functions, read/write by 1, 2, 4 byte. Signed-off-by: Jianquan Lin --- drivers/ub/ubus/ubus_config.c | 281 ++++++++++++++++++++++++++++++++++ drivers/ub/ubus/ubus_config.h | 5 + drivers/ub/ubus/ubus_driver.c | 11 ++ include/ub/ubus/ubus.h | 44 ++++++ 4 files changed, 341 insertions(+) diff --git a/drivers/ub/ubus/ubus_config.c b/drivers/ub/ubus/ubus_config.c index 698e465607aa..5a56108d98d3 100644 --- a/drivers/ub/ubus/ubus_config.c +++ b/drivers/ub/ubus/ubus_config.c @@ -60,6 +60,27 @@ struct ub_cfg { u8 byte_mask; }; +static struct ub_cfg ub_cfg_param[] = { { 0x1, 0x0, 0x1 }, + { 0x2, 0x1, 0x3 }, + { 0x4, 0x3, 0xf } }; +#define UB_CFG_PARAM_CNT ARRAY_SIZE(ub_cfg_param) + +static bool pos_size_valid(u64 pos, u8 size) +{ + struct ub_cfg *cfg; + u8 i; + + if (pos >= UB_CFG_SAPCE_SLICE_END) + return false; + + for (i = 0; i < UB_CFG_PARAM_CNT; i++) { + cfg = &ub_cfg_param[i]; + if (cfg->size == size) + return !(pos & cfg->pos_mask); + } + return false; /* size invalid */ +} + static void ub_msg_extended_header_init(struct msg_extended_header *msgetah, u16 plen, u8 code) { @@ -103,3 +124,263 @@ void ub_msg_pkt_header_init(struct msg_pkt_header *header, struct ub_entity *uen ub_msg_extended_header_init(&header->msgetah, plen, code); } EXPORT_SYMBOL_GPL(ub_msg_pkt_header_init); + +int ub_check_cfg_msg_code(struct device *dev, u8 req_code, u8 rsp_code) +{ + u8 req_sub = sub_msg_code(req_code); + u8 rsp_sub = sub_msg_code(rsp_code); + u8 req = msg_code(req_code); + u8 rsp = msg_code(rsp_code); + + if (req != UB_MSG_CODE_CFG || rsp != UB_MSG_CODE_CFG) { + dev_err(dev, "The message code is incorrect, req message code=%#x, rsp message code=%#x\n", + req, rsp); + return -EIO; + } + + if (msg_type(req_code) == MSG_REQ && + msg_type(rsp_code) == MSG_RSP && req_sub == rsp_sub) + return 0; + + dev_err(dev, "The response code is incorrect, req code=%#x, rsp code=%#x\n", + req_code, rsp_code); + return -EIO; +} +EXPORT_SYMBOL_GPL(ub_check_cfg_msg_code); + +static int ub_sync_cfg_rsp_check(struct ub_entity *uent, + struct cfg_msg_req_pkt *req_pkt, + struct cfg_msg_rsp_pkt *rsp_pkt) +{ + struct msg_pkt_header *req_header = &req_pkt->header; + struct msg_pkt_header *rsp_header = &rsp_pkt->header; + u32 rsp_seid = eid_gen(rsp_header->seid_h, rsp_header->seid_l); + u32 req_seid = eid_gen(req_header->seid_h, req_header->seid_l); + u8 rsp_status = rsp_header->msgetah.rsp_status; + + if (rsp_status != UB_MSG_RSP_SUCCESS) { + ub_err(uent, "Message response error, rsp_status=%x\n", + rsp_status); + return -EIO; + } + + if (rsp_header->nth.scna != req_header->nth.dcna || + rsp_header->nth.dcna != req_header->nth.scna) { + ub_err(uent, "CNA mismatch, req_scna=%#x req_dcna=%#x, rsp_scna=%#x rsp_dcna=%#x\n", + req_header->nth.scna, req_header->nth.dcna, + rsp_header->nth.scna, rsp_header->nth.dcna); + return -EIO; + } + + if (rsp_seid != req_header->deid || rsp_header->deid != req_seid) { + ub_err(uent, "EID mismatch, req_seid=%#x req_deid=%#x, rsp_seid=%#x rsp_deid=%#x\n", + req_seid, req_header->deid, rsp_seid, rsp_header->deid); + return -EIO; + } + + if (rsp_header->msgetah.plen != CFG_MSG_PLD_SIZE) { + ub_err(uent, "The packet length is incorrect, len=%#x\n", + rsp_header->msgetah.plen); + return -EIO; + } + + return ub_check_cfg_msg_code(&uent->dev, req_header->msgetah.code, + rsp_header->msgetah.code); +} + +void ub_sync_cfg_rsp_handle(struct cfg_msg_pld_rsp *rsp, u8 size, + u64 pos, bool write, u32 *val) +{ +#define UB_CFG_REG_SIZE 4 + u8 pos_in_reg = pos % UB_CFG_REG_SIZE; + u32 read_data; + + if (!write) { + read_data = rsp->read_data >> (pos_in_reg * BITS_PER_BYTE); + if (size == sizeof(u8)) + *(u8 *)val = read_data; + else if (size == sizeof(u16)) + *(u16 *)val = read_data; + else + *val = read_data; + } +} + +static u8 gen_cfg_sub_msg_code(bool is_write, u64 pos) +{ + if (pos >= UB_CFG1_BASIC_SLICE && pos < UB_PORT_SLICE_START) + return is_write ? UB_CFG1_WRITE : UB_CFG1_READ; + else + return is_write ? UB_CFG0_WRITE : UB_CFG0_READ; +} + +static void ub_msg_pkt_req_init(struct ub_entity *uent, u8 size, u64 pos, u32 *val, + struct cfg_msg_pld_req *req) +{ + u8 bt_mask = ub_cfg_param[size >> 1].byte_mask; + + if (val) { + if (size == sizeof(u8)) + req->write_data = *(u8 *)val; + else if (size == sizeof(u16)) + req->write_data = *(u16 *)val; + else + req->write_data = *val; + req->write_data <<= ((pos % sizeof(u32)) * BITS_PER_BYTE); + } + + req->byte_enable = bt_mask << (u8)(pos % sizeof(u32)); + req->entity_idx = uent->entity_idx; + /* The address is in four bytes. */ + req->req_addr = pos / sizeof(u32); +} + +static int ub_sync_cfg(struct ub_entity *uent, u8 size, u64 pos, bool iswrite, + u32 *val) +{ + struct cfg_msg_req_pkt req_pkt = {}; + struct cfg_msg_rsp_pkt rsp_pkt = {}; + struct msg_info info = {}; + u8 sub_msg_code; + int ret; + + if (!pos_size_valid(pos, size)) { + ub_err(uent, "pos or size invalid, pos=%#llx, size=%#x\n", pos, + size); + return -EINVAL; + } + + if (!iswrite) + memset(val, 0xFF, size); + + sub_msg_code = gen_cfg_sub_msg_code(iswrite, pos); + ub_msg_pkt_header_init(&req_pkt.header, uent, CFG_MSG_PLD_SIZE, + code_gen(UB_MSG_CODE_CFG, sub_msg_code, + MSG_REQ), false); + + ub_msg_pkt_req_init(uent, size, pos, (iswrite ? val : NULL), + &req_pkt.req_payload); + + message_info_init(&info, uent, &req_pkt, &rsp_pkt, + (MSG_CFG_PKT_SIZE << MSG_REQ_SIZE_OFFSET) | + MSG_CFG_PKT_SIZE); + ret = message_sync_request(uent->message->mdev, &info, + req_pkt.header.msgetah.code); + if (ret) + return ret; + + ret = ub_sync_cfg_rsp_check(uent, &req_pkt, &rsp_pkt); + if (!ret) + ub_sync_cfg_rsp_handle(&rsp_pkt.rsp_payload, size, pos, iswrite, val); + + return ret; +} + +int ub_send_cfg(struct ub_entity *uent, u8 size, u64 pos, u32 *val) +{ + struct cfg_msg_req_pkt req_pkt = {}; + struct msg_info info = {}; + u8 sub_msg_code; + + if (!uent || !uent->message || !uent->message->mdev) { + pr_err("uent or message or mdev is null\n"); + return -EINVAL; + } + + if (!pos_size_valid(pos, size)) { + pr_err("pos or size invalid, pos=%#llx, size=%u\n", pos, size); + return -EINVAL; + } + + sub_msg_code = gen_cfg_sub_msg_code(true, pos); + ub_msg_pkt_header_init(&req_pkt.header, uent, CFG_MSG_PLD_SIZE, + code_gen(UB_MSG_CODE_CFG, sub_msg_code, + MSG_REQ), false); + + ub_msg_pkt_req_init(uent, size, pos, val, + &req_pkt.req_payload); + + message_info_init(&info, uent, &req_pkt, NULL, + (MSG_CFG_PKT_SIZE << MSG_REQ_SIZE_OFFSET) | + MSG_CFG_PKT_SIZE); + return message_send(uent->message->mdev, &info, + req_pkt.header.msgetah.code); +} + +int __ub_cfg_read_byte(struct ub_entity *uent, u64 pos, u8 *val) +{ + if (!uent || !uent->message || !uent->message->mdev || !val) { + pr_err("uent or message or mdev is null\n"); + return -EINVAL; + } + + return ub_sync_cfg(uent, (u8)sizeof(u8), pos, false, (u32 *)val); +} + +int __ub_cfg_read_word(struct ub_entity *uent, u64 pos, u16 *val) +{ + if (!uent || !uent->message || !uent->message->mdev || !val) { + pr_err("uent or message or mdev is null\n"); + return -EINVAL; + } + + return ub_sync_cfg(uent, (u8)sizeof(u16), pos, false, (u32 *)val); +} + +int __ub_cfg_read_dword(struct ub_entity *uent, u64 pos, u32 *val) +{ + if (!uent || !uent->message || !uent->message->mdev || !val) { + pr_err("uent or message or mdev is null\n"); + return -EINVAL; + } + + return ub_sync_cfg(uent, (u8)sizeof(u32), pos, false, val); +} + +int __ub_cfg_write_byte(struct ub_entity *uent, u64 pos, u8 val) +{ + if (!uent || !uent->message || !uent->message->mdev) { + pr_err("uent or message or mdev is null\n"); + return -EINVAL; + } + + return ub_sync_cfg(uent, (u8)sizeof(u8), pos, true, (u32 *)&val); +} + +int __ub_cfg_write_word(struct ub_entity *uent, u64 pos, u16 val) +{ + if (!uent || !uent->message || !uent->message->mdev) { + pr_err("uent or message or mdev is null\n"); + return -EINVAL; + } + + return ub_sync_cfg(uent, (u8)sizeof(u16), pos, true, (u32 *)&val); +} + +int __ub_cfg_write_dword(struct ub_entity *uent, u64 pos, u32 val) +{ + if (!uent || !uent->message || !uent->message->mdev) { + pr_err("uent or message or mdev is null\n"); + return -EINVAL; + } + + return ub_sync_cfg(uent, (u8)sizeof(u32), pos, true, (u32 *)&val); +} + +int ub_cfg_ops_init(void) +{ + int ret; + + ret = register_ub_cfg_read_ops(__ub_cfg_read_byte, __ub_cfg_read_word, + __ub_cfg_read_dword); + if (ret) + return ret; + + ret = register_ub_cfg_write_ops(__ub_cfg_write_byte, + __ub_cfg_write_word, + __ub_cfg_write_dword); + if (ret) + unregister_ub_cfg_ops(); + + return ret; +} diff --git a/drivers/ub/ubus/ubus_config.h b/drivers/ub/ubus/ubus_config.h index b7f733ed6549..5971c87e481b 100644 --- a/drivers/ub/ubus/ubus_config.h +++ b/drivers/ub/ubus/ubus_config.h @@ -6,4 +6,9 @@ #ifndef __UBUS_CONFIG_H__ #define __UBUS_CONFIG_H__ +struct ub_entity; +int ub_cfg_ops_init(void); +int ub_check_cfg_msg_code(struct device *dev, u8 req_code, u8 rsp_code); +int ub_send_cfg(struct ub_entity *uent, u8 size, u64 pos, u32 *val); + #endif /* __UBUS_CONFIG_H__ */ diff --git a/drivers/ub/ubus/ubus_driver.c b/drivers/ub/ubus/ubus_driver.c index a14cadf75725..1ad798c2aa84 100644 --- a/drivers/ub/ubus/ubus_driver.c +++ b/drivers/ub/ubus/ubus_driver.c @@ -509,13 +509,24 @@ void ub_bus_type_uninit(void) int ub_host_probe(void) { + int ret; + ub_bus_type_init(); + ret = ub_cfg_ops_init(); + if (ret) + goto ub_cfg_ops_init_fail; + return 0; + +ub_cfg_ops_init_fail: + ub_bus_type_uninit(); + return ret; } EXPORT_SYMBOL_GPL(ub_host_probe); void ub_host_remove(void) { + unregister_ub_cfg_ops(); ub_bus_type_uninit(); } EXPORT_SYMBOL_GPL(ub_host_remove); diff --git a/include/ub/ubus/ubus.h b/include/ub/ubus/ubus.h index 9a90f9b3866b..87a4022190f8 100644 --- a/include/ub/ubus/ubus.h +++ b/include/ub/ubus/ubus.h @@ -99,6 +99,7 @@ struct ub_entity { u16 module; unsigned int cna; unsigned int eid; + unsigned short entity_idx; /* entity topology info */ struct ub_bus_controller *ubc; @@ -233,6 +234,37 @@ extern struct bus_type ub_bus_type; void ub_bus_type_iommu_ops_set(const struct iommu_ops *ops); const struct iommu_ops *ub_bus_type_iommu_ops_get(void); +/** + * ub_cfg_read_byte() - 1 byte configuration access read. + * @uent: UB entity. + * @pos: Config space address. + * @val: Output buffer. + * + * Initiate configuration access to the specified address of the entity + * configuration space and read 1 byte. + * + * Context: Any context, It will take spin_lock_irqsave()/spin_unlock_restore() + * Return: 0 if success, or negative value if failed. + */ +int ub_cfg_read_byte(struct ub_entity *uent, u64 pos, u8 *val); +int ub_cfg_read_word(struct ub_entity *uent, u64 pos, u16 *val); +int ub_cfg_read_dword(struct ub_entity *uent, u64 pos, u32 *val); +/** + * ub_cfg_write_byte() - 1 byte configuration access write. + * @uent: UB entity. + * @pos: Config space address. + * @val: Data. + * + * Initiate configuration access to the specified address of the entity + * configuration space and write 1 byte. + * + * Context: Any context, It will take spin_lock_irqsave()/spin_unlock_restore() + * Return: 0 if success, or negative value if failed. + */ +int ub_cfg_write_byte(struct ub_entity *uent, u64 pos, u8 val); +int ub_cfg_write_word(struct ub_entity *uent, u64 pos, u16 val); +int ub_cfg_write_dword(struct ub_entity *uent, u64 pos, u32 val); + /** * ub_entity_get() - Atomically increment the reference count for the entity. * @uent: UB entity pointer. @@ -291,6 +323,18 @@ void ub_unregister_driver(struct ub_driver *drv); static inline struct ub_entity *ub_entity_get(struct ub_entity *uent) { return NULL; } static inline void ub_entity_put(struct ub_entity *uent) {} +static inline int ub_cfg_read_byte(struct ub_entity *uent, u64 pos, u8 *val) +{ return -ENODEV; } +static inline int ub_cfg_read_word(struct ub_entity *uent, u64 pos, u16 *val) +{ return -ENODEV; } +static inline int ub_cfg_read_dword(struct ub_entity *uent, u64 pos, u32 *val) +{ return -ENODEV; } +static inline int ub_cfg_write_byte(struct ub_entity *uent, u64 pos, u8 val) +{ return -ENODEV; } +static inline int ub_cfg_write_word(struct ub_entity *uent, u64 pos, u16 val) +{ return -ENODEV; } +static inline int ub_cfg_write_dword(struct ub_entity *uent, u64 pos, u32 val) +{ return -ENODEV; } static inline int ub_get_bus_controller(struct ub_entity *uents[], unsigned int max_num, unsigned int *real_num) -- Gitee From 028f3b19ea003fd44df4b20838d1a336bdc8df2a Mon Sep 17 00:00:00 2001 From: Junlong Zheng Date: Fri, 19 Sep 2025 11:33:52 +0800 Subject: [PATCH 12/45] ub:ubus: Support for UB port sysfs attribute files driver inclusion category: feature bugzilla: https://gitee.com/openeuler/kernel/issues/ID34DG CVE: NA ----------------------------------------------------------- Provide a list of basic sysfs attribute files for UB port, including port network address, neighbor information, link status, etc. Signed-off-by: Junlong Zheng --- drivers/ub/ubus/port.c | 349 +++++++++++++++++++++++++++++++++++++++++ include/ub/ubus/ubus.h | 2 + 2 files changed, 351 insertions(+) diff --git a/drivers/ub/ubus/port.c b/drivers/ub/ubus/port.c index 6b70c229463b..81117aa1f9e9 100644 --- a/drivers/ub/ubus/port.c +++ b/drivers/ub/ubus/port.c @@ -5,6 +5,8 @@ #define pr_fmt(fmt) "ubus port: " fmt +#include + #include "ubus.h" #include "port.h" @@ -17,7 +19,335 @@ struct ub_port_attribute { #define to_ub_port(o) container_of(o, struct ub_port, kobj) #define to_ub_port_attr(a) container_of(a, struct ub_port_attribute, attr) +#define UB_PORT_ATTR_RO(field) \ + static struct ub_port_attribute ub_port_attr_##field = __ATTR_RO(field) + +#define UB_PORT_ATTR_RW(field) \ + static struct ub_port_attribute ub_port_attr_##field = __ATTR_RW(field) + +#define UB_PORT_ATTR_WO(field) \ + static struct ub_port_attribute ub_port_attr_##field = __ATTR_WO(field) + +int ub_port_read_byte(struct ub_port *port, u32 pos, u8 *val) +{ + u64 base = UB_PORT_SLICE_START + port->index * UB_PORT_SLICE_SIZE; + + return ub_cfg_read_byte(port->uent, base + pos, val); +} + +int ub_port_read_word(struct ub_port *port, u32 pos, u16 *val) +{ + u64 base = UB_PORT_SLICE_START + port->index * UB_PORT_SLICE_SIZE; + + return ub_cfg_read_word(port->uent, base + pos, val); +} + +int ub_port_read_dword(struct ub_port *port, u32 pos, u32 *val) +{ + u64 base = UB_PORT_SLICE_START + port->index * UB_PORT_SLICE_SIZE; + + return ub_cfg_read_dword(port->uent, base + pos, val); +} + +int ub_port_write_byte(struct ub_port *port, u32 pos, u8 val) +{ + u64 base = UB_PORT_SLICE_START + port->index * UB_PORT_SLICE_SIZE; + + return ub_cfg_write_byte(port->uent, base + pos, val); +} + +int ub_port_write_word(struct ub_port *port, u32 pos, u16 val) +{ + u64 base = UB_PORT_SLICE_START + port->index * UB_PORT_SLICE_SIZE; + + return ub_cfg_write_word(port->uent, base + pos, val); +} + +int ub_port_write_dword(struct ub_port *port, u32 pos, u32 val) +{ + u64 base = UB_PORT_SLICE_START + port->index * UB_PORT_SLICE_SIZE; + + return ub_cfg_write_dword(port->uent, base + pos, val); +} + +static ssize_t cna_show(struct ub_port *port, char *buf) +{ + return sysfs_emit(buf, "%#06x\n", port->cna); +} +UB_PORT_ATTR_RO(cna); + +static ssize_t boundary_show(struct ub_port *port, char *buf) +{ + return sysfs_emit(buf, "%#01x\n", port->domain_boundary); +} +UB_PORT_ATTR_RO(boundary); + +static ssize_t linkup_show(struct ub_port *port, char *buf) +{ + u8 val; + + if (port->type == VIRTUAL) + return sysfs_emit(buf, "Virtual port don't support\n"); + + if (ub_port_read_byte(port, UB_PORT_PHYSICAL_PORT_LINK_STATUS, &val)) { + ub_err(port->uent, "get port link cap fail\n"); + return -EIO; + } + + return sysfs_emit(buf, "%u\n", val & UB_PORT_LINK_STATE); +} +UB_PORT_ATTR_RO(linkup); + +static ssize_t neighbor_port_idx_show(struct ub_port *port, char *buf) +{ + if (!port->r_uent) + return sysfs_emit(buf, "No Neighbor\n"); + return sysfs_emit(buf, "%u\n", port->r_index); +} +UB_PORT_ATTR_RO(neighbor_port_idx); + +static ssize_t neighbor_guid_show(struct ub_port *port, char *buf) +{ + struct ub_guid *guid; + int count; + + if (!port->r_uent && guid_is_null(&port->r_guid)) + return sysfs_emit(buf, "No Neighbor\n"); + + guid = port->r_uent ? &port->r_uent->guid : + (struct ub_guid *)&port->r_guid; + + count = ub_show_guid(guid, buf); + + return count + sysfs_emit_at(buf, count, "\n"); +} +UB_PORT_ATTR_RO(neighbor_guid); + +static ssize_t neighbor_show(struct ub_port *port, char *buf) +{ + if (!port->r_uent) + return sysfs_emit(buf, "No Neighbor\n"); + + return sysfs_emit(buf, "%05x\n", port->r_uent->uent_num); +} +UB_PORT_ATTR_RO(neighbor); + +static ssize_t asy_link_width_show(struct ub_port *port, char *buf) +{ + u8 val; + + if (ub_port_read_byte(port, PORT_CAP15_QDLWS_CAP, &val)) { + ub_err(port->uent, "get port cap15 cap fail\n"); + return -EIO; + } + + if (val & ASY_LINK_WIDTH_MASK) + return sysfs_emit(buf, "Support\n"); + else + return sysfs_emit(buf, "Not Support\n"); +} +UB_PORT_ATTR_RO(asy_link_width); + +static ssize_t glb_qdlws_show(struct ub_port *port, char *buf) +{ + u8 val; + + if (ub_port_read_byte(port, PORT_CAP15_QDLWS_CTRL, &val)) { + ub_err(port->uent, "get global qdlws fail\n"); + return -EIO; + } + + return sysfs_emit(buf, "%s\n", str_enable_disable(val & GLB_QDLWS_MASK)); +} + +static ssize_t glb_qdlws_store(struct ub_port *port, const char *buf, + size_t count) +{ + unsigned long val; + int ret; + u8 cur; + + ret = kstrtoul(buf, 0, &val); + if (ret || (val != 0 && val != 1)) { + ub_err(port->uent, "Invalid val for global qdlws\n"); + return -EINVAL; + } + + if (ub_port_read_byte(port, PORT_CAP15_QDLWS_CTRL, &cur)) { + ub_err(port->uent, "get global qdlws fail\n"); + return -EIO; + } + + if (val) + cur = cur | GLB_QDLWS_ENABLE_MASK; + else + cur = cur & GLB_QDLWS_DISABLE_MASK; + + if (ub_port_write_byte(port, PORT_CAP15_QDLWS_CTRL, cur)) { + ub_err(port->uent, "set global qdlws fail\n"); + return -EIO; + } + + return count; +} +UB_PORT_ATTR_RW(glb_qdlws); + +static ssize_t tx_qdlws_show(struct ub_port *port, char *buf) +{ + u8 val; + + if (ub_port_read_byte(port, PORT_CAP15_QDLWS_CTRL, &val)) { + ub_err(port->uent, "get TX qdlws fail\n"); + return -EIO; + } + + return sysfs_emit(buf, "%s\n", str_enable_disable(val & TX_QDLWS_MASK)); +} + +static ssize_t tx_qdlws_store(struct ub_port *port, const char *buf, + size_t count) +{ + unsigned long val; + int ret; + u8 cur; + + ret = kstrtoul(buf, 0, &val); + if (ret || (val != 0 && val != 1)) { + ub_err(port->uent, "Invalid val for TX qdlws\n"); + return -EINVAL; + } + + if (ub_port_read_byte(port, PORT_CAP15_QDLWS_CTRL, &cur)) { + ub_err(port->uent, "get value for TX qdlws fail\n"); + return -EIO; + } + + if (val) + cur = cur | TX_QDLWS_ENABLE_MASK; + else + cur = cur & TX_QDLWS_DISABLE_MASK; + + if (ub_port_write_byte(port, PORT_CAP15_QDLWS_CTRL, cur)) { + ub_err(port->uent, "set TX qdlws fail\n"); + return -EIO; + } + + return count; +} +UB_PORT_ATTR_RW(tx_qdlws); + +static ssize_t rx_qdlws_show(struct ub_port *port, char *buf) +{ + u8 val; + + if (ub_port_read_byte(port, PORT_CAP15_QDLWS_CTRL, &val)) { + ub_err(port->uent, "get RX qdlws fail\n"); + return -EIO; + } + + return sysfs_emit(buf, "%s\n", str_enable_disable(val & RX_QDLWS_MASK)); +} + +static ssize_t rx_qdlws_store(struct ub_port *port, const char *buf, + size_t count) +{ + unsigned long val; + int ret; + u8 cur; + + ret = kstrtoul(buf, 0, &val); + if (ret || (val != 0 && val != 1)) { + ub_err(port->uent, "Invalid val for RX qdlws\n"); + return -EINVAL; + } + + if (ub_port_read_byte(port, PORT_CAP15_QDLWS_CTRL, &cur)) { + ub_err(port->uent, "get value for RX qdlws fail\n"); + return -EIO; + } + + if (val) + cur = cur | RX_QDLWS_ENABLE_MASK; + else + cur = cur & RX_QDLWS_DISABLE_MASK; + + if (ub_port_write_byte(port, PORT_CAP15_QDLWS_CTRL, cur)) { + ub_err(port->uent, "set RX qdlws fail\n"); + return -EIO; + } + + return count; +} +UB_PORT_ATTR_RW(rx_qdlws); + +static const char * const status[] = { "IDLE", "NAK", "In progress", "Timeout", + "Successful Done" }; + +static ssize_t qdlws_exec_state_show(struct ub_port *port, char *buf) +{ + u8 val; + + if (ub_port_read_byte(port, PORT_CAP15_QDLWS_STATE, &val)) { + ub_err(port->uent, "get qdlws exec state fail\n"); + return -EIO; + } + + val = val & QDLWS_EXEC_STATUS_MASK; + if (val > QDLWS_EXEC_STATUS_MAX) { + ub_err(port->uent, "get error state, value[%u]\n", val); + return sysfs_emit(buf, "Not support state\n"); + } + + return sysfs_emit(buf, "%s\n", status[val]); +} +UB_PORT_ATTR_RO(qdlws_exec_state); + +static struct attribute *ub_port_default_attrs[] = { + &ub_port_attr_cna.attr, + &ub_port_attr_boundary.attr, + &ub_port_attr_linkup.attr, + /* neighbor info */ + &ub_port_attr_neighbor_port_idx.attr, + &ub_port_attr_neighbor_guid.attr, + &ub_port_attr_neighbor.attr, + NULL +}; + +static const struct attribute_group ub_port_default_group = { + .attrs = ub_port_default_attrs, +}; + +static struct attribute *ub_port_qdlws_attrs[] = { + &ub_port_attr_asy_link_width.attr, + &ub_port_attr_glb_qdlws.attr, + &ub_port_attr_tx_qdlws.attr, + &ub_port_attr_rx_qdlws.attr, + &ub_port_attr_qdlws_exec_state.attr, + NULL +}; + +static umode_t ub_port_qdlws_is_visible(struct kobject *kobj, + struct attribute *a, int n) +{ + struct ub_port *port = to_ub_port(kobj); + + if (port->type == VIRTUAL) + return 0; + + if (test_bit(UB_PORT_CAP15_QDLWS, port->cap_map)) + return a->mode; + + return 0; +} + +static const struct attribute_group ub_port_qdlws_group = { + .is_visible = ub_port_qdlws_is_visible, + .attrs = ub_port_qdlws_attrs, +}; + static const struct attribute_group *ub_port_groups[] = { + &ub_port_default_group, + &ub_port_qdlws_group, NULL }; @@ -110,6 +440,25 @@ bool ub_check_and_connect(struct ub_port *port, struct ub_entity *r_uent) static int ub_port_config(struct ub_port *port) { + u32 map[UB_PORT_CAP_NUM / SZ_32]; + int ret, i; + + /* virtual port doesn't have bitmap */ + if (port->type == VIRTUAL) + return 0; + + for (i = 0; i < UB_PORT_CAP_NUM / SZ_32; i++) { + ret = ub_port_read_dword( + port, UB_CFG0_PORT_BITMAP + i * sizeof(u32), &map[i]); + if (ret) { + ub_err(port->uent, + "failed to read port%u bitmap %d with %d\n", + port->index, i, ret); + return ret; + } + } + + memcpy((void *)port->cap_map, (void *)map, sizeof(map)); return 0; } diff --git a/include/ub/ubus/ubus.h b/include/ub/ubus/ubus.h index 87a4022190f8..2b7fafd1a237 100644 --- a/include/ub/ubus/ubus.h +++ b/include/ub/ubus/ubus.h @@ -75,6 +75,7 @@ struct ub_port { struct ub_entity *uent; u16 index; enum ub_port_type type; + u8 domain_boundary; u32 cna; struct ub_entity *r_uent; /* If valid, also represent link up */ u16 r_index; @@ -100,6 +101,7 @@ struct ub_entity { unsigned int cna; unsigned int eid; unsigned short entity_idx; + u32 uent_num; /* ub dev number */ /* entity topology info */ struct ub_bus_controller *ubc; -- Gitee From 99adfbf75d96bcec6e635ba16afb18e7e2bed476 Mon Sep 17 00:00:00 2001 From: Junlong Zheng Date: Fri, 19 Sep 2025 14:07:13 +0800 Subject: [PATCH 13/45] ub:ubus: Supporting the UB Shared Port Function driver inclusion category: feature bugzilla: https://gitee.com/openeuler/kernel/issues/ID34DG CVE: NA ----------------------------------------------------------- The UB Bus Controller and IDEV support shared ports, reusing port interfaces for external communication. Signed-off-by: Junlong Zheng --- drivers/ub/ubus/port.c | 135 +++++++++++++++++++++++++++++++++++++++++ drivers/ub/ubus/port.h | 6 ++ drivers/ub/ubus/ubus.h | 23 +++++++ include/ub/ubus/ubus.h | 51 ++++++++++++++++ 4 files changed, 215 insertions(+) diff --git a/drivers/ub/ubus/port.c b/drivers/ub/ubus/port.c index 81117aa1f9e9..8aa7a7053cc0 100644 --- a/drivers/ub/ubus/port.c +++ b/drivers/ub/ubus/port.c @@ -546,3 +546,138 @@ void ub_ports_unset(struct ub_entity *uent) uent->ports = NULL; pr_debug("port release\n"); } + +static LIST_HEAD(ub_share_port_notify_list); +static DECLARE_RWSEM(ub_share_port_notify_list_rwsem); + +struct ub_share_port_notify_node { + struct ub_entity *parent; + struct ub_entity *idev; + u16 port_id; + struct ub_share_port_ops *ops; + struct list_head node; +}; + +int ub_register_share_port(struct ub_entity *idev, u16 port_id, + struct ub_share_port_ops *ops) +{ + struct ub_share_port_notify_node *notify_node; + struct ub_entity *parent; + struct ub_port *port; + + if (unlikely(!idev || !ops)) + return -EINVAL; + + if (!is_idev(idev)) { + ub_err(idev, "don't support non-idev device with type %u register share port\n", + uent_type(idev)); + return -EINVAL; + } + + /* get primary entity first */ + parent = idev; + while (!is_primary(parent)) + parent = parent->pue; + + /* check parent is controller */ + parent = to_ub_entity(parent->dev.parent); + if (!is_ibus_controller(parent)) { + ub_err(idev, "don't support register share port at non-controller device with type %u\n", + uent_type(parent)); + return -EINVAL; + } + + if (port_id >= parent->port_nums) { + ub_err(parent, "port id %u exceeds port num %u\n", port_id, + parent->port_nums); + return -EINVAL; + } + + port = parent->ports + port_id; + if (!port->shareable) { + ub_err(parent, "port%u isn't shareable\n", port_id); + return -EINVAL; + } + + notify_node = kzalloc(sizeof(*notify_node), GFP_KERNEL); + if (!notify_node) + return -ENOMEM; + + notify_node->parent = parent; + notify_node->idev = idev; + notify_node->port_id = port_id; + notify_node->ops = ops; + INIT_LIST_HEAD(¬ify_node->node); + + down_write(&ub_share_port_notify_list_rwsem); + list_add_tail(¬ify_node->node, &ub_share_port_notify_list); + up_write(&ub_share_port_notify_list_rwsem); + + ub_info(idev, "register share port at %u success\n", port_id); + return 0; +} +EXPORT_SYMBOL_GPL(ub_register_share_port); + +void ub_unregister_share_port(struct ub_entity *idev, u16 port_id, + struct ub_share_port_ops *ops) +{ + struct ub_share_port_notify_node *notify_node; + + if (unlikely(!idev)) + return; + + down_write(&ub_share_port_notify_list_rwsem); + + list_for_each_entry(notify_node, &ub_share_port_notify_list, node) { + if (notify_node->idev != idev || + notify_node->port_id != port_id || notify_node->ops != ops) + continue; + + list_del(¬ify_node->node); + kfree(notify_node); + ub_info(idev, "unregister share port at %u success\n", port_id); + goto unlock; + } + + ub_err(idev, "share port %u isn't registered, unregister failed\n", + port_id); +unlock: + up_write(&ub_share_port_notify_list_rwsem); +} +EXPORT_SYMBOL_GPL(ub_unregister_share_port); + +void ub_notify_share_port(struct ub_port *port, + enum ub_share_port_notify_type type) +{ + struct ub_share_port_notify_node *notify_node; + struct ub_share_port_ops *ops; + struct ub_entity *uent; + + if (!port || type >= NOTIFY_TYPE_MAX) + return; + + uent = port->uent; + down_read(&ub_share_port_notify_list_rwsem); + list_for_each_entry(notify_node, &ub_share_port_notify_list, node) { + if (notify_node->parent != uent || + notify_node->port_id != port->index) + continue; + + ops = notify_node->ops; + switch (type) { + case RESET_PREPARE: + if (ops->reset_prepare) + ops->reset_prepare(notify_node->idev, + notify_node->port_id); + break; + case RESET_DONE: + if (ops->reset_done) + ops->reset_done(notify_node->idev, + notify_node->port_id); + break; + default: + break; + } + } + up_read(&ub_share_port_notify_list_rwsem); +} diff --git a/drivers/ub/ubus/port.h b/drivers/ub/ubus/port.h index 8f8c1d78c2be..b8757fad7060 100644 --- a/drivers/ub/ubus/port.h +++ b/drivers/ub/ubus/port.h @@ -8,6 +8,12 @@ #define for_each_uent_port(p, d) \ for ((p) = (d)->ports; ((p) - (d)->ports) < (d)->port_nums; (p)++) +enum ub_share_port_notify_type { + RESET_PREPARE, + RESET_DONE, + NOTIFY_TYPE_MAX +}; + struct ub_port; struct ub_entity; void ub_port_disconnect(struct ub_port *port); diff --git a/drivers/ub/ubus/ubus.h b/drivers/ub/ubus/ubus.h index 51828b3c9806..cd2c2a91fa52 100644 --- a/drivers/ub/ubus/ubus.h +++ b/drivers/ub/ubus/ubus.h @@ -10,6 +10,29 @@ #define UB_CP_UPI 0x7FFF +enum ub_entity_type { + UB_ENT_BUS_CONTROLLER, + UB_ENT_IBUS_CONTROLLER, + UB_ENT_SWITCH, + UB_ENT_DEVICE, + UB_ENT_IDEVICE, + UB_ENT_P_DEVICE, + UB_ENT_P_IDEVICE, + UB_ENT_UNKNOWN +}; + +#define is_primary(uent) ((uent)->entity_idx == 0) +#define is_ibus_controller(uent) ((uent)->ent_type == UB_ENT_IBUS_CONTROLLER) +#define is_bus_controller(uent) ((uent)->ent_type == UB_ENT_BUS_CONTROLLER) +#define is_controller(uent) (is_ibus_controller(uent) || is_bus_controller(uent)) +#define is_switch(uent) ((uent)->ent_type == UB_ENT_SWITCH) +#define is_device(uent) ((uent)->ent_type == UB_ENT_DEVICE) +#define is_p_device(uent) ((uent)->ent_type == UB_ENT_P_DEVICE) +#define is_dev(uent) (is_device(uent) || is_p_device(uent)) +#define is_idevice(uent) ((uent)->ent_type == UB_ENT_IDEVICE) +#define is_p_idevice(uent) ((uent)->ent_type == UB_ENT_P_IDEVICE) +#define is_idev(uent) (is_idevice(uent) || is_p_idevice(uent)) + int ub_host_probe(void); void ub_host_remove(void); diff --git a/include/ub/ubus/ubus.h b/include/ub/ubus/ubus.h index 2b7fafd1a237..120c5432caef 100644 --- a/include/ub/ubus/ubus.h +++ b/include/ub/ubus/ubus.h @@ -76,6 +76,7 @@ struct ub_port { u16 index; enum ub_port_type type; u8 domain_boundary; + bool shareable; u32 cna; struct ub_entity *r_uent; /* If valid, also represent link up */ u16 r_index; @@ -94,6 +95,7 @@ struct ub_entity { const char *driver_override; /* Driver name to force a match */ /* entity base info */ + int ent_type; struct ub_guid guid; u16 class_code; u16 mod_vendor; /* entity's module vendor and module id */ @@ -105,6 +107,7 @@ struct ub_entity { /* entity topology info */ struct ub_bus_controller *ubc; + struct ub_entity *pue; /* ue/mue connected to their mue */ /* entity port info */ u16 port_nums; @@ -229,6 +232,20 @@ static inline const char *ub_name(const struct ub_entity *pue) return dev_name(&pue->dev); } +/* interfaces for shareable port */ +enum ub_port_event { + UB_PORT_EVENT_LINK_DOWN, + UB_PORT_EVENT_LINK_UP, + UB_PORT_EVENT_RESET_PREPARE, + UB_PORT_EVENT_RESET_DONE +}; + +struct ub_share_port_ops { + void (*reset_prepare)(struct ub_entity *uent, u16 port_id); + void (*reset_done)(struct ub_entity *uent, u16 port_id); + void (*event_notify)(struct ub_entity *uent, u16 port_id, int event); +}; + #ifdef CONFIG_UB_UBUS extern struct bus_type ub_bus_type; #define dev_is_ub(d) ((d)->bus == &ub_bus_type) @@ -236,6 +253,35 @@ extern struct bus_type ub_bus_type; void ub_bus_type_iommu_ops_set(const struct iommu_ops *ops); const struct iommu_ops *ub_bus_type_iommu_ops_get(void); +/** + * ub_register_share_port() - Register a share port. + * @uent: UB entity. + * @port_id: UB Bus Controller port id. + * @ops: UB share port ops. + * + * The IDEV reuses the physical port of the ub bus controller. Record the + * callback function. + * + * Context: Any context + * Return: 0 if success, or %-ENOMEM if system out of memory, + * or %-EINVAL if parameters invalid. + */ +int ub_register_share_port(struct ub_entity *uent, u16 port_id, + struct ub_share_port_ops *ops); + +/** + * ub_unregister_share_port() - Unregister a share port. + * @uent: UB entity. + * @port_id: UB Bus Controller port id. + * @ops: UB share port ops. + * + * Clear the callback function. + * + * Context: Any context + */ +void ub_unregister_share_port(struct ub_entity *uent, u16 port_id, + struct ub_share_port_ops *ops); + /** * ub_cfg_read_byte() - 1 byte configuration access read. * @uent: UB entity. @@ -346,6 +392,11 @@ ub_put_bus_controller(struct ub_entity *uents[], unsigned int num) {} static inline void ub_bus_type_iommu_ops_set(const struct iommu_ops *ops) {} static inline const struct iommu_ops *ub_bus_type_iommu_ops_get(void) { return NULL; } +static inline int ub_register_share_port(struct ub_entity *uent, u16 port_id, + struct ub_share_port_ops *ops) +{ return -ENODEV; } +static inline void ub_unregister_share_port(struct ub_entity *uent, u16 port_id, + struct ub_share_port_ops *ops) {} static inline int __ub_register_driver(struct ub_driver *drv, struct module *owner, const char *mod_name) -- Gitee From 32d3ce2d3444db7af1a9381982609a7b8f0c20f1 Mon Sep 17 00:00:00 2001 From: Yuhao Xiang Date: Fri, 19 Sep 2025 15:13:36 +0800 Subject: [PATCH 14/45] ub:ubus: Support cc configuration and query driver inclusion category: feature bugzilla: https://gitee.com/openeuler/kernel/issues/ID34DG CVE: NA ----------------------------------------------------------- Provide UB device congestion control switch configuration function. Provide the capability to query UB device congestion control functions. Signed-off-by: Yuhao Xiang --- drivers/ub/ubus/Makefile | 2 +- drivers/ub/ubus/cc.c | 85 ++++++++++++++++++++++++++++++++++++++++ include/ub/ubus/ubus.h | 46 ++++++++++++++++++++++ 3 files changed, 132 insertions(+), 1 deletion(-) create mode 100644 drivers/ub/ubus/cc.c diff --git a/drivers/ub/ubus/Makefile b/drivers/ub/ubus/Makefile index 90ab6925210e..aef65a34cbea 100644 --- a/drivers/ub/ubus/Makefile +++ b/drivers/ub/ubus/Makefile @@ -2,6 +2,6 @@ obj-$(CONFIG_UB_UBUS) += ub-driver.o controller.o config.o -ubus-y := ubus_driver.o sysfs.o ubus_controller.o msg.o ubus_config.o port.o +ubus-y := ubus_driver.o sysfs.o ubus_controller.o msg.o ubus_config.o port.o cc.o obj-$(CONFIG_UB_UBUS_BUS) += ubus.o diff --git a/drivers/ub/ubus/cc.c b/drivers/ub/ubus/cc.c new file mode 100644 index 000000000000..5f20d047c2e4 --- /dev/null +++ b/drivers/ub/ubus/cc.c @@ -0,0 +1,85 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (c) HiSilicon Technologies Co., Ltd. 2025. All rights reserved. + */ + +#define pr_fmt(fmt) "ubus cc: " fmt + +#include "ubus.h" + +static int ub_cc_safe_check(struct ub_entity *uent) +{ + if (!uent) + return -EINVAL; + + if (is_idev(uent)) { + if (!uent->ubc || !uent->ubc->uent) + return -EINVAL; + } + + return 0; +} + +bool ub_cc_supported(struct ub_entity *uent) +{ + if (ub_cc_safe_check(uent)) + return false; + + if (is_idev(uent)) { + /* Now, idev cc enable depends ub bus controller cc enable */ + if ((uent->ubc->uent->support_feature & UB_CC_SUPPORT) && + (uent->support_feature & UB_CC_SUPPORT)) + return true; + } else { + if (uent->support_feature & UB_CC_SUPPORT) + return true; + } + + return false; +} +EXPORT_SYMBOL_GPL(ub_cc_supported); + +/** + * ub_cc_set - Set congestion control for given ub entity + * @uent: ub entity + * @val: cc switch + * + * Returns: 0 on success, an error otherwise. + */ +static int ub_cc_set(struct ub_entity *uent, u8 val) +{ + if (!ub_cc_supported(uent)) + return -EPERM; + + return ub_cfg_write_byte(uent, UB_CC_EN, val); +} + +/** + * ub_cc_enable - Enable congestion control for given ub entity + * @uent: ub entity + * + * Returns: 0 on success, an error otherwise. + */ +int ub_cc_enable(struct ub_entity *uent) +{ + if (ub_cc_safe_check(uent)) + return -EINVAL; + + return ub_cc_set(uent, 1); +} +EXPORT_SYMBOL_GPL(ub_cc_enable); + +/** + * ub_cc_disable - Disable congestion control for given ub entity + * @uent: ub entity + * + * Returns: 0 on success, an error otherwise. + */ +int ub_cc_disable(struct ub_entity *uent) +{ + if (ub_cc_safe_check(uent)) + return -EINVAL; + + return ub_cc_set(uent, 0); +} +EXPORT_SYMBOL_GPL(ub_cc_disable); diff --git a/include/ub/ubus/ubus.h b/include/ub/ubus/ubus.h index 120c5432caef..a5ccac56a2a6 100644 --- a/include/ub/ubus/ubus.h +++ b/include/ub/ubus/ubus.h @@ -55,6 +55,14 @@ struct ub_guid { #define uent_base_code(uent) ((uent)->class_code & UB_BASE_CODE_MASK) #define uent_seq(uent) ((uent)->guid.bits.seq_num) +enum ub_type { + UB_TYPE_BUS_INSTANCE = 0, + UB_TYPE_CONTROLLER = 1, + UB_TYPE_ICONTROLLER = 2, + UB_TYPE_SWITCH = 3, + UB_TYPE_ISWITCH = 4 +}; + static inline int ub_show_guid(struct ub_guid *guid, char *buf) { return sprintf(buf, "%04x-%04x-%01x-%01x-%06x-%016llx", @@ -114,6 +122,7 @@ struct ub_entity { struct ub_port *ports; struct dev_message *message; + u32 support_feature; u16 upi; }; @@ -358,6 +367,37 @@ int ub_get_bus_controller(struct ub_entity *uents[], unsigned int max_num, */ void ub_put_bus_controller(struct ub_entity *uents[], unsigned int num); +/** + * ub_cc_supported() - Congestion control capability query. + * @uent: UB entity. + * + * Context: Any context. + * Return: %true if the entity supports congestion control, %false otherwise. + */ +bool ub_cc_supported(struct ub_entity *uent); + +/** + * ub_cc_enable() - Enable the congestion control capability of the entity. + * @uent: UB entity. + * + * Context: Any context. + * Return: 0 if success, or %-EINVAL if input parameters are invalid, or + * %-EPERM if the entity does not support congestion control, or other + * failed negative values. + */ +int ub_cc_enable(struct ub_entity *uent); + +/** + * ub_cc_disable() - Disable the congestion control capability of the entity. + * @uent: UB entity. + * + * Context: Any context. + * Return: 0 if success, or %-EINVAL if input parameters are invalid, or + * %-EPERM if the entity does not support congestion control, or other + * failed negative values. + */ +int ub_cc_disable(struct ub_entity *uent); + /* Proper probing supporting hot-pluggable entities */ int __ub_register_driver(struct ub_driver *drv, struct module *owner, const char *mod_name); @@ -371,6 +411,12 @@ void ub_unregister_driver(struct ub_driver *drv); static inline struct ub_entity *ub_entity_get(struct ub_entity *uent) { return NULL; } static inline void ub_entity_put(struct ub_entity *uent) {} +static inline bool ub_cc_supported(struct ub_entity *uent) +{ return false; } +static inline int ub_cc_enable(struct ub_entity *uent) +{ return -ENODEV; } +static inline int ub_cc_disable(struct ub_entity *uent) +{ return -ENODEV; } static inline int ub_cfg_read_byte(struct ub_entity *uent, u64 pos, u8 *val) { return -ENODEV; } static inline int ub_cfg_read_word(struct ub_entity *uent, u64 pos, u16 *val) -- Gitee From 605d37e9da27a9a95f5fac71904987b74142c4e8 Mon Sep 17 00:00:00 2001 From: Jianquan Lin Date: Fri, 19 Sep 2025 15:06:42 +0800 Subject: [PATCH 15/45] ub:ubus: Add EID allocate and free interfaces by kernel driver inclusion category: feature bugzilla: https://gitee.com/openeuler/kernel/issues/ID34DG CVE: NA ----------------------------------------------------------- Add Entity index allocate and free interfaces by kernel. Signed-off-by: Jianquan Lin --- drivers/ub/ubus/Makefile | 2 +- drivers/ub/ubus/eid.c | 88 ++++++++++++++++++++++++++++++++++++++++ drivers/ub/ubus/eid.h | 16 ++++++++ drivers/ub/ubus/ubus.h | 2 + 4 files changed, 107 insertions(+), 1 deletion(-) create mode 100644 drivers/ub/ubus/eid.c create mode 100644 drivers/ub/ubus/eid.h diff --git a/drivers/ub/ubus/Makefile b/drivers/ub/ubus/Makefile index aef65a34cbea..ecd38e538b13 100644 --- a/drivers/ub/ubus/Makefile +++ b/drivers/ub/ubus/Makefile @@ -2,6 +2,6 @@ obj-$(CONFIG_UB_UBUS) += ub-driver.o controller.o config.o -ubus-y := ubus_driver.o sysfs.o ubus_controller.o msg.o ubus_config.o port.o cc.o +ubus-y := ubus_driver.o sysfs.o ubus_controller.o msg.o ubus_config.o port.o cc.o eid.o obj-$(CONFIG_UB_UBUS_BUS) += ubus.o diff --git a/drivers/ub/ubus/eid.c b/drivers/ub/ubus/eid.c new file mode 100644 index 000000000000..74eeeeb9845b --- /dev/null +++ b/drivers/ub/ubus/eid.c @@ -0,0 +1,88 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (c) HiSilicon Technologies Co., Ltd. 2025. All rights reserved. + */ + +#define pr_fmt(fmt) "ubus eid: " fmt + +#include "ubus.h" +#include "eid.h" + +static DEFINE_IDA(ub_eid_ida); + +int ub_eid_request(guid_t *id, u32 *eid) +{ + int ida; + + if (!id || !eid) + return -EINVAL; + + ida = ida_alloc_range(&ub_eid_ida, ubc_eid_start, ubc_eid_end, GFP_KERNEL); + if (ida < 0) + return ida; + + *eid = (u32)ida; + + return 0; +} + +void ub_eid_release(u32 eid) +{ + ida_free(&ub_eid_ida, eid); +} + +int ub_eid_alloc(struct ub_entity *uent) +{ + struct device *dev; + u32 eid = 0; + int ret; + + if (is_p_device(uent)) + return 0; + + if (uent->eid) { + ub_warn(uent, "uent eid not 0, eid=%#05x\n", uent->eid); + return -EPERM; + } + + if (is_ibus_controller(uent) && uent->ubc->cluster) { + dev = &uent->ubc->dev; + ret = ub_cfg_read_dword(uent, UB_EID_0, &eid); + if (ret) { + dev_err(dev, "query cluster ubc, ret=%d\n", ret); + return ret; + } + + eid &= UB_COMPACT_EID_MASK; + if (eid) + dev_info(dev, "update cluster ubc eid, eid=%#x\n", eid); + + uent->eid = eid; + return 0; + } + + ret = ub_eid_request(&uent->guid.id, &eid); + if (!ret) + uent->eid = eid; + + return ret; +} + +void ub_eid_free(struct ub_entity *uent) +{ + u32 eid = uent->eid; + + if (is_p_device(uent)) + return; + + if (!eid) { + ub_warn(uent, "eid free 0\n"); + return; + } + + if (is_ibus_controller(uent) && uent->ubc->cluster) + return; + + uent->eid = 0; + ub_eid_release(eid); +} diff --git a/drivers/ub/ubus/eid.h b/drivers/ub/ubus/eid.h new file mode 100644 index 000000000000..9d71ad5cf228 --- /dev/null +++ b/drivers/ub/ubus/eid.h @@ -0,0 +1,16 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Copyright (c) HiSilicon Technologies Co., Ltd. 2025. All rights reserved. + */ + +#ifndef __EID_H__ +#define __EID_H__ + +struct ub_entity; + +int ub_eid_request(guid_t *id, u32 *eid); +void ub_eid_release(u32 eid); +int ub_eid_alloc(struct ub_entity *uent); +void ub_eid_free(struct ub_entity *uent); + +#endif /* __EID_H__ */ diff --git a/drivers/ub/ubus/ubus.h b/drivers/ub/ubus/ubus.h index cd2c2a91fa52..33275db12ff2 100644 --- a/drivers/ub/ubus/ubus.h +++ b/drivers/ub/ubus/ubus.h @@ -33,6 +33,8 @@ enum ub_entity_type { #define is_p_idevice(uent) ((uent)->ent_type == UB_ENT_P_IDEVICE) #define is_idev(uent) (is_idevice(uent) || is_p_idevice(uent)) +#define UB_COMPACT_EID_MASK GENMASK(19, 0) + int ub_host_probe(void); void ub_host_remove(void); -- Gitee From be989e18f1eb0688f03b8661c6342afa0d944d53 Mon Sep 17 00:00:00 2001 From: Jianquan Lin Date: Fri, 19 Sep 2025 15:22:05 +0800 Subject: [PATCH 16/45] ub:ubus: Add CNA allocate and free interfaces by kernel driver inclusion category: feature bugzilla: https://gitee.com/openeuler/kernel/issues/ID34DG CVE: NA ----------------------------------------------------------- Add Clan network address and free interfaces by kernel. Signed-off-by: Jianquan Lin --- drivers/ub/ubus/Makefile | 2 +- drivers/ub/ubus/cna.c | 86 ++++++++++++++++++++++++++++++++++++++++ drivers/ub/ubus/cna.h | 13 ++++++ drivers/ub/ubus/ubus.h | 2 + 4 files changed, 102 insertions(+), 1 deletion(-) create mode 100644 drivers/ub/ubus/cna.c create mode 100644 drivers/ub/ubus/cna.h diff --git a/drivers/ub/ubus/Makefile b/drivers/ub/ubus/Makefile index ecd38e538b13..caef4d64df35 100644 --- a/drivers/ub/ubus/Makefile +++ b/drivers/ub/ubus/Makefile @@ -2,6 +2,6 @@ obj-$(CONFIG_UB_UBUS) += ub-driver.o controller.o config.o -ubus-y := ubus_driver.o sysfs.o ubus_controller.o msg.o ubus_config.o port.o cc.o eid.o +ubus-y := ubus_driver.o sysfs.o ubus_controller.o msg.o ubus_config.o port.o cc.o eid.o cna.o obj-$(CONFIG_UB_UBUS_BUS) += ubus.o diff --git a/drivers/ub/ubus/cna.c b/drivers/ub/ubus/cna.c new file mode 100644 index 000000000000..e435259b1411 --- /dev/null +++ b/drivers/ub/ubus/cna.c @@ -0,0 +1,86 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (c) HiSilicon Technologies Co., Ltd. 2025. All rights reserved. + */ + +#define pr_fmt(fmt) "ubus cna: " fmt + +#include "ubus.h" +#include "port.h" +#include "cna.h" + +static DEFINE_IDA(ub_cna_ida); + +static int ub_alloc_cna_id(void) +{ + return ida_alloc_range(&ub_cna_ida, ubc_cna_start, ubc_cna_end, GFP_KERNEL); +} + +static void ub_free_cna_id(u32 cna) +{ + ida_free(&ub_cna_ida, cna); +} + +void ub_cna_free(struct ub_entity *uent) +{ + struct ub_port *port; + + if (!is_primary(uent)) { + ub_warn(uent, "only entity0 need to free cna\n"); + return; + } + + for_each_uent_port(port, uent) { + if (port->domain_boundary && is_ibus_controller(uent)) + continue; + + if (port->cna) { + if (!ONE_CNA(uent)) + ub_free_cna_id(port->cna); + port->cna = 0; + } + } + + if (is_ibus_controller(uent) && uent->ubc->cluster) + return; + + if (uent->cna) { + ub_free_cna_id(uent->cna); + uent->cna = 0; + } +} + +int ub_cna_alloc(struct ub_entity *uent) +{ + bool one_cna = ONE_CNA(uent); + struct ub_port *port; + int ret; + + if (!is_primary(uent)) { + ub_warn(uent, "only entity0 need to alloc cna\n"); + return 0; + } + + if (!(is_ibus_controller(uent) && uent->ubc->cluster)) { + ret = ub_alloc_cna_id(); + if (ret < 0) + return ret; + + uent->cna = (u32)ret; + } + + for_each_uent_port(port, uent) { + if (is_ibus_controller(uent) && port->domain_boundary) + continue; + if (one_cna) { + port->cna = uent->cna; + } else { + ret = ub_alloc_cna_id(); + if (ret < 0) + return ret; + port->cna = (u32)ret; + } + } + + return 0; +} diff --git a/drivers/ub/ubus/cna.h b/drivers/ub/ubus/cna.h new file mode 100644 index 000000000000..68d3d9a97fcc --- /dev/null +++ b/drivers/ub/ubus/cna.h @@ -0,0 +1,13 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Copyright (c) HiSilicon Technologies Co., Ltd. 2025. All rights reserved. + */ + +#ifndef __CNA_H__ +#define __CNA_H__ + +struct ub_entity; +int ub_cna_alloc(struct ub_entity *uent); +void ub_cna_free(struct ub_entity *uent); + +#endif /* __CNA_H__ */ diff --git a/drivers/ub/ubus/ubus.h b/drivers/ub/ubus/ubus.h index 33275db12ff2..e46611a1bdf0 100644 --- a/drivers/ub/ubus/ubus.h +++ b/drivers/ub/ubus/ubus.h @@ -33,6 +33,8 @@ enum ub_entity_type { #define is_p_idevice(uent) ((uent)->ent_type == UB_ENT_P_IDEVICE) #define is_idev(uent) (is_idevice(uent) || is_p_idevice(uent)) +#define ONE_CNA(uent) (is_switch(uent) || (uent)->port_nums == 1) + #define UB_COMPACT_EID_MASK GENMASK(19, 0) int ub_host_probe(void); -- Gitee From 89c8f0cb54744dc252265be39da73ede530252b5 Mon Sep 17 00:00:00 2001 From: Junlong Zheng Date: Fri, 19 Sep 2025 14:45:05 +0800 Subject: [PATCH 17/45] ub:ubus: Support for UB routing table configuration function driver inclusion category: feature bugzilla: https://gitee.com/openeuler/kernel/issues/ID34DG CVE: NA ----------------------------------------------------------- Support for device CNA routing node management and routing table configuration. Signed-off-by: Junlong Zheng --- drivers/ub/ubus/Makefile | 2 +- drivers/ub/ubus/route.c | 183 +++++++++++++++++++++++++++++++++++++++ drivers/ub/ubus/route.h | 15 ++++ drivers/ub/ubus/ubus.h | 13 +++ include/ub/ubus/ubus.h | 4 + 5 files changed, 216 insertions(+), 1 deletion(-) create mode 100644 drivers/ub/ubus/route.c create mode 100644 drivers/ub/ubus/route.h diff --git a/drivers/ub/ubus/Makefile b/drivers/ub/ubus/Makefile index caef4d64df35..9b0575e123be 100644 --- a/drivers/ub/ubus/Makefile +++ b/drivers/ub/ubus/Makefile @@ -2,6 +2,6 @@ obj-$(CONFIG_UB_UBUS) += ub-driver.o controller.o config.o -ubus-y := ubus_driver.o sysfs.o ubus_controller.o msg.o ubus_config.o port.o cc.o eid.o cna.o +ubus-y := ubus_driver.o sysfs.o ubus_controller.o msg.o ubus_config.o port.o cc.o eid.o cna.o route.o obj-$(CONFIG_UB_UBUS_BUS) += ubus.o diff --git a/drivers/ub/ubus/route.c b/drivers/ub/ubus/route.c new file mode 100644 index 000000000000..2fb8724bbb71 --- /dev/null +++ b/drivers/ub/ubus/route.c @@ -0,0 +1,183 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (c) HiSilicon Technologies Co., Ltd. 2025. All rights reserved. + */ + +#define pr_fmt(fmt) "ubus route: " fmt + +#include "ubus.h" +#include "port.h" + +#define UB_ROUTE_TABLE_ENTRY_START (UB_ROUTE_TABLE_SLICE_START + (0x10 << 2)) +#define EBW(port_nums) ((((port_nums) - 1) >> 5) + 1) /* Entry Bit Width */ +#define UB_ROUTE_TABLE_ENTRY_BITS SZ_128 + +/* node to update routing table */ +struct cna_node { + u32 cna; + u16 distance; /* distance of path */ + u16 count; /* number of ports that have route to this cna */ + bool need_update; /* need to write to routing table */ + + struct kref ref; + struct list_head node; +}; + +static struct cna_node *ub_create_cna_node(u32 cna, short distance) +{ + struct cna_node *new_cna; + + new_cna = kzalloc(sizeof(*new_cna), GFP_KERNEL); + if (!new_cna) + return NULL; + + new_cna->cna = cna; + new_cna->need_update = true; + new_cna->distance = distance; + new_cna->count = 1; + kref_init(&new_cna->ref); + INIT_LIST_HEAD(&new_cna->node); + + return new_cna; +} + +static void ub_cna_node_release(struct kref *kref) +{ + struct cna_node *cna = container_of(kref, struct cna_node, ref); + + kfree(cna); +} + +static struct cna_node *ub_cna_node_get(struct cna_node *cna) +{ + kref_get(&cna->ref); + return cna; +} + +static void ub_cna_node_put(struct cna_node *cna) +{ + kref_put(&cna->ref, ub_cna_node_release); +} + +static int ub_add_cna_node(u32 cna, short distance, struct list_head *cna_list) +{ + struct list_head *prev_head = cna_list; + struct cna_node *new_cna, *prev_cna; + + /** + * keep the cna list in ascending order to get the smallest cna quickly + * traverse from tail to end, insert new node when prev is smaller + */ + list_for_each_entry_reverse(prev_cna, cna_list, node) { + if (prev_cna->cna > cna) + continue; + + if (prev_cna->cna == cna) { + prev_cna->count++; + return 0; + } + + prev_head = &prev_cna->node; + break; + } + + new_cna = ub_create_cna_node(cna, distance); + if (!new_cna) + return -ENOMEM; + + list_add(&new_cna->node, prev_head); + return 0; +} + +static void ub_clear_cna_list(struct list_head *cna_list) +{ + struct cna_node *cna, *tmp; + + list_for_each_entry_safe(cna, tmp, cna_list, node) { + list_del(&cna->node); + ub_cna_node_put(cna); + } +} + +void ub_route_clear(struct ub_entity *uent) +{ + struct ub_port *port; + + if (!uent) + return; + + for_each_uent_port(port, uent) + bitmap_clear(port->cna_maps, 0, UB_ROUTE_TABLE_ENTRY_BITS); + + ub_clear_cna_list(&uent->cna_list); +} + +int ub_route_add_entry(struct ub_port *port, u32 cna, short distance) +{ + if (!port) + return -EINVAL; + + if (test_bit(cna, port->cna_maps)) + return -EEXIST; + + set_bit(cna, port->cna_maps); + return ub_add_cna_node(cna, distance, &port->uent->cna_list); +} + +static void ub_set_route_table_entry(struct ub_entity *uent, u32 dst_cna, + u32 *route_table_entry) +{ + int i; + u32 offset; + + /* Routing Table Block is not required for single-port devices. */ + if (uent->port_nums == 1) + return; + + pr_info("cna %#x uent set dstcna %#x route\n", uent->cna, dst_cna); + + for (i = 0; i < EBW(uent->port_nums); i++) { + offset = ((dst_cna + 1) * EBW(uent->port_nums) + i) << SZ_2; + ub_cfg_write_dword(uent, UB_ROUTE_TABLE_ENTRY_START + offset, + route_table_entry[i]); + } +} + +/** + * after updating routing table in software, this function must be called + * to sync the software routing table to config space + */ +void ub_route_sync_dev(struct ub_entity *uent) +{ + DECLARE_BITMAP(route_table_entry, UB_ROUTE_TABLE_ENTRY_BITS); + struct cna_node *cna_node, *tmp; + struct ub_port *port; + u32 cna; + + if (!uent || !ub_entity_test_priv_flag(uent, UB_ENTITY_ROUTE_UPDATED)) + return; + + list_for_each_entry_safe(cna_node, tmp, &uent->cna_list, node) { + if (!cna_node->need_update) + continue; + + cna = cna_node->cna; + bitmap_zero(route_table_entry, UB_ROUTE_TABLE_ENTRY_BITS); + + if (cna_node->count == 0) { + list_del(&cna_node->node); + kfree(cna_node); + goto set_entry; + } + + for_each_uent_port(port, uent) + if (test_bit(cna, port->cna_maps)) + set_bit(port->index, route_table_entry); + + cna_node->need_update = false; +set_entry: + ub_set_route_table_entry(uent, cna, (u32 *)route_table_entry); + } + + ub_entity_assign_priv_flag(uent, UB_ENTITY_ROUTE_UPDATED, false); +} diff --git a/drivers/ub/ubus/route.h b/drivers/ub/ubus/route.h new file mode 100644 index 000000000000..04bd052729a0 --- /dev/null +++ b/drivers/ub/ubus/route.h @@ -0,0 +1,15 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Copyright (c) HiSilicon Technologies Co., Ltd. 2025. All rights reserved. + */ + +#ifndef __ROUTE_H__ +#define __ROUTE_H__ + +struct ub_entity; +struct ub_port; +void ub_route_clear(struct ub_entity *uent); +int ub_route_add_entry(struct ub_port *port, u32 cna, short distance); +void ub_route_sync_dev(struct ub_entity *uent); + +#endif /* __ROUTE_H__ */ diff --git a/drivers/ub/ubus/ubus.h b/drivers/ub/ubus/ubus.h index e46611a1bdf0..3f0812e77212 100644 --- a/drivers/ub/ubus/ubus.h +++ b/drivers/ub/ubus/ubus.h @@ -37,6 +37,19 @@ enum ub_entity_type { #define UB_COMPACT_EID_MASK GENMASK(19, 0) +/* ub_entity priv_flags */ +#define UB_ENTITY_ROUTE_UPDATED 2 /* Flag indicate uent's route is updated */ +static inline void ub_entity_assign_priv_flag(struct ub_entity *uent, int bit, + bool flag) +{ + assign_bit(bit, &uent->priv_flags, flag); +} + +static inline bool ub_entity_test_priv_flag(struct ub_entity *uent, int bit) +{ + return test_bit(bit, &uent->priv_flags); +} + int ub_host_probe(void); void ub_host_remove(void); diff --git a/include/ub/ubus/ubus.h b/include/ub/ubus/ubus.h index a5ccac56a2a6..8d476a7d7c6b 100644 --- a/include/ub/ubus/ubus.h +++ b/include/ub/ubus/ubus.h @@ -101,6 +101,7 @@ struct ub_entity { struct ub_driver *driver; bool match_driver; /* Skip attaching driver before dev ready */ const char *driver_override; /* Driver name to force a match */ + unsigned long priv_flags; /* Private flags for the UB driver */ /* entity base info */ int ent_type; @@ -121,6 +122,9 @@ struct ub_entity { u16 port_nums; struct ub_port *ports; + /* entity route info */ + struct list_head cna_list; /* store distance for cna in route table */ + struct dev_message *message; u32 support_feature; -- Gitee From 0cd15f5dfe23707302055c2cee1d577d614fbabe Mon Sep 17 00:00:00 2001 From: Junlong Zheng Date: Fri, 19 Sep 2025 15:37:23 +0800 Subject: [PATCH 18/45] ub:ubus: Support for UB bus enumeration message initialization driver inclusion category: feature bugzilla: https://gitee.com/openeuler/kernel/issues/ID34DG CVE: NA ----------------------------------------------------------- Implement the UB protocol device enumeration message definition and initialize the corresponding message header section. Signed-off-by: Junlong Zheng --- drivers/ub/ubus/Makefile | 1 + drivers/ub/ubus/enum.c | 197 +++++++++++++++++++++++++++++++++++++++ drivers/ub/ubus/enum.h | 186 ++++++++++++++++++++++++++++++++++++ include/ub/ubus/ubus.h | 1 + 4 files changed, 385 insertions(+) create mode 100644 drivers/ub/ubus/enum.c create mode 100644 drivers/ub/ubus/enum.h diff --git a/drivers/ub/ubus/Makefile b/drivers/ub/ubus/Makefile index 9b0575e123be..fada110e4bf1 100644 --- a/drivers/ub/ubus/Makefile +++ b/drivers/ub/ubus/Makefile @@ -3,5 +3,6 @@ obj-$(CONFIG_UB_UBUS) += ub-driver.o controller.o config.o ubus-y := ubus_driver.o sysfs.o ubus_controller.o msg.o ubus_config.o port.o cc.o eid.o cna.o route.o +ubus-y += enum.o obj-$(CONFIG_UB_UBUS_BUS) += ubus.o diff --git a/drivers/ub/ubus/enum.c b/drivers/ub/ubus/enum.c new file mode 100644 index 000000000000..b298402e2352 --- /dev/null +++ b/drivers/ub/ubus/enum.c @@ -0,0 +1,197 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (c) HiSilicon Technologies Co., Ltd. 2025. All rights reserved. + */ + +#define pr_fmt(fmt) "ubus enum: " fmt + +#include "ubus.h" +#include "msg.h" +#include "port.h" +#include "enum.h" + +#define ENUM_MAX_HOPS 255 + +enum enum_topo_query_opcode { + ENUM_TOPO_QUERY_RESPONSE = 0, + ENUM_TOPO_QUERY_REQUEST +}; + +enum enum_na_cfg_opcode { + ENUM_NA_CFG_RESPONSE = 0, + ENUM_NA_CFG_PRIMARY = 2, + ENUM_NA_CFG_PORT = 3 +}; + +enum enum_hop_type { + ENUM_HOP_TYPE_4BIT = 0, + ENUM_HOP_TYPE_BYTE, + ENUM_HOP_TYPE_WORD +}; + +enum enum_na_query_opcode { + ENUM_NA_QUERY_RESPONSE = 0, + ENUM_NA_QUERY_SWITCH, + ENUM_NA_QUERY_DEVICE, + ENUM_NA_QUERY_PORT, +}; + +struct enum_na_cfg_req { + /* DW0~DW5 */ + struct enum_pld_scan_pdu_common common; + /* DW6 */ + u16 rsvd; + u16 port_idx; + /* DW7 */ + u32 cna : 24; + u32 rsvd1 : 8; +}; +#define ENUM_NA_CFG_REQ_SIZE 32 + +struct enum_na_cfg_rsp { + /* DW0~DW5 */ + struct enum_pld_scan_pdu_common common; +}; +#define ENUM_NA_CFG_RSP_SIZE 24 + +struct enum_na_query_req { + /* DW0~DW5 */ + struct enum_pld_scan_pdu_common common; + /* DW6 */ + u32 port_idx : 16; + u32 rsv : 16; +}; +#define ENUM_NA_QUERY_REQ_SIZE 28 + +struct enum_na_query_rsp { + /* DW0~DW5 */ + struct enum_pld_scan_pdu_common common; + /* DW6 */ + u32 cna : 24; + u32 rsvd : 8; +}; +#define ENUM_NA_QUERY_RSP_SIZE 28 + +static void ub_enum_pkt_header_init(void *buf) +{ + struct enum_pkt_header *header = (struct enum_pkt_header *)buf; + struct compact_network_header *cnth = &header->cnth; + struct ub_link_header *ulh = &header->ulh; + + ulh->cfg = UB_COMPACT_LINK_CFG; + cnth->nth_nlp = NTH_NLP_WITHOUT_TPH; + cnth->mgmt = 1; + header->upi = UB_CP_UPI; +} + +/* Attention: The caller must ensure that hop_type is valid */ +static size_t calc_forward_path_size(struct enum_pld_scan_header *header) +{ +#define FOUR_BITS_PER_DWORD 8 + u8 hop_bits[] = { SZ_4, SZ_8, SZ_16 }; + + /* Path size is hops * hop_bits[], then align it to 4byte */ + return ALIGN(hop_bits[header->bits.hop_type] * header->bits.hops / + hop_bits[0], FOUR_BITS_PER_DWORD) / SZ_2; +} + +static void set_hop_path(u8 *path, int type, u32 index, u32 val) +{ + u32 offset, mask; + u16 *p; + + switch (type) { + case ENUM_HOP_TYPE_4BIT: + offset = index / SZ_2; + mask = 0x0F << ((index % SZ_2) * SZ_4); + val = val << ((index % SZ_2) * SZ_4); + break; + case ENUM_HOP_TYPE_BYTE: + offset = index; + mask = GENMASK(7, 0); + break; + case ENUM_HOP_TYPE_WORD: + offset = index * SZ_2; + mask = GENMASK(15, 0); + break; + default: + return; + } + + p = (u16 *)(path + offset); + *p = (*p & ~mask) | (val & mask); +} + +static void ub_enum_pld_header_init(struct ub_entity *uent, void *buf) +{ + struct enum_pld_scan_header *header; + struct ub_entity *parent, *target; + struct ub_port *port; + u8 *path, *r_path; + size_t size; + int i, j; + + header = (struct enum_pld_scan_header *)(buf + ENUM_PKT_HEADER_SIZE); + header->bits.hop_type = ENUM_HOP_TYPE_BYTE; + header->bits.hops = (u32)uent->topo_rank; + header->bits.step = 0; + + if (header->bits.hops == 0) /* First hop doesn't need path */ + return; + + header->bits.r = 1; /* Use return path */ + + size = calc_forward_path_size(header); + + path = header->path; + r_path = path + size; + target = uent; + for (i = uent->topo_rank - 1; i >= 0; i--) { + parent = to_ub_entity(target->dev.parent); + for_each_uent_port(port, parent) { + if (port->r_uent == target) { + set_hop_path(path, header->bits.hop_type, + (u32)i, (u32)port->index); + j = uent->topo_rank - 1 - i; + set_hop_path(r_path, header->bits.hop_type, + (u32)j, (u32)port->r_index); + break; + } + } + target = parent; + } +} + +/* rsp should check return value, req is inside caller, don't need */ +size_t calc_enum_pld_header_size(struct enum_pld_scan_header *header, bool req) +{ + size_t bytes; + + if (!req && header->bits.hop_type > ENUM_HOP_TYPE_WORD) { + pr_err("rsp header hop_type error, type=%u\n", header->bits.hop_type); + return 0; + } + + bytes = calc_forward_path_size(header); + + if (req && header->bits.r) + bytes <<= 1; + + bytes += ENUM_PLD_SCAN_HEADER_BASE_SIZE; + + return bytes; +} +EXPORT_SYMBOL_GPL(calc_enum_pld_header_size); + +/* pkt header + scan header */ +static size_t ub_enum_calc_header_sz(void *buf, bool req) +{ + size_t bytes; + + bytes = calc_enum_pld_header_size((struct enum_pld_scan_header *)(buf + + ENUM_PKT_HEADER_SIZE), req); + if (bytes == 0) + return 0; + + return bytes + ENUM_PKT_HEADER_SIZE; +} diff --git a/drivers/ub/ubus/enum.h b/drivers/ub/ubus/enum.h new file mode 100644 index 000000000000..db80b7040286 --- /dev/null +++ b/drivers/ub/ubus/enum.h @@ -0,0 +1,186 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Copyright (c) HiSilicon Technologies Co., Ltd. 2025. All rights reserved. + */ + +#ifndef __ENUM_H__ +#define __ENUM_H__ + +enum enum_cmd { + ENUM_CMD_TOPO_QUERY = 0, + ENUM_CMD_NA_CFG, + ENUM_CMD_NA_QUERY +}; + +/* + * enum pkt : enum_pkt_header + enum_pld_scan_header + reqX + * reqX : enum_topo_query_req or enum_na_cfg_req or enum_na_query_req + */ +struct enum_pkt_header { + /* DW0 */ + struct ub_link_header ulh; + /* DW1-DW2 */ + struct compact_network_header cnth; + /* DW3 */ + u16 rsv; + u16 upi; + + /* DW4~ */ + char payload[]; +}; +#define ENUM_PKT_HEADER_SIZE 16 + +struct enum_pld_scan_header { + /* DW0 */ + union { + struct { + u32 step : 8; + u32 hops : 8; + u32 hop_type : 4; + u32 r : 1; + u32 rsv : 11; + } bits; + u32 dw0; + }; + /* DW1~ */ + u8 path[]; /* include forward & return, 4byte align */ +}; +#define ENUM_PLD_SCAN_HEADER_BASE_SIZE 4 /* exclusive path */ + +struct enum_pld_scan_pdu_common { + /* DW0 */ + union { + struct { + union { + u8 status; + u8 slice_id; + }; + u8 opcode; + u8 cmd; +#define UB_ENUM_MNG_VERSION 0x1 + u8 version; + } bits; + u32 dw0; + }; + /* DW1 */ + u32 msn : 16; + u32 pdu_len : 8; + u32 msgq_id : 8; + /* DW2~DW5 */ + guid_t guid; +}; +#define ENUM_PLD_SCAN_PDU_COMMON_SIZE 24 + +struct enum_topo_query_req { + /* DW0~DW5 */ + struct enum_pld_scan_pdu_common common; +}; +#define ENUM_TOPO_QUERY_REQ_SIZE 24 + +struct enum_tlv_port_info { + /* DW0 */ + union { + struct { + u32 rsvd : 8; + u32 s : 1; + u32 b : 1; + u32 w : 1; + u32 t : 1; + u32 rsvd1 : 4; + u32 len : 8; + u32 type : 8; + } bits0; + u32 dw0; + }; + /* DW1 */ + u16 remote_port_idx; + u16 local_port_idx; + + /* DW2 */ + u16 cur_rate; + u16 max_rate; + + /* DW3~DW6 */ + guid_t remote_guid; +}; +#define ENUM_TOPO_QUERY_RSP_PORT_SIZE 28 + +struct enum_topo_query_rsp { + /* DW0~DW5 */ + struct enum_pld_scan_pdu_common common; + /* DW6 */ + union { + struct { + u32 num_seg : 4; + u32 rsv0 : 4; + u32 lf : 1; + u32 lp : 1; + u32 rsv1 : 6; + u32 mtu : 3; + u32 rsv2 : 5; + u32 sup_mtu : 3; + u32 rsv3 : 5; + } bits; + u32 dw6; + }; + /* DW7 */ + u32 num_lf_entries; + /* DW8 */ + u32 num_lp_entries; + /* DW9 */ + u16 num_ports; /* Number of ports in the current message */ + u16 total_num_ports; /* Total number of ports on the target device */ + /* DW10~ */ + struct enum_tlv_port_info port_info[]; +}; +#define ENUM_TOPO_QUERY_RSP_BASE_SIZE 40 /* exclusive port_info */ + +enum enum_tlv_type { /* M: Mandatory , O: optional */ + TLV_SLICE_INFO = 0, /* M */ + TLV_PORT_NUM = 1, /* M */ + TLV_PORT_INFO = 2, /* M */ + TLV_CAP_INFO = 4 /* M */ +}; + +struct enum_tlv_common { + u32 value : 16; + u32 len : 8; + u32 type : 8; +}; + +struct enum_tlv_slice_info { + u32 slice_id : 8; + u32 total_slice : 8; + u32 len : 8; + u32 type : 8; +}; +#define ENUM_TLV_SLICE_INFO_SZ 4 + +struct enum_tlv_port_num { + u32 total_num_ports : 16; + u32 len : 8; + u32 type : 8; + + u32 rsvd : 16; + u32 num_port_tlv : 16; +}; +#define ENUM_TLV_PORT_NUM_SZ 8 + +struct enum_tlv_cap_info { + u32 da : 1; + u32 rsvd0 : 7; + u32 mtu : 3; + u32 rsvd1 : 1; + u32 sup_mtu : 3; + u32 rsvd2 : 1; + u32 len : 8; + u32 type : 8; + + u32 class_code : 16; + u32 rsvd3 : 16; +}; +#define ENUM_TLV_CAP_INFO_SZ 8 + +size_t calc_enum_pld_header_size(struct enum_pld_scan_header *header, bool req); + +#endif /* __ENUM_H__ */ diff --git a/include/ub/ubus/ubus.h b/include/ub/ubus/ubus.h index 8d476a7d7c6b..679d79c76213 100644 --- a/include/ub/ubus/ubus.h +++ b/include/ub/ubus/ubus.h @@ -117,6 +117,7 @@ struct ub_entity { /* entity topology info */ struct ub_bus_controller *ubc; struct ub_entity *pue; /* ue/mue connected to their mue */ + int topo_rank; /* The levels of Breadth-First Search */ /* entity port info */ u16 port_nums; -- Gitee From 728a07ef67139b04dd29636e89e943205b7dc3cc Mon Sep 17 00:00:00 2001 From: Jianquan Lin Date: Fri, 19 Sep 2025 16:01:00 +0800 Subject: [PATCH 19/45] ub:ubus: Add UBUS resource space framework driver inclusion category: feature bugzilla: https://gitee.com/openeuler/kernel/issues/ID34DG CVE: NA ----------------------------------------------------------- Add basic framework for UBUS resource space, including reading/writing functions, and so on. Signed-off-by: Jianquan Lin --- drivers/ub/ubus/Makefile | 2 +- drivers/ub/ubus/resource.c | 174 +++++++++++++++++++++++++++++++++++++ drivers/ub/ubus/resource.h | 13 +++ include/ub/ubus/ubus.h | 73 ++++++++++++++++ 4 files changed, 261 insertions(+), 1 deletion(-) create mode 100644 drivers/ub/ubus/resource.c create mode 100644 drivers/ub/ubus/resource.h diff --git a/drivers/ub/ubus/Makefile b/drivers/ub/ubus/Makefile index fada110e4bf1..6b61b611ee22 100644 --- a/drivers/ub/ubus/Makefile +++ b/drivers/ub/ubus/Makefile @@ -3,6 +3,6 @@ obj-$(CONFIG_UB_UBUS) += ub-driver.o controller.o config.o ubus-y := ubus_driver.o sysfs.o ubus_controller.o msg.o ubus_config.o port.o cc.o eid.o cna.o route.o -ubus-y += enum.o +ubus-y += enum.o resource.o obj-$(CONFIG_UB_UBUS_BUS) += ubus.o diff --git a/drivers/ub/ubus/resource.c b/drivers/ub/ubus/resource.c new file mode 100644 index 000000000000..9ec78931a744 --- /dev/null +++ b/drivers/ub/ubus/resource.c @@ -0,0 +1,174 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (c) HiSilicon Technologies Co., Ltd. 2025. All rights reserved. + */ + +#define pr_fmt(fmt) "ubus resource: " fmt + +#include +#include + +#include "ubus.h" +#include "msg.h" +#include "resource.h" + +static const struct vm_operations_struct ub_phys_vm_ops = { + .access = generic_access_phys, +}; + +int ub_mmap_resource_range(struct ub_entity *uent, unsigned long idx, + struct vm_area_struct *vma, int write_combine) +{ + resource_size_t size; + + size = ((ub_resource_len(uent, idx) - 1) >> PAGE_SHIFT) + 1; + if (vma->vm_pgoff + vma_pages(vma) > size) + return -EINVAL; + + if (write_combine) + vma->vm_page_prot = pgprot_writecombine(vma->vm_page_prot); + else + vma->vm_page_prot = pgprot_device(vma->vm_page_prot); + + vma->vm_pgoff += (ub_resource_start(uent, idx) >> PAGE_SHIFT); + vma->vm_ops = &ub_phys_vm_ops; + + return io_remap_pfn_range(vma, vma->vm_start, vma->vm_pgoff, + vma->vm_end - vma->vm_start, + vma->vm_page_prot); +} + +static void __iomem *ub_iomap_range(struct ub_entity *uent, int res_id, + unsigned long offset, unsigned long maxlen, + bool is_wc) +{ + resource_size_t start = ub_resource_start(uent, res_id); + resource_size_t len = ub_resource_len(uent, res_id); + unsigned long flags = ub_resource_flags(uent, res_id); + + if (len <= offset || !start) + return NULL; + len -= offset; + start += offset; + if (maxlen && len > maxlen) + len = maxlen; + if (!(flags & IORESOURCE_MEM)) + return NULL; + + if (is_wc) + return ioremap_wc(start, len); + else + return ioremap(start, len); +} + +void __iomem *ub_iomap(struct ub_entity *uent, int res_id, unsigned long maxlen) +{ + if (res_id >= MAX_UB_RES_NUM || !uent) + return NULL; + + return ub_iomap_range(uent, res_id, 0, maxlen, false); +} +EXPORT_SYMBOL_GPL(ub_iomap); + +void __iomem *ub_iomap_wc(struct ub_entity *uent, int res_id, unsigned long maxlen) +{ + if (res_id >= MAX_UB_RES_NUM || !uent) + return NULL; + + return ub_iomap_range(uent, res_id, 0, maxlen, true); +} +EXPORT_SYMBOL_GPL(ub_iomap_wc); + +void ub_iounmap(void __iomem *addr) +{ + iounmap(addr); +} +EXPORT_SYMBOL_GPL(ub_iounmap); + +static int ub_read_ubba_reg(struct ub_entity *uent, size_t pg_size, int idx) +{ + u32 addr_l_reg[MAX_UB_RES_NUM] = { UB_ERS0_UBBA_L, UB_ERS1_UBBA_L, + UB_ERS2_UBBA_L }; + u32 addr_h_reg[MAX_UB_RES_NUM] = { UB_ERS0_UBBA_H, UB_ERS1_UBBA_H, + UB_ERS2_UBBA_H }; + u32 ss_reg[MAX_UB_RES_NUM] = { UB_ERS0_SS, UB_ERS1_SS, UB_ERS2_SS }; + u32 hpa_l = 0, hpa_h = 0, size = 0; + int ret; + + ret = ub_cfg_read_dword(uent, addr_l_reg[idx], &hpa_l); + ret |= ub_cfg_read_dword(uent, addr_h_reg[idx], &hpa_h); + ret |= ub_cfg_read_dword(uent, ss_reg[idx], &size); + if (ret) { + ub_err(uent, "read reg failed when request HPA resource\n"); + return -EINVAL; + } else if (size == 0) { + ub_err(uent, "size is 0 when request HPA resource\n"); + return -EINVAL; + } + + uent->zone[idx].res.start = ubba_gen(hpa_h, hpa_l); + uent->zone[idx].res.end = + uent->zone[idx].res.start + ((u64)size * pg_size - 1); + + uent->zone[idx].res.flags = IORESOURCE_MEM; + uent->zone[idx].res.name = ub_name(uent); + uent->zone[idx].ubba_used = 1; + + ub_info(uent, "mmio idx %d, %pR\n", idx, &uent->zone[idx].res); + return 0; +} + +static int ub_read_sa_reg(struct ub_entity *uent, size_t pg_size, int idx) +{ + u32 sa_l_reg[MAX_UB_RES_NUM] = { UB_ERS0_SA_L, UB_ERS1_SA_L, + UB_ERS2_SA_L }; + u32 sa_h_reg[MAX_UB_RES_NUM] = { UB_ERS0_SA_H, UB_ERS1_SA_H, + UB_ERS2_SA_H }; + u32 ss_reg[MAX_UB_RES_NUM] = { UB_ERS0_SS, UB_ERS1_SS, UB_ERS2_SS }; + u32 sa_l = 0, sa_h = 0, size = 0; + int ret; + + ret = ub_cfg_read_dword(uent, sa_l_reg[idx], &sa_l); + ret |= ub_cfg_read_dword(uent, sa_h_reg[idx], &sa_h); + ret |= ub_cfg_read_dword(uent, ss_reg[idx], &size); + if (ret) { + ub_err(uent, "read reg failed when request SA resource\n"); + return -EINVAL; + } else if (size == 0) { + ub_err(uent, "size is 0 when request SA resource\n"); + return -EINVAL; + } + + /* Shift left 32 bits to get upper-bit address */ + uent->zone[idx].region.start = ((u64)sa_h << 32) | sa_l; + uent->zone[idx].region.size = size * pg_size; + uent->zone[idx].res.flags = IORESOURCE_MEM; + uent->zone[idx].sa_used = 1; + + return 0; +} + +static int ub_entity_read_mmio_idx(struct ub_entity *uent, int idx) +{ + struct ub_entity *pue; + size_t pg_size = 0; + + /* ue to its mue, mue to Entity0, Entity0 use itself */ + pue = uent->pue; + if (pue->entity_idx) /* to ensure Entity0 uent */ + pue = pue->pue; + + (void)ub_cfg_read_byte(uent, UB_SYS_PGS, (u8 *)&pg_size); + if ((u8)pg_size & UB_SYS_PGS_SIZE) + pg_size = SZ_64K; + else + pg_size = SZ_4K; + + if (pue->zone[idx].ubba_used) + return ub_read_ubba_reg(uent, pg_size, idx); + + if (pue->zone[idx].sa_used) + return ub_read_sa_reg(uent, pg_size, idx); + + return -EPERM; +} diff --git a/drivers/ub/ubus/resource.h b/drivers/ub/ubus/resource.h new file mode 100644 index 000000000000..70c72ef0bca4 --- /dev/null +++ b/drivers/ub/ubus/resource.h @@ -0,0 +1,13 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Copyright (c) HiSilicon Technologies Co., Ltd. 2025. All rights reserved. + */ + +#ifndef __RESOURCE_H__ +#define __RESOURCE_H__ + +int ub_mmap_resource_range(struct ub_entity *uent, unsigned long idx, + struct vm_area_struct *vma, int write_combine); +void ub_entity_free_mmio_idx(struct ub_entity *dev, int idx); + +#endif /* __RESOURCE_H__ */ diff --git a/include/ub/ubus/ubus.h b/include/ub/ubus/ubus.h index 679d79c76213..b9154c9aae9a 100644 --- a/include/ub/ubus/ubus.h +++ b/include/ub/ubus/ubus.h @@ -28,8 +28,32 @@ .mod_vendor = (u32)UB_ANY_ID, .module = (u32)UB_ANY_ID, \ .class_code = (dev_class), .class_mask = (dev_class_mask) +#define ub_resource_start(dev, resno) ((dev)->zone[(resno)].res.start) +#define ub_resource_end(dev, resno) ((dev)->zone[(resno)].res.end) +#define ub_resource_flags(dev, resno) ((dev)->zone[(resno)].res.flags) +#define ub_resource_len(dev, resno) \ + ((ub_resource_start((dev), (resno)) == 0 && \ + ub_resource_end((dev), (resno)) == \ + ub_resource_start((dev), (resno))) ? 0 : \ + (ub_resource_end((dev), (resno)) - \ + ub_resource_start((dev), (resno)) + 1)) + #define UB_GUID_DW_NUM SZ_4 +struct ub_bus_region { + unsigned long start; + unsigned long size; +}; + +struct mmio_zone { + struct resource res; + struct ub_bus_region region; + u8 ubba_used; + u8 sa_used; + u8 init_succ; + u8 decoder_mapped; +}; + struct ub_guid { union { struct { @@ -71,6 +95,8 @@ static inline int ub_show_guid(struct ub_guid *guid, char *buf) guid->bits.reserved, guid->bits.seq_num); } +#define MAX_UB_RES_NUM 3 + enum ub_port_type { PHYSICAL, VIRTUAL, @@ -113,6 +139,7 @@ struct ub_entity { unsigned int eid; unsigned short entity_idx; u32 uent_num; /* ub dev number */ + struct mmio_zone zone[MAX_UB_RES_NUM]; /* entity topology info */ struct ub_bus_controller *ubc; @@ -267,6 +294,45 @@ extern struct bus_type ub_bus_type; void ub_bus_type_iommu_ops_set(const struct iommu_ops *ops); const struct iommu_ops *ub_bus_type_iommu_ops_get(void); +/** + * ub_iomap() - Map the resource space of the entity. + * @uent: UB entity. + * @resno: Resource Number. + * @maxlen: Map size. + * + * Invoke ioremap() to map the entity resource space, if maxlen is 0, then map + * size is ub_resource_len(), else map size is min(maxlen, ub_resource_len()). + * + * Context: Any context. + * Return: The return value is the same as that of ioremap(). + */ +void __iomem *ub_iomap(struct ub_entity *uent, int resno, unsigned long maxlen); + +/** + * ub_iomap_wc() - Map the resource space of the entity. + * @uent: UB entity. + * @resno: Resource Number. + * @maxlen: Map size. + * + * Invoke ioremap_wc() to map the entity resource space, if maxlen is 0, + * then map size is ub_resource_len(), else map size is + * min(maxlen, ub_resource_len()). + * + * Context: Any context. + * Return: The return value is the same as that of ioremap_wc(). + */ +void __iomem *ub_iomap_wc(struct ub_entity *uent, int resno, unsigned long maxlen); + +/** + * ub_iounmap() - Unmap the resource space of the entity. + * @addr: Target resource address. + * + * Invoke iounmap() to unmap the entity resource space. + * + * Context: Any context. + */ +void ub_iounmap(void __iomem *addr); + /** * ub_register_share_port() - Register a share port. * @uent: UB entity. @@ -416,6 +482,13 @@ void ub_unregister_driver(struct ub_driver *drv); static inline struct ub_entity *ub_entity_get(struct ub_entity *uent) { return NULL; } static inline void ub_entity_put(struct ub_entity *uent) {} +static inline void __iomem * +ub_iomap(struct ub_entity *uent, int resno, unsigned long maxlen) +{ return NULL; } +static inline void __iomem * +ub_iomap_wc(struct ub_entity *uent, int resno, unsigned long maxlen) +{ return NULL; } +static inline void ub_iounmap(void __iomem *addr) {} static inline bool ub_cc_supported(struct ub_entity *uent) { return false; } static inline int ub_cc_enable(struct ub_entity *uent) -- Gitee From f50b4aa58affd23f423825cb226ec0ea08d9b63d Mon Sep 17 00:00:00 2001 From: Jianquan Lin Date: Fri, 19 Sep 2025 16:30:54 +0800 Subject: [PATCH 20/45] ub:ubus: Add UBUS resource space basic functions driver inclusion category: feature bugzilla: https://gitee.com/openeuler/kernel/issues/ID34DG CVE: NA ----------------------------------------------------------- Add UBUS resource allocate/release and set-up and unset functions. Signed-off-by: Jianquan Lin --- drivers/ub/ubus/resource.c | 285 +++++++++++++++++++++++++++++++++++++ drivers/ub/ubus/resource.h | 5 + include/ub/ubus/ubus.h | 2 + 3 files changed, 292 insertions(+) diff --git a/drivers/ub/ubus/resource.c b/drivers/ub/ubus/resource.c index 9ec78931a744..ebb1a529af77 100644 --- a/drivers/ub/ubus/resource.c +++ b/drivers/ub/ubus/resource.c @@ -7,11 +7,38 @@ #include #include +#include #include "ubus.h" #include "msg.h" #include "resource.h" +struct query_token_msg_pld_req { + u32 token_enable : 1; + u32 reserved : 31; +}; +#define QUERY_TOKEN_MSG_REQ_PLD_SIZE 4 +#define QUERY_TOKEN_MSG_REQ_SIZE 36 + +struct query_token_msg_pld_rsp { + u32 token_id : 20; + u32 reserved : 12; + u32 token_value; +}; +#define QUERY_TOKEN_MSG_RSP_SIZE 40 + +struct query_token_msg_pld { + union { + struct query_token_msg_pld_req req; + struct query_token_msg_pld_rsp rsp; + }; +}; + +struct query_token_msg_pkt { + struct msg_pkt_header header; + struct query_token_msg_pld pld; +}; + static const struct vm_operations_struct ub_phys_vm_ops = { .access = generic_access_phys, }; @@ -38,6 +65,73 @@ int ub_mmap_resource_range(struct ub_entity *uent, unsigned long idx, vma->vm_page_prot); } +static int _ub_assign_resource(struct ub_entity *uent, int idx, + resource_size_t size, resource_size_t align) +{ + struct resource *res; + struct resource_entry *entry, *tmp; + struct resource *uent_res = &uent->zone[idx].res; + struct ub_bus_controller *ubc = uent->ubc; + int ret = -EINVAL; + + if (ubc == NULL) { + ub_err(uent, "ub assign resource failed, ubc is NULL.\n"); + return -EINVAL; + } + + resource_list_for_each_entry_safe(entry, tmp, &ubc->resources) { + res = entry->res; + + if (!(res->flags & IORESOURCE_MEM) || + !strstr(res->name, "UB_BUS_CTL")) + continue; + ret = allocate_resource(res, uent_res, size, 0, ~0, align, NULL, + NULL); + if (ret == 0) + return 0; + } + ub_err(uent, "ub assign resource failed, no res available, ret = %d\n", ret); + + return -ENOMEM; +} + +int ub_assign_resource(struct ub_entity *uent, int idx) +{ + struct resource *res = &uent->zone[idx].res; + resource_size_t size; + resource_size_t page_size = PAGE_SIZE; + /* decoder mapped address unit 1M */ + resource_size_t align = page_size > SZ_1M ? page_size : SZ_1M; + int ret; + + res->name = ub_name(uent); + res->flags |= IORESOURCE_UNSET | IORESOURCE_SIZEALIGN; + size = uent->zone[idx].region.size; + ret = _ub_assign_resource(uent, idx, size, align); + if (ret < 0) { + ub_err(uent, "RESOURCE %d: failed to assign, size=%#llx\n", + idx, size); + return ret; + } + + res->flags &= ~IORESOURCE_UNSET; + res->flags &= ~IORESOURCE_STARTALIGN; + ub_info(uent, "RESOURCE %d: assigned %pR\n", idx, res); + + return 0; +} + +void ub_release_resource(struct ub_entity *uent, int idx) +{ + int ret; + + if (uent->zone[idx].res.parent) { + ret = release_resource(&uent->zone[idx].res); + if (ret) + ub_err(uent, "resource release failed, ret=%d.\n", ret); + } +} + static void __iomem *ub_iomap_range(struct ub_entity *uent, int res_id, unsigned long offset, unsigned long maxlen, bool is_wc) @@ -172,3 +266,194 @@ static int ub_entity_read_mmio_idx(struct ub_entity *uent, int idx) return -EPERM; } + +int ub_insert_resource(struct ub_entity *dev, int idx) +{ + struct resource *res, *root = NULL; + int ret; + + if (idx >= MAX_UB_RES_NUM) + return -EINVAL; + + res = &dev->zone[idx].res; + if (!(res->flags & IORESOURCE_MEM)) + return -EINVAL; + + root = &iomem_resource; + + ret = insert_resource(root, res); + if (ret) { + ub_warn(dev, "failed to claim uent resource %pR\n", res); + return -ENOMEM; + } + + return 0; +} + +int ub_entity_alloc_mmio_idx(struct ub_entity *dev, int idx) +{ + if (is_ibus_controller(dev) || is_idev(dev)) + return ub_insert_resource(dev, idx); + return ub_assign_resource(dev, idx); +} + +void ub_entity_free_mmio_idx(struct ub_entity *dev, int idx) +{ + ub_release_resource(dev, idx); + dev->zone[idx].init_succ = 0; + dev->zone[idx].ubba_used = 0; + dev->zone[idx].sa_used = 0; +} + +static int ub_query_token_rsp_handle(struct ub_entity *uent, + struct query_token_msg_pkt *pkt) +{ + struct query_token_msg_pld_rsp *rsp = &pkt->pld.rsp; + + if (pkt->header.msgetah.rsp_status != UB_MSG_RSP_SUCCESS) { + ub_err(uent, "query token rsp, status=%#02x\n", + pkt->header.msgetah.rsp_status); + return -EINVAL; + } + + uent->token_id = rsp->token_id; + uent->token_value = rsp->token_value; + + return 0; +} + +static int ub_query_token_info(struct ub_entity *uent) +{ + struct message_device *mdev = uent->message->mdev; + struct query_token_msg_pkt req_pkt = {}; + struct query_token_msg_pkt rsp_pkt = {}; + struct msg_info info = {}; + int ret; + + ub_msg_pkt_header_init(&req_pkt.header, uent, + QUERY_TOKEN_MSG_REQ_PLD_SIZE, + code_gen(UB_MSG_CODE_SEC, UB_TOKEN_CHECK_CFG, + MSG_REQ), false); + + req_pkt.pld.req.token_enable = 1; + message_info_init(&info, uent, &req_pkt, &rsp_pkt, + (QUERY_TOKEN_MSG_REQ_SIZE << MSG_REQ_SIZE_OFFSET) | + QUERY_TOKEN_MSG_RSP_SIZE); + + ret = message_sync_request(mdev, &info, req_pkt.header.msgetah.code); + if (ret) + return ret; + + return ub_query_token_rsp_handle(uent, &rsp_pkt); +} + +static int ub_entity_writeback_addr(struct ub_entity *uent, int idx) +{ + u32 addr_l_reg[MAX_UB_RES_NUM] = { UB_ERS0_UBBA_L, UB_ERS1_UBBA_L, + UB_ERS2_UBBA_L }; + u32 addr_h_reg[MAX_UB_RES_NUM] = { UB_ERS0_UBBA_H, UB_ERS1_UBBA_H, + UB_ERS2_UBBA_H }; + u32 support_feature, ubba_l, ubba_h; + int ret; + + if (!uent->zone[idx].sa_used) + return 0; + + ret = ub_cfg_read_dword(uent, UB_CFG1_SUPPORT_FEATURE_L, + &support_feature); + if (ret) { + ub_err(uent, + "read reg failed when request sup feature, ret=%d\n", + ret); + return -EINVAL; + } + if (support_feature & UB_UBBAS_SUPPORT) { + ubba_l = (u32)(uent->zone[idx].res.start); + /* Shift right 32 bits to get upper-bit address */ + ubba_h = (u32)((uent->zone[idx].res.start) >> 32); + ub_cfg_write_dword(uent, addr_l_reg[idx], ubba_l); + ub_cfg_write_dword(uent, addr_h_reg[idx], ubba_h); + } + + return 0; +} + +int _ub_entity_setup_mmio(struct ub_entity *dev) +{ + int ret; + int i; + + if (is_ibus_controller(dev)) { + ub_info(dev, "now doesn't support ub bus controller mmio\n"); + return 0; + } + + if (is_device(dev) && !is_p_device(dev)) { + ret = ub_query_token_info(dev); + if (ret) { + ub_err(dev, "Token query failed\n"); + return ret; + } + ub_info(dev, "Token ID=%#x\n", dev->token_id); + } + + for (i = 0; i < MAX_UB_RES_NUM; i++) { + ret = ub_entity_read_mmio_idx(dev, i); + if (ret) + continue; + + ret = ub_entity_alloc_mmio_idx(dev, i); + if (ret) { + ub_err(dev, + "failed to alloc resource for mmio idx %d\n", i); + goto fail; + } + + /* Write back the address reg for the VM scenario capture. */ + ret = ub_entity_writeback_addr(dev, i); + if (ret) { + ub_err(dev, + "failed to write back address for mmio idx %d\n", + i); + ub_entity_free_mmio_idx(dev, i); + goto fail; + } + + ub_info(dev, "MMIO[%d]: ubba=%#lx, size=%#llx, hpa=%#llx, wr attr=%01u, prefetchable=%01u, order type=%01u,\n", + i, dev->zone[i].region.start, ub_resource_len(dev, i), + dev->zone[i].res.start, 0, 0, 0); + dev->zone[i].init_succ = 1; + } + + return 0; + +fail: + for (i -= 1; i >= 0; i--) + if (dev->zone[i].init_succ) + ub_entity_free_mmio_idx(dev, i); + + return ret; +} + +int ub_entity_setup_mmio(struct ub_entity *uent) +{ + int i; + + for (i = 0; i < MAX_UB_RES_NUM; i++) + if (!uent->zone[i].init_succ) + break; + + if (i == MAX_UB_RES_NUM) + return 0; + + return _ub_entity_setup_mmio(uent); +} + +void ub_entity_unset_mmio(struct ub_entity *dev) +{ + int i; + + for (i = 0; i < MAX_UB_RES_NUM; i++) + if (dev->zone[i].init_succ) + ub_entity_free_mmio_idx(dev, i); +} diff --git a/drivers/ub/ubus/resource.h b/drivers/ub/ubus/resource.h index 70c72ef0bca4..90da3c402dc0 100644 --- a/drivers/ub/ubus/resource.h +++ b/drivers/ub/ubus/resource.h @@ -6,8 +6,13 @@ #ifndef __RESOURCE_H__ #define __RESOURCE_H__ +struct ub_entity; +int _ub_entity_setup_mmio(struct ub_entity *uent); +int ub_entity_setup_mmio(struct ub_entity *dev); +void ub_entity_unset_mmio(struct ub_entity *dev); int ub_mmap_resource_range(struct ub_entity *uent, unsigned long idx, struct vm_area_struct *vma, int write_combine); +int ub_insert_resource(struct ub_entity *dev, int idx); void ub_entity_free_mmio_idx(struct ub_entity *dev, int idx); #endif /* __RESOURCE_H__ */ diff --git a/include/ub/ubus/ubus.h b/include/ub/ubus/ubus.h index b9154c9aae9a..4b7df4bb80b6 100644 --- a/include/ub/ubus/ubus.h +++ b/include/ub/ubus/ubus.h @@ -140,6 +140,8 @@ struct ub_entity { unsigned short entity_idx; u32 uent_num; /* ub dev number */ struct mmio_zone zone[MAX_UB_RES_NUM]; + u32 token_id; + u32 token_value; /* entity topology info */ struct ub_bus_controller *ubc; -- Gitee From 9a45f0505093fd3a1da9bcfb0f935c3d2278f7dc Mon Sep 17 00:00:00 2001 From: Junlong Zheng Date: Fri, 19 Sep 2025 16:26:49 +0800 Subject: [PATCH 21/45] ub:ubus: Support enumeration topology query commands driver inclusion category: feature bugzilla: https://gitee.com/openeuler/kernel/issues/ID34DG CVE: NA ----------------------------------------------------------- Implement device topology query message transmission and reception, and parse response message information. Signed-off-by: Junlong Zheng --- drivers/ub/ubus/enum.c | 336 +++++++++++++++++++++++++++++++++++++++++ drivers/ub/ubus/enum.h | 2 + drivers/ub/ubus/msg.c | 9 ++ drivers/ub/ubus/msg.h | 5 + include/ub/ubus/ubus.h | 1 + 5 files changed, 353 insertions(+) diff --git a/drivers/ub/ubus/enum.c b/drivers/ub/ubus/enum.c index b298402e2352..315077f184da 100644 --- a/drivers/ub/ubus/enum.c +++ b/drivers/ub/ubus/enum.c @@ -195,3 +195,339 @@ static size_t ub_enum_calc_header_sz(void *buf, bool req) return bytes + ENUM_PKT_HEADER_SIZE; } + +static struct topo_scan_info { +#define UB_TOPO_BUF_SZ SZ_4K + void *buf; + struct list_head dev_list; +} topo_scan; + +static int ub_enum_topo_scan_init(void) +{ + topo_scan.buf = kzalloc(UB_TOPO_BUF_SZ, GFP_KERNEL); + if (!topo_scan.buf) + return -ENOMEM; + + INIT_LIST_HEAD(&topo_scan.dev_list); + + return 0; +} + +static void ub_enum_topo_scan_uninit(void) +{ + kfree(topo_scan.buf); +} + +static inline void ub_enum_refresh_and_init_buffer_header(struct ub_entity *uent, + void *buf) +{ + memset(buf, 0, UB_TOPO_BUF_SZ); + ub_enum_pkt_header_init(buf); + ub_enum_pld_header_init(uent, buf); +} + +static void ub_enum_pld_common_setup(struct enum_pld_scan_pdu_common *common, + u8 cmd, u8 opcode, guid_t *guid, + int plen) +{ + common->bits.version = UB_ENUM_MNG_VERSION; + common->bits.cmd = cmd; + common->bits.opcode = opcode; + common->msn = 0; + common->guid = *guid; + common->pdu_len = (u8)(plen / SZ_4); +} + +static int ub_enum_topo_query(struct ub_entity *uent, void *buf, u16 start_idx, + u16 *actual_rsp_size) +{ + struct message_device *mdev = uent->message->mdev; + struct enum_topo_query_req *req; + struct msg_info info = {}; + size_t header_sz; + int ret; + + ub_enum_refresh_and_init_buffer_header(uent, buf); + + header_sz = ub_enum_calc_header_sz(buf, true); + req = (struct enum_topo_query_req *)(buf + header_sz); + ub_enum_pld_common_setup(&req->common, ENUM_CMD_TOPO_QUERY, + ENUM_TOPO_QUERY_REQUEST, &uent->guid.id, + ENUM_TOPO_QUERY_REQ_SIZE); + + req->common.bits.slice_id = start_idx; + + message_info_init(&info, uent, buf, buf, + ((header_sz + ENUM_TOPO_QUERY_REQ_SIZE) << + MSG_REQ_SIZE_OFFSET) | UB_TOPO_BUF_SZ); + + ret = message_sync_enum(mdev, &info, ENUM_CMD_TOPO_QUERY); + if (!ret) + *actual_rsp_size = info.actual_rsp_size; + + return ret; +} + +static struct enum_topo_query_rsp * +ub_enum_topo_query_and_check(struct ub_entity *uent, void *buf, u16 start_idx) +{ + struct device *dev = &uent->ubc->dev; + struct enum_topo_query_rsp *rsp; + u16 actual_rsp_size; + size_t header_sz; + int ret; + + ret = ub_enum_topo_query(uent, buf, start_idx, &actual_rsp_size); + if (ret) { + dev_err(dev, "enum topo query failed, ret=%d\n", ret); + return NULL; + } + + header_sz = ub_enum_calc_header_sz(buf, false); + if (header_sz == 0) { + dev_err(dev, "enum topo query rsp header sz 0\n"); + return NULL; + } + + if (header_sz + ENUM_TOPO_QUERY_RSP_BASE_SIZE > actual_rsp_size) { + dev_err(dev, "enum topo query rsp pkt size invalid\n"); + return NULL; + } + + rsp = (struct enum_topo_query_rsp *)(buf + header_sz); + ret = rsp->common.bits.status; + if (ret) { + dev_err(dev, "enum rsp has error, status=%d\n", ret); + return NULL; + } + + if (header_sz + rsp->common.pdu_len * SZ_4 != actual_rsp_size) { + dev_err(dev, "enum topo query rsp pdu_len invalid\n"); + return NULL; + } + + return rsp; +} + +static void ub_enum_parse_port(struct ub_entity *uent, + struct enum_tlv_port_info *pi, u16 num) +{ + struct enum_tlv_port_info *port_info; + struct device *dev = &uent->ubc->dev; + struct ub_port *port; + + for (u16 i = 0; i < num; i++) { + port_info = pi + i; + if (port_info->local_port_idx >= uent->port_nums) { + dev_err(dev, "local port idx %u exceeds uent port num %u\n", + port_info->local_port_idx, uent->port_nums); + continue; + } + + port = uent->ports + port_info->local_port_idx; + + port->domain_boundary = port_info->bits0.b; + port->type = port_info->bits0.t ? VIRTUAL : PHYSICAL; + port->shareable = (bool)port_info->bits0.w; + + /* skip link down port */ + if (!port_info->bits0.s) + continue; + + if (!is_device(uent) && !is_idev(uent) && port->domain_boundary) + continue; + + /* neighbor info of a boundary port shouldn't be stored */ + port->r_index = port_info->remote_port_idx; + guid_copy(&port->r_guid, &port_info->remote_guid); + } +} + +struct enum_tlv_info { + struct enum_tlv_slice_info *si; + struct enum_tlv_port_info *pi; + struct enum_tlv_port_num *pn; + struct enum_tlv_cap_info *ci; + u16 port_nums; +}; + +static int ub_enum_tlv_parse(void *tlv, int tlv_len, struct enum_tlv_info *info) +{ + struct enum_tlv_common *common; + u16 port_nums = 0; + int left_len = tlv_len; + + while ((left_len > 0) && (left_len % SZ_4 == 0)) { + common = (struct enum_tlv_common *)tlv; + + switch (common->type) { + case TLV_SLICE_INFO: + info->si = (struct enum_tlv_slice_info *)tlv; + break; + case TLV_PORT_NUM: + info->pn = (struct enum_tlv_port_num *)tlv; + break; + case TLV_PORT_INFO: + if (port_nums == 0) + info->pi = (struct enum_tlv_port_info *)tlv; + port_nums++; + break; + case TLV_CAP_INFO: + info->ci = (struct enum_tlv_cap_info *)tlv; + break; + default: + pr_warn("not support tlv type[%u]\n", common->type); + } + + if (common->len == 0) { + pr_err("enum tlv len is invalid\n"); + return -EINVAL; + } + + left_len -= (int)common->len; + tlv += common->len; + } + + info->port_nums = port_nums; + + return 0; +} + +/* only check guid itself here, repetition problem is not considered */ +bool ub_type_valid(struct ub_entity *uent, bool is_ctl) +{ + struct device *dev = &uent->ubc->dev; + u16 code = uent_class(uent); + u8 type = uent_type(uent); + + if (uent->ent_type == UB_ENT_UNKNOWN) { + dev_err(dev, "ent type unknown, type=%#x, class=%#x\n", + type, code); + return false; + } + + if (is_ctl && !is_ibus_controller(uent)) { + dev_err(dev, "ubc bad type, type=%#x, class=%#x\n", + type, code); + return false; + } + + if (!is_ctl && (is_ibus_controller(uent) || is_bus_controller(uent))) { + dev_err(dev, "peripheral bad type, type=%#x, class=%#x\n", + type, code); + return false; + } + + if (uent->pool && !(is_p_device(uent) || is_p_idevice(uent))) { + dev_err(dev, "pool bad type, type=%#x, class=%#x\n", + type, code); + return false; + } + + return true; +} + +void ub_entity_type_init(struct ub_entity *uent) +{ + u8 base = uent_base_code(uent); + + switch (uent_type(uent)) { + case UB_TYPE_CONTROLLER: + if (base == UB_BASE_CODE_BUS_CONTROLLER) { + if (uent->class_code == UB_CLASS_BUS_CONTROLLER) + uent->ent_type = UB_ENT_BUS_CONTROLLER; + else + uent->ent_type = UB_ENT_UNKNOWN; + } else { + if (!uent->pool) + uent->ent_type = UB_ENT_DEVICE; + else + uent->ent_type = UB_ENT_P_DEVICE; + } + break; + case UB_TYPE_ICONTROLLER: + if (base == UB_BASE_CODE_BUS_CONTROLLER) { + uent->ent_type = UB_ENT_IBUS_CONTROLLER; + } else { + if (!uent->pool) + uent->ent_type = UB_ENT_IDEVICE; + else + uent->ent_type = UB_ENT_P_IDEVICE; + } + break; + case UB_TYPE_SWITCH: + case UB_TYPE_ISWITCH: + uent->ent_type = UB_ENT_SWITCH; + break; + default: + uent->ent_type = UB_ENT_UNKNOWN; + } +} + +#define PORT_TOTAL_NUM_MAX 256 + +static int ub_enum_ent(struct ub_entity *uent, void *buf) +{ + struct device *dev = &uent->ubc->dev; + struct enum_pld_scan_pdu_common *pc; + int tlv_len, ret, total_num_ports; + struct enum_tlv_info info = {}; + u8 total_slice, slice_id = 0; + void *rsp, *tlv; + + do { + rsp = (void *)ub_enum_topo_query_and_check(uent, buf, slice_id); + if (!rsp) { + ret = -EBUSY; + goto err; + } + + pc = (struct enum_pld_scan_pdu_common *)rsp; + tlv_len = (int)pc->pdu_len * SZ_4 - ENUM_PLD_SCAN_PDU_COMMON_SIZE; + tlv = rsp + ENUM_PLD_SCAN_PDU_COMMON_SIZE; + + ret = ub_enum_tlv_parse(tlv, tlv_len, &info); + if (ret) + goto err; + + if (slice_id == 0) { + if (!info.si || !info.pi || !info.pn || !info.ci) { + dev_err(dev, "tlv si/pi/pn/ci NULL\n"); + goto err; + } + + uent->class_code = info.ci->class_code; + ub_entity_type_init(uent); + if (!ub_type_valid(uent, !uent->topo_rank)) + goto err; + + total_slice = info.si->total_slice; + total_num_ports = (int)info.pn->total_num_ports; + } + + if (!uent->ports) { + uent->port_nums = total_num_ports; + if (uent->port_nums > PORT_TOTAL_NUM_MAX) { + dev_err(dev, "Total num ports is over total num max(%d).\n", + PORT_TOTAL_NUM_MAX); + return -EINVAL; + } + + ret = ub_ports_setup(uent); + if (ret) { + dev_err(dev, "enum port setup fail, ret=%d\n", + ret); + return ret; + } + } + + ub_enum_parse_port(uent, info.pi, info.port_nums); + slice_id++; + } while (slice_id < total_slice); + + return 0; +err: + if (uent->ports) + ub_ports_unset(uent); + return ret; +} diff --git a/drivers/ub/ubus/enum.h b/drivers/ub/ubus/enum.h index db80b7040286..da4d293f38e8 100644 --- a/drivers/ub/ubus/enum.h +++ b/drivers/ub/ubus/enum.h @@ -182,5 +182,7 @@ struct enum_tlv_cap_info { #define ENUM_TLV_CAP_INFO_SZ 8 size_t calc_enum_pld_header_size(struct enum_pld_scan_header *header, bool req); +bool ub_type_valid(struct ub_entity *uent, bool is_ctl); +void ub_entity_type_init(struct ub_entity *uent); #endif /* __ENUM_H__ */ diff --git a/drivers/ub/ubus/msg.c b/drivers/ub/ubus/msg.c index c7be05f350bb..67dc56ab8ce3 100644 --- a/drivers/ub/ubus/msg.c +++ b/drivers/ub/ubus/msg.c @@ -134,3 +134,12 @@ int message_send(struct message_device *mdev, struct msg_info *info, return -ENOTTY; } + +int message_sync_enum(struct message_device *mdev, struct msg_info *info, + u8 cmd) +{ + if (mdev->ops->sync_enum) + return mdev->ops->sync_enum(mdev, info, cmd); + + return -ENOTTY; +} diff --git a/drivers/ub/ubus/msg.h b/drivers/ub/ubus/msg.h index fd1d9df7d20c..c980033803bc 100644 --- a/drivers/ub/ubus/msg.h +++ b/drivers/ub/ubus/msg.h @@ -192,6 +192,7 @@ enum message_tx_type { * @remove_dev: remove ub_entity to uninit message * @sync_request: send message to target ub_entity and wait response * @send: send message to target ub_entity but not wait response + * @sync_enum: send enum message to target ub_entity and wait response * @owner: Driver module providing these ops */ struct message_ops { @@ -201,6 +202,8 @@ struct message_ops { u8 code); int (*send)(struct message_device *mdev, struct msg_info *info, u8 code); + int (*sync_enum)(struct message_device *mdev, struct msg_info *info, + u8 cmd); struct module *owner; }; @@ -253,5 +256,7 @@ int message_sync_request(struct message_device *mdev, struct msg_info *info, u8 code); int message_send(struct message_device *mdev, struct msg_info *info, u8 code); +int message_sync_enum(struct message_device *mdev, struct msg_info *info, + u8 cmd); #endif /* __MSG_H__ */ diff --git a/include/ub/ubus/ubus.h b/include/ub/ubus/ubus.h index 4b7df4bb80b6..f9dc86d65ba4 100644 --- a/include/ub/ubus/ubus.h +++ b/include/ub/ubus/ubus.h @@ -130,6 +130,7 @@ struct ub_entity { unsigned long priv_flags; /* Private flags for the UB driver */ /* entity base info */ + bool pool; int ent_type; struct ub_guid guid; u16 class_code; -- Gitee From a5e26129f70202c020d456d968c88fa92aa8d910 Mon Sep 17 00:00:00 2001 From: Junlong Zheng Date: Fri, 19 Sep 2025 16:46:38 +0800 Subject: [PATCH 22/45] ub:ubus: Supports network address configuration and querying driver inclusion category: feature bugzilla: https://gitee.com/openeuler/kernel/issues/ID34DG CVE: NA ----------------------------------------------------------- Implement the functionality for configuring network addresses in enumeration messages and for sending and receiving network address query messages. Signed-off-by: Junlong Zheng --- drivers/ub/ubus/enum.c | 272 +++++++++++++++++++++++++++++++++++++++++ drivers/ub/ubus/enum.h | 2 + 2 files changed, 274 insertions(+) diff --git a/drivers/ub/ubus/enum.c b/drivers/ub/ubus/enum.c index 315077f184da..466c7998c992 100644 --- a/drivers/ub/ubus/enum.c +++ b/drivers/ub/ubus/enum.c @@ -8,6 +8,7 @@ #include "ubus.h" #include "msg.h" #include "port.h" +#include "cna.h" #include "enum.h" #define ENUM_MAX_HOPS 255 @@ -531,3 +532,274 @@ static int ub_enum_ent(struct ub_entity *uent, void *buf) ub_ports_unset(uent); return ret; } + +static int ub_enum_na_query_rsp(void *buf, u32 *cna, u16 actual_rsp_size) +{ + struct enum_na_query_rsp *rsp; + size_t header_sz; + + header_sz = ub_enum_calc_header_sz(buf, false); + if (header_sz == 0) { + pr_err("na query rsp header sz 0\n"); + return -EINVAL; + } + + if (header_sz + ENUM_NA_QUERY_RSP_SIZE != actual_rsp_size) { + pr_err("na query rsp pld sz invalid\n"); + return -EINVAL; + } + + rsp = (struct enum_na_query_rsp *)(buf + header_sz); + + if (!rsp->common.bits.status) + *cna = rsp->cna; + + return rsp->common.bits.status; +} + +static size_t ub_enum_na_query_init(struct ub_entity *uent, void *buf, u8 opcode, + u16 port_idx) +{ + struct enum_na_query_req *req; + size_t header_sz; + + memset(buf, 0, UB_TOPO_BUF_SZ); + ub_enum_pkt_header_init(buf); + ub_enum_pld_header_init(uent, buf); + + header_sz = ub_enum_calc_header_sz(buf, true); + req = (struct enum_na_query_req *)(buf + header_sz); + + ub_enum_pld_common_setup(&req->common, ENUM_CMD_NA_QUERY, + opcode, &uent->guid.id, + ENUM_NA_QUERY_REQ_SIZE); + + if (opcode == ENUM_NA_QUERY_PORT) + req->port_idx = port_idx; + + return header_sz + ENUM_NA_QUERY_REQ_SIZE; +} + +int ub_query_port_na(struct ub_entity *uent, void *buf) +{ + struct device *dev = &uent->ubc->dev; + struct msg_info info = {0}; + size_t req_pkt_sz; + struct ub_port *p; + int ret; + u32 cna; + + for_each_uent_port(p, uent) { + if (!p->domain_boundary) + continue; + + req_pkt_sz = ub_enum_na_query_init(uent, buf, + ENUM_NA_QUERY_PORT, + p->index); + + message_info_init(&info, uent, buf, buf, + (req_pkt_sz << MSG_REQ_SIZE_OFFSET) | + UB_TOPO_BUF_SZ); + + ret = message_sync_enum(uent->ubc->mdev, &info, + ENUM_CMD_NA_QUERY); + if (ret) + goto out; + + ret = ub_enum_na_query_rsp(buf, &cna, info.actual_rsp_size); + if (ret) { + dev_err(dev, "port na query rsp, status=%d\n", ret); + ret = -EBUSY; + goto out; + } + + p->cna = cna; + if (p->cna) + dev_info(dev, "update boundary port%u cna to %#x\n", + p->index, p->cna); + } + + return 0; +out: + for_each_uent_port(p, uent) + if (p->domain_boundary) + p->cna = 0; + + dev_err(dev, "query fail and reset boundary port cna to 0.\n"); + + return ret; +} + +int ub_query_ent_na(struct ub_entity *uent, void *buf) +{ + struct device *dev = &uent->ubc->dev; + struct msg_info info = {}; + size_t req_pkt_sz; + int ret; + u32 cna; + + req_pkt_sz = ub_enum_na_query_init(uent, buf, ENUM_NA_QUERY_DEVICE, 0); + message_info_init(&info, uent, buf, buf, + (req_pkt_sz << MSG_REQ_SIZE_OFFSET) | + UB_TOPO_BUF_SZ); + + ret = message_sync_enum(uent->ubc->mdev, &info, + ENUM_CMD_NA_QUERY); + if (ret) + return ret; + + ret = ub_enum_na_query_rsp(buf, &cna, info.actual_rsp_size); + if (ret) { + dev_err(dev, "dev na query rsp, status=%d\n", ret); + return -EBUSY; + } + + uent->cna = cna; + if (uent->cna) + dev_info(dev, "update cluster ubc cna to %#x\n", uent->cna); + + return 0; +} + +static int ub_enum_cluster_query(struct ub_entity *uent, void *buf) +{ + int ret; + + ret = ub_query_ent_na(uent, buf); + if (ret) { + dev_err(&uent->ubc->dev, "query cluster ubc dev cna err\n"); + return ret; + } + + ret = ub_query_port_na(uent, buf); + if (ret) + dev_err(&uent->ubc->dev, "query cluster ubc port cna err\n"); + + return ret; +} + +static int ub_enum_na_cfg_rsp(void *buf, u16 actual_rsp_size) +{ + struct enum_na_cfg_rsp *rsp; + size_t header_sz; + + header_sz = ub_enum_calc_header_sz(buf, false); + if (header_sz == 0) { + pr_err("na cfg rsp header sz 0\n"); + return -EINVAL; + } + + if (header_sz + ENUM_NA_CFG_RSP_SIZE != actual_rsp_size) { + pr_err("na cfg rsp pld sz invalid\n"); + return -EINVAL; + } + + rsp = (struct enum_na_cfg_rsp *)(buf + header_sz); + + return rsp->common.bits.status; +} + +static size_t ub_enum_na_cfg_init(struct ub_entity *uent, void *buf, u32 cna, + u8 opcode, u16 port_idx) +{ + struct enum_na_cfg_req *req; + size_t header_sz; + + ub_enum_refresh_and_init_buffer_header(uent, buf); + + header_sz = ub_enum_calc_header_sz(buf, true); + req = (struct enum_na_cfg_req *)(buf + header_sz); + + ub_enum_pld_common_setup(&req->common, ENUM_CMD_NA_CFG, opcode, + &uent->guid.id, ENUM_NA_CFG_REQ_SIZE); + + req->cna = cna; + if (opcode == ENUM_NA_CFG_PORT) + req->port_idx = port_idx; + + return header_sz + ENUM_NA_CFG_REQ_SIZE; +} + +/* Configuring the CNA of the ub entity Port */ +static int ub_enum_port_na_cfg(struct ub_entity *uent, void *buf) +{ + struct message_device *mdev = uent->message->mdev; + struct msg_info info = {}; + size_t req_pkt_sz; + struct ub_port *p; + int ret; + + for_each_uent_port(p, uent) { + if (is_ibus_controller(uent) && p->domain_boundary) + continue; + + req_pkt_sz = ub_enum_na_cfg_init(uent, buf, p->cna, + ENUM_NA_CFG_PORT, p->index); + + message_info_init(&info, uent, buf, buf, + (req_pkt_sz << MSG_REQ_SIZE_OFFSET) | + UB_TOPO_BUF_SZ); + + ret = message_sync_enum(mdev, &info, ENUM_CMD_NA_CFG); + if (ret) + return ret; + + ret = ub_enum_na_cfg_rsp(buf, info.actual_rsp_size); + if (ret) { + dev_err(&uent->ubc->dev, "port na cfg rsp, status=%d\n", + ret); + return -EBUSY; + } + } + + return 0; +} + +static int ub_enum_na_cfg(struct ub_entity *uent, void *buf) +{ + struct message_device *mdev = uent->message->mdev; + struct device *dev = &uent->ubc->dev; + enum enum_na_cfg_opcode opcode; + struct msg_info info = {}; + size_t req_pkt_sz; + int ret; + + ret = ub_cna_alloc(uent); + if (ret) { + dev_err(dev, "cna alloc fail, ret=%d\n", ret); + goto na_failed; + } + + if (is_switch(uent)) /* Switch hasn't port cna register */ + goto cfg_dev; + + ret = ub_enum_port_na_cfg(uent, buf); + if (ret) { + dev_err(dev, "port na cfg fail, ret=%d\n", ret); + goto na_failed; + } + + if (is_ibus_controller(uent) && uent->ubc->cluster) { + ret = ub_enum_cluster_query(uent, buf); + if (ret) + goto na_failed; + return 0; + } + +cfg_dev: + opcode = ENUM_NA_CFG_PRIMARY; + req_pkt_sz = ub_enum_na_cfg_init(uent, buf, uent->cna, opcode, 0); + + message_info_init(&info, uent, buf, buf, + (req_pkt_sz << MSG_REQ_SIZE_OFFSET) | UB_TOPO_BUF_SZ); + + ret = message_sync_enum(mdev, &info, ENUM_CMD_NA_CFG); + if (ret) + goto na_failed; + + return 0; + +na_failed: + ub_cna_free(uent); + return ret; +} diff --git a/drivers/ub/ubus/enum.h b/drivers/ub/ubus/enum.h index da4d293f38e8..837197321ed3 100644 --- a/drivers/ub/ubus/enum.h +++ b/drivers/ub/ubus/enum.h @@ -182,6 +182,8 @@ struct enum_tlv_cap_info { #define ENUM_TLV_CAP_INFO_SZ 8 size_t calc_enum_pld_header_size(struct enum_pld_scan_header *header, bool req); +int ub_query_port_na(struct ub_entity *uent, void *buf); +int ub_query_ent_na(struct ub_entity *uent, void *buf); bool ub_type_valid(struct ub_entity *uent, bool is_ctl); void ub_entity_type_init(struct ub_entity *uent); -- Gitee From e27c0119db0792012578ba6d57cf1491c7726747 Mon Sep 17 00:00:00 2001 From: Junlong Zheng Date: Fri, 19 Sep 2025 16:58:25 +0800 Subject: [PATCH 23/45] ub:ubus: Support for shortest path routing configuration driver inclusion category: feature bugzilla: https://gitee.com/openeuler/kernel/issues/ID34DG CVE: NA ----------------------------------------------------------- Implement shortest path calculation using BFS to complete the basic routing configuration for the entire network. Signed-off-by: Junlong Zheng --- drivers/ub/ubus/enum.c | 187 +++++++++++++++++++++++++++++++++++++++++ drivers/ub/ubus/enum.h | 1 + drivers/ub/ubus/ubus.h | 1 + include/ub/ubus/ubus.h | 1 + 4 files changed, 190 insertions(+) diff --git a/drivers/ub/ubus/enum.c b/drivers/ub/ubus/enum.c index 466c7998c992..50d00cbdf486 100644 --- a/drivers/ub/ubus/enum.c +++ b/drivers/ub/ubus/enum.c @@ -5,10 +5,13 @@ #define pr_fmt(fmt) "ubus enum: " fmt +#include + #include "ubus.h" #include "msg.h" #include "port.h" #include "cna.h" +#include "route.h" #include "enum.h" #define ENUM_MAX_HOPS 255 @@ -803,3 +806,187 @@ static int ub_enum_na_cfg(struct ub_entity *uent, void *buf) ub_cna_free(uent); return ret; } + + +/* + * Routing Algorithm: The BFS (Breadth First Search) algorithm + * calculates the shortest forwarding path. The steps are as follows: + * 1. Obtain the "nodes" and forwarding device information in the host + * scenario, and construct an interconnected network in the single host + * scenario. + * 2. Starting from each node in the interconnected network, sequentially + * visit the adjacent nodes that are directly connected with one hop, + * without considering weight/cost. + * 3. Visit the adjacent nodes of the adjacent nodes, traversing layer by + * layer. + * 4. Continue until reaching the destination or there are no unvisited nodes + * left, constructing the forwarding path with the minimum number of hops. + * 5. When the weights/costs of the links are not the same, the BFS algorithm + * may calculate non-shortest paths. + * 6. Based on the above mechanism, the BFS algorithm does not allow cycles. + */ + +struct bfs_port_path { + struct ub_port *s_port; /* source port for set cna_map */ + struct ub_entity *uent; /* BFS current ub entity */ +}; + +static struct bfs_route_info { + DECLARE_KFIFO_PTR(kfifo, struct bfs_port_path); + int *visited; /* 0: unvisited, 1: visited, 2: visiting */ + u32 cna_used; + u32 *cna_map; +} bfs_route; + +enum { BFS_UNVISITED, BFS_VISITED, BFS_VISITING }; + +/* Return: Whether to join the kfifo */ +static int bfs_route_test_and_put(struct ub_port *p, struct ub_entity *uent) +{ + struct bfs_port_path path; + + if (!is_switch(uent) && !is_ibus_controller(uent)) + return 0; + + if (uent->port_nums == 1) + return 0; + + path.s_port = p; + path.uent = uent; + + if (!kfifo_put(&bfs_route.kfifo, path)) { + pr_info("multiple paths join the kfifo, kfifo put failed\n"); + return 0; + } + + return 1; +} + +static int cna2index(int cna) +{ + int i; + + for (i = 0; i < bfs_route.cna_used; ++i) { + if (bfs_route.cna_map[i] == cna) + return i; + } + + return 0; +} + +static void ub_enum_bfs_layer_update(struct ub_entity *uent, int *curr, int *next, + int *layer) +{ + int i; + + *curr = *next; + *next = 0; + *layer += 1; + + for (i = 0; i < bfs_route.cna_used; ++i) + if (bfs_route.visited[i] == BFS_VISITING) + bfs_route.visited[i] = BFS_VISITED; +} + +static void ub_enum_bfs_route_dev(struct ub_entity *ldev) +{ + struct ub_entity *uent, *rdev; + struct ub_port *p, *rport, *s_port; + struct bfs_port_path path = { NULL, ldev }; + int curr_layer_devs = 1, next_layer_devs = 0, layer = 0; + + bfs_route.visited[cna2index(ldev->cna)] = BFS_VISITED; + kfifo_put(&bfs_route.kfifo, path); + while (kfifo_get(&bfs_route.kfifo, &path)) { + curr_layer_devs--; + uent = path.uent; + for_each_uent_port(p, uent) { + if (!p->cna || !p->r_uent || + bfs_route.visited[cna2index(p->r_uent->cna)] == + BFS_VISITED || + !ub_entity_test_priv_flag(p->r_uent, UB_ENTITY_DETACHED)) + continue; + + rdev = p->r_uent; + rport = rdev->ports + p->r_index; + s_port = path.s_port ? path.s_port : p; + + bfs_route.visited[cna2index(rdev->cna)] = BFS_VISITING; + ub_route_add_entry(s_port, rdev->cna, layer + 1); + if (!ONE_CNA(rdev)) + ub_route_add_entry(s_port, rport->cna, layer + 1); + ub_entity_assign_priv_flag(ldev, UB_ENTITY_ROUTE_UPDATED, true); + next_layer_devs += bfs_route_test_and_put(s_port, rdev); + } + + if (curr_layer_devs == 0) + ub_enum_bfs_layer_update(uent, &curr_layer_devs, + &next_layer_devs, &layer); + } +} + +static void ub_enum_bfs_route_core(struct list_head *dev_list) +{ + struct ub_entity *uent; + + list_for_each_entry(uent, dev_list, node) { + if (uent->port_nums == 1) + continue; + + memset(bfs_route.visited, 0, sizeof(int) * bfs_route.cna_used); + + ub_enum_bfs_route_dev(uent); + } +} + +static void ub_bfs_set_cna_map(struct list_head *dev_list) +{ + struct ub_port *port; + struct ub_entity *uent; + int i = 0; + + list_for_each_entry(uent, dev_list, node) { + bfs_route.cna_map[i++] = uent->cna; + if (ONE_CNA(uent)) + continue; + for_each_uent_port(port, uent) + bfs_route.cna_map[i++] = port->cna; + } +} + +int ub_enum_bfs_route_cal(struct list_head *dev_list) +{ + u16 max_port_num = 0; + struct ub_entity *uent; + int ret = -ENOMEM; + + if (kfifo_alloc(&bfs_route.kfifo, SZ_4K, GFP_KERNEL)) + goto kfifo_fail; + + bfs_route.cna_used = 0; + list_for_each_entry(uent, dev_list, node) { + if (max_port_num < uent->port_nums) + max_port_num = uent->port_nums; + bfs_route.cna_used += ONE_CNA(uent) ? 1 : uent->port_nums + 1; + } + + bfs_route.cna_map = kcalloc(bfs_route.cna_used, sizeof(u32), GFP_KERNEL); + if (!bfs_route.cna_map) + goto map_fail; + ub_bfs_set_cna_map(dev_list); + + bfs_route.visited = kcalloc(bfs_route.cna_used, sizeof(int), GFP_KERNEL); + if (!bfs_route.visited) + goto visited_fail; + + ub_enum_bfs_route_core(dev_list); + ret = 0; + + kfree(bfs_route.visited); +visited_fail: + kfree(bfs_route.cna_map); +map_fail: + kfifo_free(&bfs_route.kfifo); +kfifo_fail: + return ret; +} diff --git a/drivers/ub/ubus/enum.h b/drivers/ub/ubus/enum.h index 837197321ed3..2f5fec228f1a 100644 --- a/drivers/ub/ubus/enum.h +++ b/drivers/ub/ubus/enum.h @@ -182,6 +182,7 @@ struct enum_tlv_cap_info { #define ENUM_TLV_CAP_INFO_SZ 8 size_t calc_enum_pld_header_size(struct enum_pld_scan_header *header, bool req); +int ub_enum_bfs_route_cal(struct list_head *uent_list); int ub_query_port_na(struct ub_entity *uent, void *buf); int ub_query_ent_na(struct ub_entity *uent, void *buf); bool ub_type_valid(struct ub_entity *uent, bool is_ctl); diff --git a/drivers/ub/ubus/ubus.h b/drivers/ub/ubus/ubus.h index 3f0812e77212..3bd02c9ea4a3 100644 --- a/drivers/ub/ubus/ubus.h +++ b/drivers/ub/ubus/ubus.h @@ -38,6 +38,7 @@ enum ub_entity_type { #define UB_COMPACT_EID_MASK GENMASK(19, 0) /* ub_entity priv_flags */ +#define UB_ENTITY_DETACHED 1 /* Flag indicate uent is detached */ #define UB_ENTITY_ROUTE_UPDATED 2 /* Flag indicate uent's route is updated */ static inline void ub_entity_assign_priv_flag(struct ub_entity *uent, int bit, bool flag) diff --git a/include/ub/ubus/ubus.h b/include/ub/ubus/ubus.h index f9dc86d65ba4..56a6e24015e6 100644 --- a/include/ub/ubus/ubus.h +++ b/include/ub/ubus/ubus.h @@ -145,6 +145,7 @@ struct ub_entity { u32 token_value; /* entity topology info */ + struct list_head node; struct ub_bus_controller *ubc; struct ub_entity *pue; /* ue/mue connected to their mue */ int topo_rank; /* The levels of Breadth-First Search */ -- Gitee From de71d6c3f30bd0ce19879f0c5cdec758e5de7f53 Mon Sep 17 00:00:00 2001 From: Junlong Zheng Date: Fri, 19 Sep 2025 17:52:34 +0800 Subject: [PATCH 24/45] ub:ubus: Support for UB Bus Controller Enumeration driver inclusion category: feature bugzilla: https://gitee.com/openeuler/kernel/issues/ID34DG CVE: NA ----------------------------------------------------------- Perform a topological scan of the network using breadth-first search, and currently implement enumeration of the UB Bus Controller. Signed-off-by: Junlong Zheng --- drivers/ub/ubus/Makefile | 2 +- drivers/ub/ubus/enum.c | 222 ++++++++++++++++++++++++++++++++++ drivers/ub/ubus/ubus_entity.c | 14 +++ drivers/ub/ubus/ubus_entity.h | 11 ++ 4 files changed, 248 insertions(+), 1 deletion(-) create mode 100644 drivers/ub/ubus/ubus_entity.c create mode 100644 drivers/ub/ubus/ubus_entity.h diff --git a/drivers/ub/ubus/Makefile b/drivers/ub/ubus/Makefile index 6b61b611ee22..906372a5be99 100644 --- a/drivers/ub/ubus/Makefile +++ b/drivers/ub/ubus/Makefile @@ -3,6 +3,6 @@ obj-$(CONFIG_UB_UBUS) += ub-driver.o controller.o config.o ubus-y := ubus_driver.o sysfs.o ubus_controller.o msg.o ubus_config.o port.o cc.o eid.o cna.o route.o -ubus-y += enum.o resource.o +ubus-y += enum.o resource.o ubus_entity.o obj-$(CONFIG_UB_UBUS_BUS) += ubus.o diff --git a/drivers/ub/ubus/enum.c b/drivers/ub/ubus/enum.c index 50d00cbdf486..083532671568 100644 --- a/drivers/ub/ubus/enum.c +++ b/drivers/ub/ubus/enum.c @@ -8,6 +8,8 @@ #include #include "ubus.h" +#include "ubus_inner.h" +#include "ubus_entity.h" #include "msg.h" #include "port.h" #include "cna.h" @@ -990,3 +992,223 @@ int ub_enum_bfs_route_cal(struct list_head *dev_list) kfifo_fail: return ret; } + +int ub_cfg_read_guid(struct ub_entity *uent) +{ + u32 val = 0; + int i, ret; + + for (i = 0; i < UB_GUID_DW_NUM; i++) { + ret = ub_cfg_read_dword(uent, UB_GUID + i * sizeof(u32), &val); + if (ret) + return ret; + + uent->guid.dw[i] = val; + } + + return 0; +} + +static void ub_enum_destroy_entity(struct ub_entity *uent) +{ + message_remove_device(uent); + + if (is_primary(uent)) + ub_ubc_put(uent->ubc); + + kfree(uent); +} + +static struct ub_entity *ub_enum_create_uent(struct ub_bus_controller *ubc) +{ + struct ub_entity *uent; + int ret; + + uent = ub_alloc_ent(); + if (!uent) + return NULL; + + uent->pue = uent; + uent->entity_idx = 0; + uent->ubc = ub_ubc_get(ubc); + uent->upi = UB_CP_UPI; + ret = message_probe_device(uent); + if (ret) { + dev_err(&ubc->dev, "enum msg probe dev fail, ret=%d\n", ret); + goto err_out; + } + + return uent; +err_out: + ub_ubc_put(ubc); + kfree(uent); + return NULL; +} + +static struct ub_entity *ub_enum_create_bus_controller(struct ub_bus_controller *ubc) +{ + struct ub_entity *uent; + int ret; + + uent = ub_enum_create_uent(ubc); + if (!uent) + return (struct ub_entity *)ERR_PTR(-ENOMEM); + + ubc->uent = uent; + ret = ub_cfg_read_guid(uent); + if (ret) { + dev_err(&ubc->dev, "read guid failed, ret=%d\n", ret); + goto err_out; + } + + uent->topo_rank = 0; + uent->dev.parent = &ubc->dev; + + return uent; +err_out: + ub_enum_destroy_entity(uent); + return (struct ub_entity *)ERR_PTR(ret); +} + +static void ub_enum_topo_ent_uninit(struct ub_entity *uent) +{ + if (is_primary(uent)) { + ub_route_clear(uent); /* alloc in route_config */ + ub_cna_free(uent); /* alloc in na_cfg */ + ub_ports_unset(uent); /* alloc in scan_device */ + } +} + +static int ub_enum_and_configure_ent(struct ub_entity *uent, void *buf) +{ + int ret; + + ret = ub_enum_ent(uent, buf); + if (ret) + return ret; + + ret = ub_enum_na_cfg(uent, buf); + if (ret) + ub_ports_unset(uent); + + return ret; +} + +void ub_enum_clear_ent_list(struct list_head *dev_list) +{ + struct ub_entity *uent, *tmp; + struct ub_port *port; + + list_for_each_entry_safe_reverse(uent, tmp, dev_list, node) { + list_del(&uent->node); + for_each_uent_port(port, uent) + ub_port_disconnect(port); + + ub_cna_free(uent); + ub_ports_unset(uent); + ub_enum_destroy_entity(uent); + } +} + +static int ub_enum_bus_controllers(struct list_head *dev_list) +{ + struct ub_bus_controller *ubc; + struct ub_entity *uent; + int ret; + + list_for_each_entry(ubc, &ubc_list, node) { + uent = ub_enum_create_bus_controller(ubc); + if (IS_ERR(uent)) { + ret = PTR_ERR(uent); + dev_err(&ubc->dev, "create controller failed, ret=%d\n", + ret); + goto clear_list; + } + + ret = ub_enum_and_configure_ent(uent, topo_scan.buf); + if (ret) { + dev_err(&ubc->dev, "enum controller failed, ret=%d\n", + ret); + ub_enum_destroy_entity(uent); + goto clear_list; + } + + list_add_tail(&uent->node, dev_list); + } + + return 0; +clear_list: + ub_enum_clear_ent_list(dev_list); + return ret; +} + +/* + * During topo scan, just alloc ub_entity, alloc ub_port, alloc cna, + * so, when topo scan failed, just go to free devs in topo_scan.uents + */ +static int ub_enum_topo_scan(struct list_head *dev_list) +{ + int ret; + + ret = ub_enum_topo_scan_init(); + if (ret) { + pr_err("enum topo scan init failed, ret=%d\n", ret); + return ret; + } + + ret = ub_enum_bus_controllers(dev_list); + if (ret) { + pr_err("enum create controllers failed, ret=%d\n", ret); + goto out; + } + + if (list_empty(dev_list)) { + pr_warn("No ub bus controller exists in the current environment.\n"); + ret = -ENODEV; + goto out; + } + +out: + ub_enum_topo_scan_uninit(); + return ret; +} + +static void ub_enum_free_all(void) +{ + struct ub_entity *uent, *tmp; + + list_for_each_entry_safe_reverse(uent, tmp, &topo_scan.dev_list, node) { + list_del(&uent->node); + ub_enum_topo_ent_uninit(uent); + ub_enum_destroy_entity(uent); + } +} + +int ub_enum_probe(void) +{ + int ret; + + ret = ub_enum_topo_scan(&topo_scan.dev_list); + if (ret == -ENODEV) { + return 0; + } else if (ret) { + pr_err("topo_scan fail, ret=%d\n", ret); + return ret; + } + + ret = ub_enum_bfs_route_cal(&topo_scan.dev_list); + if (ret) { + pr_err("route cal fail, ret=%d\n", ret); + goto err_out; + } + + return 0; +err_out: + ub_enum_free_all(); + return ret; +} + +void ub_enum_remove(void) +{ + ub_enum_free_all(); +} diff --git a/drivers/ub/ubus/ubus_entity.c b/drivers/ub/ubus/ubus_entity.c new file mode 100644 index 000000000000..eb773c8affeb --- /dev/null +++ b/drivers/ub/ubus/ubus_entity.c @@ -0,0 +1,14 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (c) HiSilicon Technologies Co., Ltd. 2025. All rights reserved. + */ + +#define pr_fmt(fmt) "ubus entity: " fmt + +#include "ubus.h" + +struct ub_entity *ub_alloc_ent(void) +{ + return NULL; +} +EXPORT_SYMBOL_GPL(ub_alloc_ent); diff --git a/drivers/ub/ubus/ubus_entity.h b/drivers/ub/ubus/ubus_entity.h new file mode 100644 index 000000000000..b9e4e57ce1a7 --- /dev/null +++ b/drivers/ub/ubus/ubus_entity.h @@ -0,0 +1,11 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Copyright (c) HiSilicon Technologies Co., Ltd. 2025. All rights reserved. + */ + +#ifndef __UBUS_ENTITY_H__ +#define __UBUS_ENTITY_H__ + +struct ub_entity *ub_alloc_ent(void); + +#endif /* __UBUS_ENTITY_H__ */ -- Gitee From c397139a285e7f3e3a7db1a6afe18f27e4a15868 Mon Sep 17 00:00:00 2001 From: Junlong Zheng Date: Fri, 19 Sep 2025 18:01:13 +0800 Subject: [PATCH 25/45] ub:ubus: Supports device enumeration. driver inclusion category: feature bugzilla: https://gitee.com/openeuler/kernel/issues/ID34DG CVE: NA ----------------------------------------------------------- Enumerate all devices within the network, enable them, and add them to the system. Signed-off-by: Junlong Zheng --- drivers/ub/ubus/enum.c | 178 +++++++++++++++++++++++++++++++++- drivers/ub/ubus/enum.h | 2 + drivers/ub/ubus/ubus_entity.c | 24 +++++ drivers/ub/ubus/ubus_entity.h | 5 + 4 files changed, 208 insertions(+), 1 deletion(-) diff --git a/drivers/ub/ubus/enum.c b/drivers/ub/ubus/enum.c index 083532671568..4b25412c8c50 100644 --- a/drivers/ub/ubus/enum.c +++ b/drivers/ub/ubus/enum.c @@ -1142,6 +1142,144 @@ static int ub_enum_bus_controllers(struct list_head *dev_list) return ret; } +static struct ub_entity *ub_enum_get_ent(guid_t *guid, struct list_head *dev_list) +{ + struct ub_entity *uent; + + list_for_each_entry(uent, dev_list, node) + if (guid_equal(guid, &uent->guid.id)) + return uent; + + return NULL; +} + +static bool ub_enum_port_recognise_check(struct ub_entity *root, + struct ub_port *port) +{ + struct ub_port *tmp; + + if (guid_equal(&port->r_guid, &root->guid.id)) { + tmp = root->ports + port->r_index; + if (guid_is_null(&tmp->r_guid)) { + port->r_index = 0; + guid_copy(&port->r_guid, &guid_null); + return true; + } + } + + return false; +} + +static bool port_need_scan(struct ub_entity *root, struct ub_port *port) +{ + if (port->r_uent) + return false; + + if (guid_is_null(&port->r_guid)) + return false; + + if (root && ub_enum_port_recognise_check(root, port)) + return false; + + return true; +} + +static struct ub_entity *ub_enum_create_entity(guid_t *guid, struct ub_entity *parent) +{ + struct ub_entity *uent; + + if (parent->topo_rank + 1 > ENUM_MAX_HOPS) { + dev_err(&parent->ubc->dev, "ub support max hops: %d\n", + ENUM_MAX_HOPS); + return (struct ub_entity *)ERR_PTR(-EINVAL); + } + + uent = ub_enum_create_uent(parent->ubc); + if (!uent) + return (struct ub_entity *)ERR_PTR(-ENOMEM); + + guid_copy(&uent->guid.id, guid); + + uent->topo_rank = parent->topo_rank + 1; + uent->dev.parent = &parent->dev; + + return uent; +} + +static int ub_enum_do_topo_scan(struct ub_entity *root, struct list_head *dev_list, + void *buf) +{ +#define UB_TOPO_KFIFO_DEPTH SZ_128 + DECLARE_KFIFO(kfifo, struct ub_entity *, UB_TOPO_KFIFO_DEPTH); + struct ub_entity *uent, *r_uent; + struct ub_port *port; + struct device *dev; + int ret = 0; + + INIT_KFIFO(kfifo); + + if (root) + kfifo_put(&kfifo, root); + + list_for_each_entry(uent, dev_list, node) + kfifo_put(&kfifo, uent); + + while (kfifo_get(&kfifo, &uent)) { + dev = &uent->ubc->dev; + + for_each_uent_port(port, uent) { + if (!port_need_scan(root, port)) + continue; + + r_uent = ub_enum_get_ent(&port->r_guid, dev_list); + if (r_uent) + goto connect_port; + + if (is_device(uent)) { + ret = -EINVAL; + dev_err(dev, "device connect to multi peer\n"); + goto clear_list; + } + + r_uent = ub_enum_create_entity(&port->r_guid, uent); + if (IS_ERR(r_uent)) { + ret = PTR_ERR(r_uent); + dev_err(dev, "enum create device failed, ret=%d\n", + ret); + goto clear_list; + } + + /* set here to help enum find path */ + port->r_uent = r_uent; + ret = ub_enum_and_configure_ent(r_uent, buf); + if (ret) { + dev_err(dev, "enum device failed, ret=%d\n", + ret); + ub_enum_destroy_entity(r_uent); + goto clear_port; + } + + list_add_tail(&r_uent->node, dev_list); + if (!kfifo_put(&kfifo, r_uent)) + dev_err(dev, "do topo scan kfifo put full\n"); + +connect_port: + if (!ub_check_and_connect(port, r_uent)) { + dev_err(dev, "port%u wrong topo\n", port->index); + ret = -EINVAL; + goto clear_port; + } + } + } + + return 0; +clear_port: + port->r_uent = NULL; +clear_list: + ub_enum_clear_ent_list(dev_list); + return ret; +} + /* * During topo scan, just alloc ub_entity, alloc ub_port, alloc cna, * so, when topo scan failed, just go to free devs in topo_scan.uents @@ -1168,6 +1306,10 @@ static int ub_enum_topo_scan(struct list_head *dev_list) goto out; } + ret = ub_enum_do_topo_scan(NULL, dev_list, topo_scan.buf); + if (ret) + pr_err("enum scan devices failed, ret=%d\n", ret); + out: ub_enum_topo_scan_uninit(); return ret; @@ -1182,6 +1324,33 @@ static void ub_enum_free_all(void) ub_enum_topo_ent_uninit(uent); ub_enum_destroy_entity(uent); } + + ub_stop_entities(); + ub_remove_entities(); +} + +int ub_enum_entities_active(struct list_head *dev_list) +{ + struct ub_entity *uent, *tmp; + int ret; + + list_for_each_entry_safe(uent, tmp, dev_list, node) { + if (uent->entity_idx != 0) + continue; + + ub_route_sync_dev(uent); + ret = ub_setup_ent(uent); + if (ret) { + pr_err("setup dev err, ret=%d\n", ret); + return ret; + } + + list_del(&uent->node); + ub_entity_add(uent, uent->ubc); + ub_start_ent(uent); + } + + return 0; } int ub_enum_probe(void) @@ -1202,6 +1371,12 @@ int ub_enum_probe(void) goto err_out; } + ret = ub_enum_entities_active(&topo_scan.dev_list); + if (ret) { + pr_err("devices start fail, ret=%d\n", ret); + goto err_out; + } + return 0; err_out: ub_enum_free_all(); @@ -1210,5 +1385,6 @@ int ub_enum_probe(void) void ub_enum_remove(void) { - ub_enum_free_all(); + ub_stop_entities(); + ub_remove_entities(); } diff --git a/drivers/ub/ubus/enum.h b/drivers/ub/ubus/enum.h index 2f5fec228f1a..554d503d5d51 100644 --- a/drivers/ub/ubus/enum.h +++ b/drivers/ub/ubus/enum.h @@ -185,6 +185,8 @@ size_t calc_enum_pld_header_size(struct enum_pld_scan_header *header, bool req); int ub_enum_bfs_route_cal(struct list_head *uent_list); int ub_query_port_na(struct ub_entity *uent, void *buf); int ub_query_ent_na(struct ub_entity *uent, void *buf); +int ub_enum_probe(void); +void ub_enum_remove(void); bool ub_type_valid(struct ub_entity *uent, bool is_ctl); void ub_entity_type_init(struct ub_entity *uent); diff --git a/drivers/ub/ubus/ubus_entity.c b/drivers/ub/ubus/ubus_entity.c index eb773c8affeb..258143c309e3 100644 --- a/drivers/ub/ubus/ubus_entity.c +++ b/drivers/ub/ubus/ubus_entity.c @@ -12,3 +12,27 @@ struct ub_entity *ub_alloc_ent(void) return NULL; } EXPORT_SYMBOL_GPL(ub_alloc_ent); + +int ub_setup_ent(struct ub_entity *uent) +{ + return -EINVAL; +} +EXPORT_SYMBOL_GPL(ub_setup_ent); + +void ub_entity_add(struct ub_entity *uent, void *ctx) +{ +} +EXPORT_SYMBOL_GPL(ub_entity_add); + +void ub_start_ent(struct ub_entity *uent) +{ +} +EXPORT_SYMBOL_GPL(ub_start_ent); + +void ub_stop_entities(void) +{ +} + +void ub_remove_entities(void) +{ +} diff --git a/drivers/ub/ubus/ubus_entity.h b/drivers/ub/ubus/ubus_entity.h index b9e4e57ce1a7..5cb3f020e4e6 100644 --- a/drivers/ub/ubus/ubus_entity.h +++ b/drivers/ub/ubus/ubus_entity.h @@ -7,5 +7,10 @@ #define __UBUS_ENTITY_H__ struct ub_entity *ub_alloc_ent(void); +int ub_setup_ent(struct ub_entity *uent); +void ub_entity_add(struct ub_entity *uent, void *ctx); +void ub_start_ent(struct ub_entity *uent); +void ub_stop_entities(void); +void ub_remove_entities(void); #endif /* __UBUS_ENTITY_H__ */ -- Gitee From f08f23e25d9507ce9b54309781fbd8304d26ad73 Mon Sep 17 00:00:00 2001 From: Junlong Zheng Date: Fri, 19 Sep 2025 18:39:36 +0800 Subject: [PATCH 26/45] ub:ubus: Support UB device enabling basic interface driver inclusion category: feature bugzilla: https://gitee.com/openeuler/kernel/issues/ID34DG CVE: NA ----------------------------------------------------------- Implementing the basic configuration for entity0 initialization and de-initialization. Signed-off-by: Junlong Zheng --- drivers/ub/ubus/enum.h | 1 + drivers/ub/ubus/sysfs.c | 4 + drivers/ub/ubus/sysfs.h | 1 + drivers/ub/ubus/ubus.h | 4 + drivers/ub/ubus/ubus_driver.c | 2 + drivers/ub/ubus/ubus_driver.h | 11 ++ drivers/ub/ubus/ubus_entity.c | 315 +++++++++++++++++++++++++++++++++- drivers/ub/ubus/ubus_entity.h | 1 + include/ub/ubus/ubus.h | 28 +++ 9 files changed, 365 insertions(+), 2 deletions(-) create mode 100644 drivers/ub/ubus/ubus_driver.h diff --git a/drivers/ub/ubus/enum.h b/drivers/ub/ubus/enum.h index 554d503d5d51..2ab04e0f705f 100644 --- a/drivers/ub/ubus/enum.h +++ b/drivers/ub/ubus/enum.h @@ -182,6 +182,7 @@ struct enum_tlv_cap_info { #define ENUM_TLV_CAP_INFO_SZ 8 size_t calc_enum_pld_header_size(struct enum_pld_scan_header *header, bool req); +int ub_cfg_read_guid(struct ub_entity *uent); int ub_enum_bfs_route_cal(struct list_head *uent_list); int ub_query_port_na(struct ub_entity *uent, void *buf); int ub_query_ent_na(struct ub_entity *uent, void *buf); diff --git a/drivers/ub/ubus/sysfs.c b/drivers/ub/ubus/sysfs.c index 84d32e36249b..eca8d3f7f58c 100644 --- a/drivers/ub/ubus/sysfs.c +++ b/drivers/ub/ubus/sysfs.c @@ -143,3 +143,7 @@ const struct attribute_group *ub_bus_groups[] = { &ub_bus_group, NULL }; + +const struct device_type ub_dev_type = { + .groups = NULL, +}; diff --git a/drivers/ub/ubus/sysfs.h b/drivers/ub/ubus/sysfs.h index 8c8fc5d3fa72..bbd4d1b49dbc 100644 --- a/drivers/ub/ubus/sysfs.h +++ b/drivers/ub/ubus/sysfs.h @@ -7,5 +7,6 @@ extern const struct attribute_group *ub_entity_groups[]; extern const struct attribute_group *ub_bus_groups[]; +extern const struct device_type ub_dev_type; #endif /* __SYSFS_H__ */ diff --git a/drivers/ub/ubus/ubus.h b/drivers/ub/ubus/ubus.h index 3bd02c9ea4a3..b2463fccee7a 100644 --- a/drivers/ub/ubus/ubus.h +++ b/drivers/ub/ubus/ubus.h @@ -36,10 +36,14 @@ enum ub_entity_type { #define ONE_CNA(uent) (is_switch(uent) || (uent)->port_nums == 1) #define UB_COMPACT_EID_MASK GENMASK(19, 0) +#define UB_UPI_MASK GENMASK(14, 0) /* ub_entity priv_flags */ +#define UB_ENTITY_START 0 /* Flag indicate uent can match with driver */ #define UB_ENTITY_DETACHED 1 /* Flag indicate uent is detached */ #define UB_ENTITY_ROUTE_UPDATED 2 /* Flag indicate uent's route is updated */ +#define UB_ENTITY_SETUP 4 /* Flag indicate uent is setup */ + static inline void ub_entity_assign_priv_flag(struct ub_entity *uent, int bit, bool flag) { diff --git a/drivers/ub/ubus/ubus_driver.c b/drivers/ub/ubus/ubus_driver.c index 1ad798c2aa84..9e504806fd33 100644 --- a/drivers/ub/ubus/ubus_driver.c +++ b/drivers/ub/ubus/ubus_driver.c @@ -19,6 +19,8 @@ #include "ubus_controller.h" #include "ubus_inner.h" +DECLARE_RWSEM(ub_bus_sem); + static DEFINE_MUTEX(manage_subsystem_ops_mutex); static const struct ub_manage_subsystem_ops *manage_subsystem_ops; diff --git a/drivers/ub/ubus/ubus_driver.h b/drivers/ub/ubus/ubus_driver.h new file mode 100644 index 000000000000..6a259d224942 --- /dev/null +++ b/drivers/ub/ubus/ubus_driver.h @@ -0,0 +1,11 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Copyright (c) HiSilicon Technologies Co., Ltd. 2025. All rights reserved. + */ + +#ifndef __UBUS_DRIVER_H__ +#define __UBUS_DRIVER_H__ + +extern struct rw_semaphore ub_bus_sem; + +#endif /* __UBUS_DRIVER_H__ */ diff --git a/drivers/ub/ubus/ubus_entity.c b/drivers/ub/ubus/ubus_entity.c index 258143c309e3..ae697e80c528 100644 --- a/drivers/ub/ubus/ubus_entity.c +++ b/drivers/ub/ubus/ubus_entity.c @@ -5,30 +5,341 @@ #define pr_fmt(fmt) "ubus entity: " fmt +#include + #include "ubus.h" +#include "sysfs.h" +#include "msg.h" +#include "enum.h" +#include "route.h" +#include "port.h" +#include "eid.h" +#include "cna.h" +#include "resource.h" +#include "ubus_controller.h" +#include "ubus_driver.h" +#include "ubus_inner.h" + +/* + * Entity lifecycle + * + * init process: {ub_alloc_ent -> ub_setup_ent -> ub_entity_add -> ub_start_ent} + * + * uninit process: {ub_stop_ent -> ub_remove_ent} + */ + +#define UENT_NUM_START 1 +#define UENT_NUM_END 0xfffff +#define UB_DEFAULT_MAX_SEG_SIZE SZ_64K struct ub_entity *ub_alloc_ent(void) { - return NULL; + struct ub_entity *uent; + + uent = kzalloc(sizeof(*uent), GFP_KERNEL); + if (!uent) + return NULL; + + INIT_LIST_HEAD(&uent->node); + INIT_LIST_HEAD(&uent->cna_list); + + uent->dev.type = &ub_dev_type; + uent->cna = 0; + + ub_entity_assign_priv_flag(uent, UB_ENTITY_DETACHED, true); + + return uent; } EXPORT_SYMBOL_GPL(ub_alloc_ent); -int ub_setup_ent(struct ub_entity *uent) +static DEFINE_IDA(uent_num_ida); +void ub_entity_num_free(struct ub_entity *uent) +{ + ida_free(&uent_num_ida, uent->uent_num); +} + +static int ub_entity_num_alloc(void) +{ + return ida_alloc_range(&uent_num_ida, UENT_NUM_START, UENT_NUM_END, + GFP_KERNEL); +} + +static int ub_get_guid(struct ub_entity *uent) +{ + return 0; +} + +static void ub_config_upi(struct ub_entity *uent) +{ + struct device *dev; + int ret; + u16 upi; + + if (is_ibus_controller(uent) && uent->ubc->cluster) { + dev = &uent->ubc->dev; + ret = ub_cfg_read_word(uent, UB_UPI, &upi); + if (ret) { + dev_err(dev, "update cluster upi fail, ret=%d\n", ret); + return; + } + + upi &= UB_UPI_MASK; + if (upi) { + dev_info(dev, "update cluster ubc upi, upi=%#x\n", upi); + uent->upi = upi; + } + return; + } + + ret = ub_cfg_write_dword(uent, UB_UPI, uent->upi); + if (ret) + ub_err(uent, "cfg upi fail, ret=%d\n", ret); +} + +static int ub_setup_ent_primary(struct ub_entity *uent) { return -EINVAL; } + +static int ub_uent_cfg(struct ub_entity *uent, u32 uent_num) +{ + struct ub_guid *guid = &uent->guid; + char buf[SZ_64] = {}; + + dev_set_name(&uent->dev, "%05x", uent_num); + uent->uent_num = uent_num; + + (void)ub_show_guid(guid, buf); + ub_info(uent, "guid=%s, uent_num=%#05x\n", buf, uent_num); + + uent->dev.bus = &ub_bus_type; + /* Card driver set to 64bit if support */ + uent->dma_mask = GENMASK(31, 0); + + return ub_setup_ent_primary(uent); +} + +static void ub_config_eid(struct ub_entity *uent) +{ + if (is_ibus_controller(uent) && uent->ubc->cluster) + return; + + ub_cfg_write_dword(uent, UB_EID_0, uent->eid); +} + +static void ub_set_fm_info(struct ub_entity *uent) +{ + if (uent->entity_idx) + return; + + if (uent->ubc->cluster && !is_idev(uent)) + return; + + ub_cfg_write_dword(uent, UB_FM_CNA, uent->ubc->uent->cna); + ub_cfg_write_dword(uent, UB_FM_EID_0, uent->ubc->uent->eid); +} + +static void ub_get_module_id(struct ub_entity *uent) +{ + int ret; + u32 val; + + ret = ub_cfg_read_dword(uent, UB_MODULE, &val); + if (ret) { + ub_err(uent, "Get dev module fail %d\n", ret); + return; + } + + uent->mod_vendor = val >> SZ_16; + uent->module = val & UB_MODULE_ID_MASK; +} + +int ub_setup_ent(struct ub_entity *uent) +{ + int ret, uent_num; + + if (!uent) + return -EINVAL; + + ret = message_probe_device(uent); + if (ret) { + ub_err(uent, "probe message fail, ret=%d\n", ret); + return ret; + } + + ret = ub_get_guid(uent); + if (ret) { + ub_err(uent, "get guid fail, ret=%d\n", ret); + goto err_alloc; + } + + ub_config_upi(uent); + + /* common setup */ + ret = ub_eid_alloc(uent); + if (ret) { + ub_err(uent, "alloc eid fail, ret=%d\n", ret); + goto err_alloc; + } + + uent_num = ub_entity_num_alloc(); + if (uent_num < 0) { + ub_err(uent, "alloc dev uent_num fail, ret=%d\n", uent_num); + goto free_eid; + } + + ret = ub_uent_cfg(uent, (u32)uent_num); + if (ret) + goto free_uent_num; + + ub_config_eid(uent); + ub_set_fm_info(uent); + ub_get_module_id(uent); + + ub_entity_assign_priv_flag(uent, UB_ENTITY_SETUP, true); + return 0; +free_uent_num: + ub_entity_num_free(uent); +free_eid: + ub_eid_free(uent); +err_alloc: + message_remove_device(uent); + return ret; +} EXPORT_SYMBOL_GPL(ub_setup_ent); +static void ub_configure_ent(struct ub_entity *uent) +{ +} + +static void ub_unconfigure_ent(struct ub_entity *uent) +{ +} + +static void ub_release_ent(struct device *dev); void ub_entity_add(struct ub_entity *uent, void *ctx) { + struct ub_bus_controller *ubc; + struct list_head *list; + int ret, node; + + if (!uent || !ctx) + return; + + ret = ub_entity_setup_mmio(uent); + WARN_ON(ret); + + ub_configure_ent(uent); + + device_initialize(&uent->dev); + uent->dev.release = ub_release_ent; + + uent->dev.dma_mask = &uent->dma_mask; + uent->dev.dma_parms = &uent->dma_parms; + uent->dev.coherent_dma_mask = GENMASK_ULL(31, 0); + dma_set_max_seg_size(&uent->dev, UB_DEFAULT_MAX_SEG_SIZE); + dma_set_seg_boundary(&uent->dev, GENMASK(31, 0)); + + ubc = (struct ub_bus_controller *)ctx; + node = ub_ubc_to_node(ubc); + list = &ubc->devs; + + set_dev_node(&uent->dev, node); + ub_entity_assign_priv_flag(uent, UB_ENTITY_DETACHED, false); + + down_write(&ub_bus_sem); + list_add_tail(&uent->node, list); + up_write(&ub_bus_sem); + + uent->match_driver = false; + ret = device_add(&uent->dev); + WARN_ON(ret < 0); + + ret = ub_ports_add(uent); + WARN_ON(ret); } EXPORT_SYMBOL_GPL(ub_entity_add); void ub_start_ent(struct ub_entity *uent) { + int ret; + + if (!uent) + return; + + uent->match_driver = true; + ret = device_attach(&uent->dev); + if (ret < 0 && ret != -EPROBE_DEFER) + ub_err(uent, "device attach failed, ret=%d\n", ret); + + ub_entity_assign_priv_flag(uent, UB_ENTITY_START, true); } EXPORT_SYMBOL_GPL(ub_start_ent); +static void ub_release_ent(struct device *dev) +{ + struct ub_entity *uent; + + uent = to_ub_entity(dev); + + ub_route_clear(uent); + ub_cna_free(uent); + ub_ports_unset(uent); + + message_remove_device(uent); + ub_ubc_put(uent->ubc); + + kfree(uent->driver_override); + uent->token_value = 0; + kfree(uent); + pr_info("uent release\n"); +} + +void ub_stop_ent(struct ub_entity *uent) +{ + if (!uent) + return; + + if (!ub_entity_test_priv_flag(uent, UB_ENTITY_START)) + return; + ub_entity_assign_priv_flag(uent, UB_ENTITY_START, false); + + device_release_driver(&uent->dev); + uent->match_driver = false; +} +EXPORT_SYMBOL_GPL(ub_stop_ent); + +void ub_remove_ent(struct ub_entity *uent) +{ + if (!uent->dev.kobj.parent) + return; + + ub_ports_del(uent); + + device_del(&uent->dev); + down_write(&ub_bus_sem); + list_del(&uent->node); + up_write(&ub_bus_sem); + + ub_unconfigure_ent(uent); + ub_entity_unset_mmio(uent); + ub_entity_num_free(uent); + ub_eid_free(uent); + ub_entity_assign_priv_flag(uent, UB_ENTITY_SETUP, false); + + put_device(&uent->dev); +} + +void ub_stop_and_remove_ent(struct ub_entity *uent) +{ + if (!uent) + return; + + ub_stop_ent(uent); + ub_remove_ent(uent); +} +EXPORT_SYMBOL_GPL(ub_stop_and_remove_ent); + void ub_stop_entities(void) { } diff --git a/drivers/ub/ubus/ubus_entity.h b/drivers/ub/ubus/ubus_entity.h index 5cb3f020e4e6..811edacf6db8 100644 --- a/drivers/ub/ubus/ubus_entity.h +++ b/drivers/ub/ubus/ubus_entity.h @@ -10,6 +10,7 @@ struct ub_entity *ub_alloc_ent(void); int ub_setup_ent(struct ub_entity *uent); void ub_entity_add(struct ub_entity *uent, void *ctx); void ub_start_ent(struct ub_entity *uent); +void ub_remove_ent(struct ub_entity *uent); void ub_stop_entities(void); void ub_remove_entities(void); diff --git a/include/ub/ubus/ubus.h b/include/ub/ubus/ubus.h index 56a6e24015e6..b5e9a413a5b4 100644 --- a/include/ub/ubus/ubus.h +++ b/include/ub/ubus/ubus.h @@ -154,6 +154,10 @@ struct ub_entity { u16 port_nums; struct ub_port *ports; + /* entity DMA info */ + u64 dma_mask; + struct device_dma_parameters dma_parms; + /* entity route info */ struct list_head cna_list; /* store distance for cna in route table */ @@ -481,6 +485,28 @@ int __ub_register_driver(struct ub_driver *drv, struct module *owner, __ub_register_driver(driver, THIS_MODULE, KBUILD_MODNAME) void ub_unregister_driver(struct ub_driver *drv); +/** + * ub_stop_ent() - Stop the entity. + * @uent: UB entity. + * + * Call device_release_driver(), user can't use it again, if it's a mue, + * will stop all ues under it, if it's entity0, will stop all entity under it. + * + * Context: Any context. + */ +void ub_stop_ent(struct ub_entity *uent); + +/** + * ub_stop_and_remove_ent() - Stop and remove the entity from system. + * @uent: UB entity. + * + * Call device_release_driver() and device_unregister(), if it's a mue, + * will remove all ues under it, if it's entity0, will remove all entity under it. + * + * Context: Any context. + */ +void ub_stop_and_remove_ent(struct ub_entity *uent); + #else /* CONFIG_UB_UBUS is not enabled */ #define dev_is_ub(d) (false) static inline struct ub_entity *ub_entity_get(struct ub_entity *uent) @@ -532,6 +558,8 @@ __ub_register_driver(struct ub_driver *drv, struct module *owner, static inline int ub_register_driver(struct ub_driver *drv) { return 0; } static inline void ub_unregister_driver(struct ub_driver *drv) {} +static inline void ub_stop_ent(struct ub_entity *uent) {} +static inline void ub_stop_and_remove_ent(struct ub_entity *uent) {} #endif /* CONFIG_UB_UBUS */ #endif /* _UB_UBUS_UBUS_H_ */ -- Gitee From 6972c50b6b1c00d4d897395623c4e62485764452 Mon Sep 17 00:00:00 2001 From: Junlong Zheng Date: Fri, 19 Sep 2025 19:07:36 +0800 Subject: [PATCH 27/45] ub:ubus: Support for multiple mue enablement driver inclusion category: feature bugzilla: https://gitee.com/openeuler/kernel/issues/ID34DG CVE: NA ----------------------------------------------------------- The UB device supports multiple entity models, and here we implement the enable/disable functionality for the mue. Signed-off-by: Junlong Zheng --- drivers/ub/ubus/Makefile | 2 +- drivers/ub/ubus/entity.c | 25 +++ drivers/ub/ubus/enum.c | 1 + drivers/ub/ubus/ubus_entity.c | 371 ++++++++++++++++++++++++++++++++-- drivers/ub/ubus/ubus_entity.h | 1 + include/ub/ubus/ubus.h | 33 +++ 6 files changed, 418 insertions(+), 15 deletions(-) create mode 100644 drivers/ub/ubus/entity.c diff --git a/drivers/ub/ubus/Makefile b/drivers/ub/ubus/Makefile index 906372a5be99..cb195c0f5742 100644 --- a/drivers/ub/ubus/Makefile +++ b/drivers/ub/ubus/Makefile @@ -1,6 +1,6 @@ # SPDX-License-Identifier: GPL-2.0+ -obj-$(CONFIG_UB_UBUS) += ub-driver.o controller.o config.o +obj-$(CONFIG_UB_UBUS) += ub-driver.o controller.o config.o entity.o ubus-y := ubus_driver.o sysfs.o ubus_controller.o msg.o ubus_config.o port.o cc.o eid.o cna.o route.o ubus-y += enum.o resource.o ubus_entity.o diff --git a/drivers/ub/ubus/entity.c b/drivers/ub/ubus/entity.c new file mode 100644 index 000000000000..5d91ee2c2c3e --- /dev/null +++ b/drivers/ub/ubus/entity.c @@ -0,0 +1,25 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (c) HiSilicon Technologies Co., Ltd. 2025. All rights reserved. + */ + +#include "ubus_inner.h" + +int ub_get_dst_eid(struct ub_entity *dev) +{ + struct ub_bus_controller *ubc; + u32 eid; + + if (!dev) + return -EINVAL; + + ubc = dev->ubc; + + if (!ubc) + return -ENODEV; + + eid = ubc->uent->eid; + + return eid; +} +EXPORT_SYMBOL_GPL(ub_get_dst_eid); diff --git a/drivers/ub/ubus/enum.c b/drivers/ub/ubus/enum.c index 4b25412c8c50..a11a380b9e7b 100644 --- a/drivers/ub/ubus/enum.c +++ b/drivers/ub/ubus/enum.c @@ -1030,6 +1030,7 @@ static struct ub_entity *ub_enum_create_uent(struct ub_bus_controller *ubc) uent->pue = uent; uent->entity_idx = 0; + uent->is_mue = 1; uent->ubc = ub_ubc_get(ubc); uent->upi = UB_CP_UPI; ret = message_probe_device(uent); diff --git a/drivers/ub/ubus/ubus_entity.c b/drivers/ub/ubus/ubus_entity.c index ae697e80c528..e90f34ab5407 100644 --- a/drivers/ub/ubus/ubus_entity.c +++ b/drivers/ub/ubus/ubus_entity.c @@ -41,11 +41,12 @@ struct ub_entity *ub_alloc_ent(void) return NULL; INIT_LIST_HEAD(&uent->node); + INIT_LIST_HEAD(&uent->mue_list); INIT_LIST_HEAD(&uent->cna_list); uent->dev.type = &ub_dev_type; uent->cna = 0; - + uent->tid = 0; /* default tid according to ummu */ ub_entity_assign_priv_flag(uent, UB_ENTITY_DETACHED, true); return uent; @@ -66,6 +67,27 @@ static int ub_entity_num_alloc(void) static int ub_get_guid(struct ub_entity *uent) { + u16 code; + int ret; + + if (is_primary(uent) && !uent->pool) + return 0; + + if (!uent->pool || uent_type(uent->pue) == UB_TYPE_ICONTROLLER) { + uent_type(uent) = uent_type(uent->pue); + ret = ub_cfg_read_guid(uent); + if (ret) + return ret; + } + + ret = ub_cfg_read_word(uent, UB_CLASS_CODE, &code); + if (ret) + return ret; + + uent->class_code = code; + ub_entity_type_init(uent); + if (!ub_type_valid(uent, false)) + return -EINVAL; return 0; } @@ -96,15 +118,82 @@ static void ub_config_upi(struct ub_entity *uent) ub_err(uent, "cfg upi fail, ret=%d\n", ret); } +static void ub_res_space_prepare(struct ub_entity *pue) +{ + u32 source_support_map[MAX_UB_RES_NUM] = { UB_ERS0S_SUPPORT, + UB_ERS1S_SUPPORT, + UB_ERS2S_SUPPORT }; + u32 support_feature = 0; + int i; + + if (ub_cfg_read_dword(pue, UB_CFG1_SUPPORT_FEATURE_L, &support_feature)) { + ub_err(pue, "read cfg1 support feature fail\n"); + return; + } + + if (is_idev(pue) || is_ibus_controller(pue)) { + if (!(support_feature & UB_UBBAS_SUPPORT)) + return; + + for (i = 0; i < MAX_UB_RES_NUM; i++) + if (support_feature & source_support_map[i]) + pue->zone[i].ubba_used = 1; + return; + } + + for (i = 0; i < MAX_UB_RES_NUM; i++) { + if (!(support_feature & source_support_map[i])) + continue; + + pue->zone[i].sa_used = 1; + if (is_p_device(pue)) + pue->zone[i].res.flags = IORESOURCE_MEM; + } +} + static int ub_setup_ent_primary(struct ub_entity *uent) { - return -EINVAL; + int ret; + + if (uent->ent_type == UB_ENT_UNKNOWN) { + ub_err(uent, "ignore unknown entity type, type=%#x, class=%#x\n", + uent_type(uent), uent_class(uent)); + return -EIO; + } + + ub_res_space_prepare(uent); + + if (is_p_device(uent)) + return 0; + + ret = ub_cfg_read_word(uent, UB_TOTAL_NUMBER_ENTITIES, + (u16 *)&uent->total_funcs); + if (ret) { + ub_err(uent, "get UB_TOTAL_NUMBER_ENTITIES fail, ret=%d\n", ret); + return ret; + } + + if (!uent->total_funcs) { + ub_err(uent, "total_funcs is zero\n"); + return -EINVAL; + } + /* Number of Entities presented to users, except Entity0. */ + uent->total_funcs -= 1; + + return 0; +} + +static int ub_setup_ent_normal(struct ub_entity *uent) +{ + uent->dev.parent = &uent->pue->dev; + return 0; } static int ub_uent_cfg(struct ub_entity *uent, u32 uent_num) { struct ub_guid *guid = &uent->guid; char buf[SZ_64] = {}; + int ret; dev_set_name(&uent->dev, "%05x", uent_num); uent->uent_num = uent_num; @@ -116,7 +205,12 @@ static int ub_uent_cfg(struct ub_entity *uent, u32 uent_num) /* Card driver set to 64bit if support */ uent->dma_mask = GENMASK(31, 0); - return ub_setup_ent_primary(uent); + if (is_primary(uent) || is_p_device(uent)) + ret = ub_setup_ent_primary(uent); + else + ret = ub_setup_ent_normal(uent); + + return ret; } static void ub_config_eid(struct ub_entity *uent) @@ -221,6 +315,7 @@ void ub_entity_add(struct ub_entity *uent, void *ctx) { struct ub_bus_controller *ubc; struct list_head *list; + struct ub_entity *pue; int ret, node; if (!uent || !ctx) @@ -240,9 +335,15 @@ void ub_entity_add(struct ub_entity *uent, void *ctx) dma_set_max_seg_size(&uent->dev, UB_DEFAULT_MAX_SEG_SIZE); dma_set_seg_boundary(&uent->dev, GENMASK(31, 0)); - ubc = (struct ub_bus_controller *)ctx; - node = ub_ubc_to_node(ubc); - list = &ubc->devs; + if (is_primary(uent) || is_p_device(uent)) { + ubc = (struct ub_bus_controller *)ctx; + node = ub_ubc_to_node(ubc); + list = &ubc->devs; + } else { + pue = (struct ub_entity *)ctx; + node = dev_to_node(&pue->dev); + list = &pue->mue_list; + } set_dev_node(&uent->dev, node); ub_entity_assign_priv_flag(uent, UB_ENTITY_DETACHED, false); @@ -255,11 +356,14 @@ void ub_entity_add(struct ub_entity *uent, void *ctx) ret = device_add(&uent->dev); WARN_ON(ret < 0); - ret = ub_ports_add(uent); - WARN_ON(ret); + if (is_primary(uent)) { + ret = ub_ports_add(uent); + WARN_ON(ret); + } } EXPORT_SYMBOL_GPL(ub_entity_add); +static int ub_mue_enable_and_map(struct ub_entity *uent); void ub_start_ent(struct ub_entity *uent) { int ret; @@ -272,6 +376,12 @@ void ub_start_ent(struct ub_entity *uent) if (ret < 0 && ret != -EPROBE_DEFER) ub_err(uent, "device attach failed, ret=%d\n", ret); + if (is_primary(uent) && !is_p_device(uent)) { + ret = ub_mue_enable_and_map(uent); + if (ret) + ub_err(uent, "enable pue failed, ret=%d\n", ret); + } + ub_entity_assign_priv_flag(uent, UB_ENTITY_START, true); } EXPORT_SYMBOL_GPL(ub_start_ent); @@ -281,13 +391,18 @@ static void ub_release_ent(struct device *dev) struct ub_entity *uent; uent = to_ub_entity(dev); - - ub_route_clear(uent); - ub_cna_free(uent); - ub_ports_unset(uent); + if (is_primary(uent) && !is_p_device(uent)) { + ub_route_clear(uent); + ub_cna_free(uent); + ub_ports_unset(uent); + } message_remove_device(uent); - ub_ubc_put(uent->ubc); + + if (is_primary(uent) || (is_p_device(uent) && uent->is_mue)) + ub_ubc_put(uent->ubc); + else + ub_entity_put(uent->pue); kfree(uent->driver_override); uent->token_value = 0; @@ -297,6 +412,8 @@ static void ub_release_ent(struct device *dev) void ub_stop_ent(struct ub_entity *uent) { + struct ub_entity *ent, *tmp; + if (!uent) return; @@ -304,6 +421,10 @@ void ub_stop_ent(struct ub_entity *uent) return; ub_entity_assign_priv_flag(uent, UB_ENTITY_START, false); + /* Stop mue in primary dev, when uent is entN, mue_list is NULL */ + list_for_each_entry_safe_reverse(ent, tmp, &uent->mue_list, node) + ub_stop_ent(ent); + device_release_driver(&uent->dev); uent->match_driver = false; } @@ -311,10 +432,17 @@ EXPORT_SYMBOL_GPL(ub_stop_ent); void ub_remove_ent(struct ub_entity *uent) { + struct ub_entity *ent, *tmp; + if (!uent->dev.kobj.parent) return; - ub_ports_del(uent); + /* Remove mue in primary dev, when uent is entN, mue_list is NULL */ + list_for_each_entry_safe_reverse(ent, tmp, &uent->mue_list, node) + ub_remove_ent(ent); + + if (is_primary(uent)) + ub_ports_del(uent); device_del(&uent->dev); down_write(&ub_bus_sem); @@ -342,8 +470,223 @@ EXPORT_SYMBOL_GPL(ub_stop_and_remove_ent); void ub_stop_entities(void) { + struct ub_bus_controller *ubc; + struct ub_entity *uent; + + list_for_each_entry_reverse(ubc, &ubc_list, node) + list_for_each_entry_reverse(uent, &ubc->devs, node) + ub_stop_ent(uent); } void ub_remove_entities(void) { + struct ub_bus_controller *ubc; + struct ub_entity *uent, *tmp; + + list_for_each_entry_reverse(ubc, &ubc_list, node) + list_for_each_entry_safe_reverse(uent, tmp, &ubc->devs, node) + ub_remove_ent(uent); +} + +struct entity_info_msg_pld_rsp { + u32 reserved; + u16 entity_nums; + u16 mue_nums; + struct ue_map map[]; +}; +#define ENTITY_INFO_BASE_PLD_SIZE 8 + +struct entity_info_msg_pld { + /* request payload is NULL */ + struct entity_info_msg_pld_rsp rsp; +}; + +struct entity_info_msg_pkt { + struct msg_pkt_header header; + struct entity_info_msg_pld pld; +}; + +static int ub_obtain_entity_info_rsp_handle(struct ub_entity *uent, + struct entity_info_msg_pkt *pkt, u16 *mue_nums, + struct ue_map *map) +{ + struct msg_extended_header *etah = &pkt->header.msgetah; + struct entity_info_msg_pld_rsp *rsp = &pkt->pld.rsp; + size_t size; + u16 i; + + if (etah->rsp_status != UB_MSG_RSP_SUCCESS) { + ub_err(uent, "obtain entity info rsp, status=%#02x\n", + etah->rsp_status); + return -EINVAL; + } + + if (etah->plen < ENTITY_INFO_BASE_PLD_SIZE) { + ub_err(uent, "obtain entity info plen=%#x invalid\n", + etah->plen); + return -EINVAL; + } + + uent->total_funcs = rsp->entity_nums; + *mue_nums = rsp->mue_nums; + + size = sizeof(struct ue_map) * (*mue_nums); + if (*mue_nums == 0 || size > SZ_1K) { + ub_err(uent, "mue_nums or size error, mue_nums=%#x, size=%#lx\n", + *mue_nums, size); + return -EINVAL; + } + + if (etah->plen != (*mue_nums * SZ_4 + ENTITY_INFO_BASE_PLD_SIZE)) { + ub_err(uent, "obtain entity info plen=%#x, mue_nums=%#x invalid\n", + etah->plen, *mue_nums); + return -EINVAL; + } + + for (i = 0; i < *mue_nums; i++) { + map[i].start_entity_idx = rsp->map[i].start_entity_idx; + map[i].end_entity_idx = rsp->map[i].end_entity_idx; + } + + return 0; +} + +static int ub_obtain_entity_info(struct ub_entity *uent, u16 *mue_nums, + struct ue_map *map) +{ + struct message_device *mdev = uent->message->mdev; + struct entity_info_msg_pkt req_pkt = {}; + struct entity_info_msg_pkt *rsp_pkt; + struct msg_info info = {}; + int ret; + + if (is_ibus_controller(uent)) { + *mue_nums = 1; + map->start_entity_idx = 0; + map->end_entity_idx = 0; + return 0; + } + + ub_msg_pkt_header_init(&req_pkt.header, uent, 0, + code_gen(UB_MSG_CODE_EXCH, UB_OBTAIN_ENTITY_INFO, + MSG_REQ), false); + + rsp_pkt = kzalloc(SZ_2K, GFP_KERNEL); + if (!rsp_pkt) + return -ENOMEM; + + message_info_init( + &info, uent, &req_pkt, rsp_pkt, + (sizeof(struct msg_pkt_header) << MSG_REQ_SIZE_OFFSET) | SZ_2K); + + ret = message_sync_request(mdev, &info, req_pkt.header.msgetah.code); + if (ret) + goto out; + + ret = ub_obtain_entity_info_rsp_handle(uent, rsp_pkt, mue_nums, map); +out: + kfree(rsp_pkt); + return ret; +} + +static int ub_enable_mues(struct ub_entity *pue, int nums, struct ue_map *map); +int ub_mue_enable_and_map(struct ub_entity *uent) +{ + struct ue_map *map; + u16 mue_nums; + int ret; + + map = kzalloc(SZ_1K, GFP_KERNEL); + if (!map) + return -ENOMEM; + + ret = ub_obtain_entity_info(uent, &mue_nums, map); + if (ret) + goto out; + + /* add map information to Entity0 */ + uent->uem.start_entity_idx = map[0].start_entity_idx; + uent->uem.end_entity_idx = map[0].end_entity_idx; + uent->total_ues = uent->uem.end_entity_idx - uent->uem.start_entity_idx + 1; + if (uent->entity_idx == map[0].start_entity_idx) + uent->total_ues = 0; + + ret = ub_enable_mues(uent, mue_nums - 1, map); + +out: + kfree(map); + return ret; +} + +static int ub_enable_ent(struct ub_entity *pue, int idx, u8 is_mue, + struct ue_map *map) +{ + int ret; + struct ub_entity *ue; + + ue = ub_alloc_ent(); + if (!ue) + return -ENOMEM; + + ue->entity_idx = idx; + ue->pue = pue; + ue->dev.parent = &pue->dev; + ue->ubc = pue->ubc; + ue->cna = pue->cna; + ue->upi = pue->upi; + + if (is_mue) { + ue->is_mue = is_mue; + ue->uem.start_entity_idx = map->start_entity_idx; + ue->uem.end_entity_idx = map->end_entity_idx; + ue->total_ues = map->end_entity_idx - map->start_entity_idx + 1; + if (ue->entity_idx == map->start_entity_idx) + ue->total_ues = 0; + } + + ret = ub_setup_ent(ue); + if (ret < 0) { + kfree(ue); + return ret; + } + + ub_entity_get(pue); + ub_entity_add(ue, pue); + ub_start_ent(ue); + + return 0; +} + +static void ub_disable_mues(struct ub_entity *pue); + +static int ub_enable_mues(struct ub_entity *pue, int nums, struct ue_map *map) +{ + int ret; + int i; + + for (i = 1; i <= nums; i++) { + ret = ub_enable_ent(pue, i, 1, &map[i]); + if (ret) + goto failed; + } + + return 0; +failed: + ub_disable_mues(pue); + return ret; +} + +void ub_disable_ent(struct ub_entity *uent) +{ + ub_info(uent, "disable entity, eid=%#05x\n", uent->eid); + ub_stop_and_remove_ent(uent); +} +EXPORT_SYMBOL_GPL(ub_disable_ent); + +static void ub_disable_mues(struct ub_entity *pue) +{ + struct ub_entity *mue, *tmp; + + list_for_each_entry_safe_reverse(mue, tmp, &pue->mue_list, node) + ub_disable_ent(mue); } diff --git a/drivers/ub/ubus/ubus_entity.h b/drivers/ub/ubus/ubus_entity.h index 811edacf6db8..d463269318fe 100644 --- a/drivers/ub/ubus/ubus_entity.h +++ b/drivers/ub/ubus/ubus_entity.h @@ -13,5 +13,6 @@ void ub_start_ent(struct ub_entity *uent); void ub_remove_ent(struct ub_entity *uent); void ub_stop_entities(void); void ub_remove_entities(void); +void ub_disable_ent(struct ub_entity *uent); #endif /* __UBUS_ENTITY_H__ */ diff --git a/include/ub/ubus/ubus.h b/include/ub/ubus/ubus.h index b5e9a413a5b4..8f798b6baf31 100644 --- a/include/ub/ubus/ubus.h +++ b/include/ub/ubus/ubus.h @@ -121,6 +121,11 @@ struct ub_port { DECLARE_BITMAP(cap_map, UB_PORT_CAP_NUM); }; +struct ue_map { + u16 start_entity_idx; + u16 end_entity_idx; +}; + struct ub_entity { /* Driver framework base info */ struct device dev; @@ -141,9 +146,17 @@ struct ub_entity { unsigned short entity_idx; u32 uent_num; /* ub dev number */ struct mmio_zone zone[MAX_UB_RES_NUM]; + unsigned int total_funcs; u32 token_id; u32 token_value; + /* mue & ue info */ + u8 is_mue; + u16 total_ues; + u16 num_ues; + struct ue_map uem; + struct list_head mue_list; /* management ub entity list */ + /* entity topology info */ struct list_head node; struct ub_bus_controller *ubc; @@ -162,6 +175,10 @@ struct ub_entity { struct list_head cna_list; /* store distance for cna in route table */ struct dev_message *message; + + /* UB entity TID */ + u32 tid; + u32 support_feature; u16 upi; @@ -302,6 +319,20 @@ extern struct bus_type ub_bus_type; void ub_bus_type_iommu_ops_set(const struct iommu_ops *ops); const struct iommu_ops *ub_bus_type_iommu_ops_get(void); +/** + * ub_get_dst_eid() - Obtain the Dest EID of the entity. + * @uent: UB entity. + * + * Return the EID of bus instance if the entity has already been bound, + * or controller's EID. + * + * Context: Any context. + * Return: positive number if success, or %-EINVAL if @dev is %NULL, + * or %-ENODEV if entity's controller %NULL, or 0 if entity hasn't been + * initialized. + */ +int ub_get_dst_eid(struct ub_entity *uent); + /** * ub_iomap() - Map the resource space of the entity. * @uent: UB entity. @@ -512,6 +543,8 @@ void ub_stop_and_remove_ent(struct ub_entity *uent); static inline struct ub_entity *ub_entity_get(struct ub_entity *uent) { return NULL; } static inline void ub_entity_put(struct ub_entity *uent) {} +static inline int ub_get_dst_eid(struct ub_entity *uent) +{ return -ENODEV; } static inline void __iomem * ub_iomap(struct ub_entity *uent, int resno, unsigned long maxlen) { return NULL; } -- Gitee From cec1577cdd4e9649e7d6ebbe4fac23992bd8d394 Mon Sep 17 00:00:00 2001 From: Yahui Liu Date: Mon, 13 Oct 2025 16:12:04 +0800 Subject: [PATCH 28/45] ub:ubus: Support device level and port level reset driver inclusion category: feature bugzilla: https://gitee.com/openeuler/kernel/issues/ID34DG CVE: NA ----------------------------------------------------------- Support device level and port level reset. Signed-off-by: Yahui Liu --- drivers/ub/ubus/Makefile | 2 +- drivers/ub/ubus/port.c | 23 +++ drivers/ub/ubus/port.h | 4 + drivers/ub/ubus/reset.c | 326 ++++++++++++++++++++++++++++++++++ drivers/ub/ubus/reset.h | 16 ++ drivers/ub/ubus/ubus_entity.c | 2 + include/ub/ubus/ubus.h | 56 ++++++ 7 files changed, 428 insertions(+), 1 deletion(-) create mode 100644 drivers/ub/ubus/reset.c create mode 100644 drivers/ub/ubus/reset.h diff --git a/drivers/ub/ubus/Makefile b/drivers/ub/ubus/Makefile index cb195c0f5742..37578637b453 100644 --- a/drivers/ub/ubus/Makefile +++ b/drivers/ub/ubus/Makefile @@ -3,6 +3,6 @@ obj-$(CONFIG_UB_UBUS) += ub-driver.o controller.o config.o entity.o ubus-y := ubus_driver.o sysfs.o ubus_controller.o msg.o ubus_config.o port.o cc.o eid.o cna.o route.o -ubus-y += enum.o resource.o ubus_entity.o +ubus-y += enum.o resource.o ubus_entity.o reset.o obj-$(CONFIG_UB_UBUS_BUS) += ubus.o diff --git a/drivers/ub/ubus/port.c b/drivers/ub/ubus/port.c index 8aa7a7053cc0..74158f9b0d55 100644 --- a/drivers/ub/ubus/port.c +++ b/drivers/ub/ubus/port.c @@ -8,6 +8,7 @@ #include #include "ubus.h" +#include "reset.h" #include "port.h" struct ub_port_attribute { @@ -148,6 +149,26 @@ static ssize_t asy_link_width_show(struct ub_port *port, char *buf) } UB_PORT_ATTR_RO(asy_link_width); +static ssize_t port_reset_store(struct ub_port *port, const char *buf, + size_t count) +{ + unsigned long val; + int ret; + + ret = kstrtoul(buf, 0, &val); + if (ret || (val != 1)) { + ub_err(port->uent, "Invalid val for port reset\n"); + return -EINVAL; + } + + ret = ub_port_reset_function(port); + if (ret < 0) + return ret; + + return count; +} +UB_PORT_ATTR_WO(port_reset); + static ssize_t glb_qdlws_show(struct ub_port *port, char *buf) { u8 val; @@ -310,6 +331,7 @@ static struct attribute *ub_port_default_attrs[] = { &ub_port_attr_neighbor_port_idx.attr, &ub_port_attr_neighbor_guid.attr, &ub_port_attr_neighbor.attr, + &ub_port_attr_port_reset.attr, NULL }; @@ -508,6 +530,7 @@ static void ub_port_init(struct ub_entity *uent, struct ub_port *port) port->type = PHYSICAL; port->cna = 0; port->r_uent = NULL; + port->link_state = LINK_STATE_NORMAL; port->r_index = 0; port->r_guid = guid_null; bitmap_zero(port->cna_maps, UB_MAX_CNA_NUM); diff --git a/drivers/ub/ubus/port.h b/drivers/ub/ubus/port.h index b8757fad7060..3135d25ebdf4 100644 --- a/drivers/ub/ubus/port.h +++ b/drivers/ub/ubus/port.h @@ -23,5 +23,9 @@ int ub_ports_add(struct ub_entity *uent); void ub_ports_del(struct ub_entity *uent); int ub_ports_setup(struct ub_entity *uent); void ub_ports_unset(struct ub_entity *uent); +void ub_notify_share_port(struct ub_port *port, + enum ub_share_port_notify_type type); + +int ub_port_write_dword(struct ub_port *port, u32 pos, u32 val); #endif /* __PORT_H__ */ diff --git a/drivers/ub/ubus/reset.c b/drivers/ub/ubus/reset.c new file mode 100644 index 000000000000..97cf00001d7a --- /dev/null +++ b/drivers/ub/ubus/reset.c @@ -0,0 +1,326 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (c) HiSilicon Technologies Co., Ltd. 2025. All rights reserved. + */ + +#define pr_fmt(fmt) "ubus reset: " fmt + +#include + +#include "ubus.h" +#include "port.h" +#include "route.h" +#include "ubus_controller.h" +#include "ubus_config.h" + +enum elr_type { + ELR_PREPARE = 0, + ELR_DONE = 1, +}; + +enum SAVED_CFG_SPACE_NUM { + DEV_TOKEN_ID = 0, +}; + +static u32 saved_cfg_offset[] = { + [DEV_TOKEN_ID] = UB_CFG1_BASIC + 0xb4, +}; + +/** + * ub_elr - Initiate an UB entity level reset + * @dev: UB entity to reset + */ +int ub_elr(struct ub_entity *dev) +{ + u8 command; + u8 val = 0; + int ret; + + /* enable ELR */ + command = 0x01; + ret = ub_cfg_write_byte(dev, UB_ELR, command); + if (ret) { + ub_err(dev, "dev elr failed, write byte error, ret=%d", ret); + return ret; + } + + /* + * ub entity must complete an ELR within 100ms, + * but may silently discard requests while the ELR is in + * progress. Wait 100ms before trying to access the device. + */ + msleep(100); + + ret = ub_cfg_read_byte(dev, UB_ELR_DONE, &val); + if (ret || !val) { + ub_err(dev, "dev elr failed, ret=%d, ELR_DONE=%u\n", ret, val); + return -EINVAL; + } + + ub_info(dev, "dev elr success\n"); + + return 0; +} + +/** + * ub_device_reset - Initiate a UB entity reset + * @ent: UB entity. + */ +int ub_device_reset(struct ub_entity *ent) +{ + u16 command; + int ret; + + if (!ent) { + pr_err("device which will be reset is NULL\n"); + return -EINVAL; + } + + if (is_ibus_controller(ent)) { + ub_warn(ent, "ub bus controller do not support reset.\n"); + return -EINVAL; + } + + /* device reset only support Entity0 */ + if (ent->entity_idx) { + ub_err(ent, "device is not entity0, entity_idx=%u\n", ent->entity_idx); + return -EINVAL; + } + + device_lock(&ent->dev); + + /* enable device reset */ + command = 0x01; + ret = ub_send_cfg(ent, (u8)sizeof(u16), UB_ENTITY_RST, (u32 *)&command); + if (ret) { + ub_err(ent, "device reset fail, ret=%d\n", ret); + device_unlock(&ent->dev); + return -EIO; + } + + device_unlock(&ent->dev); + ub_info(ent, "device reset success\n"); + + return 0; +} +EXPORT_SYMBOL_GPL(ub_device_reset); + +static void ub_save_token_state(struct ub_entity *dev) +{ + int ret; + int i; + + for (i = DEV_TOKEN_ID; i <= DEV_TOKEN_ID; i++) { + ret = ub_cfg_read_dword(dev, saved_cfg_offset[i], + &dev->saved_config_space[i]); + if (ret) { + ub_err(dev, "ub cfg read dword failed, save cfg offset: %#x.\n", + saved_cfg_offset[i]); + continue; + } + ub_info(dev, "saving config space at address %#x (reading %#x)\n", + saved_cfg_offset[i], dev->saved_config_space[i]); + } +} + +static int ub_save_state(struct ub_entity *dev) +{ + const struct ub_error_handlers *err_handler = + dev->driver ? dev->driver->err_handler : NULL; + + if (err_handler && err_handler->reset_prepare) + err_handler->reset_prepare(dev); + + ub_save_token_state(dev); + + dev->state_saved = true; + + return 0; +} + +static void ub_restore_config_dword(struct ub_entity *dev, u32 pos, u32 saved_val) +{ + int retry = 10; + u32 val; + int ret; + + ret = ub_cfg_read_dword(dev, pos, &val); + if (ret || val == saved_val) + return; + while (retry-- > 0) { + ub_info(dev, "restoring config space at address %#x (was %#x, writing %#x)\n", + pos, val, saved_val); + ub_cfg_write_dword(dev, pos, saved_val); + + if (ub_cfg_read_dword(dev, pos, &val)) + continue; + + if (val == saved_val) + return; + +#define RESTORE_RETRY_SLEEP_MS 1 + msleep(RESTORE_RETRY_SLEEP_MS); + } +} + +static void ub_restore_state(struct ub_entity *dev) +{ + const struct ub_error_handlers *err_handler = + dev->driver ? dev->driver->err_handler : NULL; + int i; + + if (!dev->state_saved) + return; + + for (i = DEV_TOKEN_ID; i <= DEV_TOKEN_ID; i++) { + ub_restore_config_dword(dev, saved_cfg_offset[i], + dev->saved_config_space[i]); + } + + dev->state_saved = false; + + if (err_handler && err_handler->reset_done) + err_handler->reset_done(dev); +} + +static int ub_reset_check(struct ub_entity *dev) +{ + if (is_ibus_controller(dev)) { + ub_err(dev, "UB Bus Controller does not support ELR!\n"); + return -EINVAL; + } + + if (!dev->reset_fn) + return -ENOTTY; + + return 0; +} + +int ub_reset_entity(struct ub_entity *dev) +{ + int ret, rc; + + if (!dev) { + pr_err("device is NULL\n"); + return -EINVAL; + } + + rc = ub_reset_check(dev); + if (rc) + return rc; + + if (!device_trylock(&dev->dev)) + return -EBUSY; + + ret = ub_save_state(dev); + if (ret) { + device_unlock(&dev->dev); + return ret; + } + + rc = ub_elr(dev); + + ub_restore_state(dev); + + device_unlock(&dev->dev); + + return rc; +} +EXPORT_SYMBOL_GPL(ub_reset_entity); + +int ub_port_reset_check(struct ub_entity *dev, int port_id) +{ + struct ub_port *port = NULL; + + if (!dev) + return -EINVAL; + + if (is_idev(dev)) { + ub_err(dev, "IDEV does not support port reset!\n"); + return -EINVAL; + } + + if (port_id >= dev->port_nums) { + ub_err(dev, "Can't reset port because port(%d) is over port_nums(%u)!\n", + port_id, dev->port_nums); + return -EINVAL; + } + + port = dev->ports + port_id; + + if (port->type == VIRTUAL) { + ub_err(dev, "vport reset is not supported now!\n"); + return -EINVAL; + } + + return 0; +} + +static bool ub_wait_port_complete(struct ub_port *port) +{ + u64 timeout = 0; + +#define TOTAL_WAIT_CNT 150 /* wait 15s for port reset */ +#define WAIT_TIME 100 + + do { + if (port->link_state == LINK_STATE_DONE) + return true; + + msleep(WAIT_TIME); + timeout++; + } while (timeout < TOTAL_WAIT_CNT); + + return false; +} + +int ub_port_reset(struct ub_entity *dev, int port_id) +{ + struct ub_port *port = NULL; + int ret; + + if (ub_port_reset_check(dev, port_id)) + return -EINVAL; + + device_lock(&dev->dev); + port = dev->ports + port_id; + if (port->link_state != LINK_STATE_NORMAL) { + ub_err(dev, "port reset is not complete last time!\n"); + device_unlock(&dev->dev); + return -EINVAL; + } + + if (port->shareable) + ub_notify_share_port(port, RESET_PREPARE); + + /* enable port reset */ + ret = ub_port_write_dword(port, UB_PORT_RST, 0x01); + if (ret) { + ub_err(port->uent, "port reset fail, dev_eid=%u, port_id=%d, ret=%d\n", + dev->eid, port_id, ret); + device_unlock(&dev->dev); + return -EIO; + } + + port->link_state = LINK_STATE_RESETING; + + device_unlock(&dev->dev); + + if (ub_wait_port_complete(port)) { + if (port->shareable) + ub_notify_share_port(port, RESET_DONE); + port->link_state = LINK_STATE_NORMAL; + ub_info(dev, "port(%d) reset success!\n", port_id); + return ret; + } + + port->link_state = LINK_STATE_NORMAL; + ub_err(dev, "port(%d) reset timeout!\n", port_id); + return -ETIME; +} +EXPORT_SYMBOL_GPL(ub_port_reset); + +int ub_port_reset_function(struct ub_port *port) +{ + return ub_port_reset(port->uent, port->index); +} diff --git a/drivers/ub/ubus/reset.h b/drivers/ub/ubus/reset.h new file mode 100644 index 000000000000..f8ab56198c0a --- /dev/null +++ b/drivers/ub/ubus/reset.h @@ -0,0 +1,16 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Copyright (c) HiSilicon Technologies Co., Ltd. 2025. All rights reserved. + */ + +#ifndef __RESET_H__ +#define __RESET_H__ + +struct ub_port; +struct ub_entity; + +int ub_port_reset_function(struct ub_port *port); +int ub_port_reset(struct ub_entity *dev, int port_id); +int ub_port_reset_check(struct ub_entity *dev, int port_id); + +#endif /* __RESET_H__ */ diff --git a/drivers/ub/ubus/ubus_entity.c b/drivers/ub/ubus/ubus_entity.c index e90f34ab5407..932c21a49615 100644 --- a/drivers/ub/ubus/ubus_entity.c +++ b/drivers/ub/ubus/ubus_entity.c @@ -335,6 +335,8 @@ void ub_entity_add(struct ub_entity *uent, void *ctx) dma_set_max_seg_size(&uent->dev, UB_DEFAULT_MAX_SEG_SIZE); dma_set_seg_boundary(&uent->dev, GENMASK(31, 0)); + uent->state_saved = false; + if (is_primary(uent) || is_p_device(uent)) { ubc = (struct ub_bus_controller *)ctx; node = ub_ubc_to_node(ubc); diff --git a/include/ub/ubus/ubus.h b/include/ub/ubus/ubus.h index 8f798b6baf31..8bc0ecc57f2b 100644 --- a/include/ub/ubus/ubus.h +++ b/include/ub/ubus/ubus.h @@ -105,6 +105,12 @@ enum ub_port_type { #define UB_MAX_CNA_NUM SZ_64K #define UB_PORT_CAP_NUM SZ_256 +enum ub_link_state { + LINK_STATE_NORMAL = 0, + LINK_STATE_RESETING = 1, + LINK_STATE_DONE = 2, +}; + struct ub_port { struct ub_entity *uent; u16 index; @@ -119,6 +125,8 @@ struct ub_port { DECLARE_BITMAP(cna_maps, UB_MAX_CNA_NUM); /* cap cache */ DECLARE_BITMAP(cap_map, UB_PORT_CAP_NUM); + + enum ub_link_state link_state; }; struct ue_map { @@ -167,10 +175,15 @@ struct ub_entity { u16 port_nums; struct ub_port *ports; + unsigned int state_saved : 1; + /* entity DMA info */ u64 dma_mask; struct device_dma_parameters dma_parms; + /* UB reset info */ + unsigned int reset_fn : 1; + /* entity route info */ struct list_head cna_list; /* store distance for cna in route table */ @@ -179,11 +192,21 @@ struct ub_entity { /* UB entity TID */ u32 tid; + /* UB saved config space */ + u32 saved_config_space[24]; /* Config space saved at reset time */ + u32 support_feature; u16 upi; }; +/* UB bus error event callbacks */ +struct ub_error_handlers { + /* UB function reset prepare or completed */ + void (*reset_prepare)(struct ub_entity *uent); + void (*reset_done)(struct ub_entity *uent); +}; + struct ub_dynids { spinlock_t lock; /* Protects list, index */ struct list_head list; /* For IDs added at runtime */ @@ -218,6 +241,7 @@ struct ub_dynids { * context, so it can sleep. * @shutdown: Hook into reboot_notifier_list (kernel/sys.c). * Intended to stop any idling operations. + * @err_handler: Error handling callbacks. * @groups: Sysfs attribute groups. * @dev_groups: Attributes attached to the device that will be * created once it is bound to the driver. @@ -240,6 +264,7 @@ struct ub_driver { /* entity removed (NULL if not a hot-plug capable driver) */ void (*remove)(struct ub_entity *uent); void (*shutdown)(struct ub_entity *uent); + const struct ub_error_handlers *err_handler; const struct attribute_group **groups; const struct attribute_group **dev_groups; struct device_driver driver; @@ -401,6 +426,33 @@ int ub_register_share_port(struct ub_entity *uent, u16 port_id, void ub_unregister_share_port(struct ub_entity *uent, u16 port_id, struct ub_share_port_ops *ops); +/** + * ub_reset_entity() - Function entity level reset. + * @ent: UB entity. + * + * Reset a single entity without affecting other entities, If you want to reuse + * the entity after reset, you need to re-initialize it. + * + * Context: Any context + * Return: 0 if success, or %-EINVAL if entity not support elr, + * or %-ENOTTY if entity can't be reset safely, + * or -EBUSY if can't get device_trylock(), or other failed negative values. + */ +int ub_reset_entity(struct ub_entity *ent); + +/** + * ub_device_reset() - Device level reset. + * @ent: UB entity. + * + * Reset Device, include all entities under the device, If you want to reuse + * the device after reset, you need to re-initialize it. + * + * Context: Any context + * Return: 0 if success, or %-EINVAL if parameters invalid, + * or %-EIO if device can't reset now, can try later. + */ +int ub_device_reset(struct ub_entity *ent); + /** * ub_cfg_read_byte() - 1 byte configuration access read. * @uent: UB entity. @@ -584,6 +636,10 @@ static inline int ub_register_share_port(struct ub_entity *uent, u16 port_id, { return -ENODEV; } static inline void ub_unregister_share_port(struct ub_entity *uent, u16 port_id, struct ub_share_port_ops *ops) {} +static inline int ub_reset_entity(struct ub_entity *uent) +{ return -ENODEV; } +static inline int ub_device_reset(struct ub_entity *uent) +{ return -ENODEV; } static inline int __ub_register_driver(struct ub_driver *drv, struct module *owner, const char *mod_name) -- Gitee From feec3da6459507151eaf00a58920fef2775a0625 Mon Sep 17 00:00:00 2001 From: Jianquan Lin Date: Fri, 19 Sep 2025 20:05:42 +0800 Subject: [PATCH 29/45] ub:ubus: Add UBUS capability interfaces driver inclusion category: feature bugzilla: https://gitee.com/openeuler/kernel/issues/ID34DG CVE: NA ----------------------------------------------------------- Add some UBUS capability interfaces, including reading, writing, setting and clearing configuration space of UBUS capability. Signed-off-by: Jianquan Lin --- drivers/ub/ubus/Makefile | 2 +- drivers/ub/ubus/cap.c | 243 ++++++++++++++++++++++++++++++++++ drivers/ub/ubus/cap.h | 14 ++ drivers/ub/ubus/ubus_entity.c | 4 + include/ub/ubus/ubus.h | 6 + 5 files changed, 268 insertions(+), 1 deletion(-) create mode 100644 drivers/ub/ubus/cap.c create mode 100644 drivers/ub/ubus/cap.h diff --git a/drivers/ub/ubus/Makefile b/drivers/ub/ubus/Makefile index 37578637b453..231f87ce48df 100644 --- a/drivers/ub/ubus/Makefile +++ b/drivers/ub/ubus/Makefile @@ -3,6 +3,6 @@ obj-$(CONFIG_UB_UBUS) += ub-driver.o controller.o config.o entity.o ubus-y := ubus_driver.o sysfs.o ubus_controller.o msg.o ubus_config.o port.o cc.o eid.o cna.o route.o -ubus-y += enum.o resource.o ubus_entity.o reset.o +ubus-y += enum.o resource.o ubus_entity.o reset.o cap.o obj-$(CONFIG_UB_UBUS_BUS) += ubus.o diff --git a/drivers/ub/ubus/cap.c b/drivers/ub/ubus/cap.c new file mode 100644 index 000000000000..03f719422a2a --- /dev/null +++ b/drivers/ub/ubus/cap.c @@ -0,0 +1,243 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (c) HiSilicon Technologies Co., Ltd. 2025. All rights reserved. + */ + +#define pr_fmt(fmt) "ubus cap: " fmt + +#include "ubus.h" + +#define DW_CHECK 3 + +void ub_set_cap_bitmap(struct ub_entity *uent) +{ + int ret; + u32 i; + + for (i = 0; i < SZ_8; i++) { + ret = ub_cfg_read_dword(uent, UB_CFG1_CAP_BITMAP + (i << SZ_2), + &uent->cfg1_bitmap[i]); + if (ret) + ub_err(uent, "Read cfg1 cap bitmap failed, ret=%d\n", + ret); + } + + if (is_p_device(uent)) + return; + + for (i = 0; i < SZ_8; i++) { + ret = ub_cfg_read_dword(uent, UB_CFG0_CAP_BITMAP + (i << SZ_2), + &uent->cfg0_bitmap[i]); + if (ret) + ub_err(uent, "Read cfg0 cap bitmap failed, ret=%d\n", + ret); + } +} + +/* Check whether the capbility register is implemented. */ +static bool ub_cap_reg_implemented(struct ub_entity *uent, u32 cap) +{ + u32 i = (cap & 0xFF) / SZ_32; + u32 val = (cap >> SZ_8) ? uent->cfg1_bitmap[i] : uent->cfg0_bitmap[i]; + + return val & BIT((cap & 0xFF) % SZ_32); +} + +/* find the start address of capability */ +u32 ub_find_capability(u32 cap) +{ + return (cap << BITS_PER_BYTE) << SZ_2; +} + +int ub_cap_read_byte(struct ub_entity *uent, u32 cap, u32 off, u8 *val) +{ + int ret; + + *val = 0; + if (off >= SZ_1K) + return -EFAULT; + + if (!ub_cap_reg_implemented(uent, cap)) + return -ENXIO; + + ret = ub_cfg_read_byte(uent, ub_find_capability(cap) + off, val); + if (ret) + *val = 0; + + return ret; +} + +int ub_cap_read_word(struct ub_entity *uent, u32 cap, u32 off, u16 *val) +{ + int ret; + + *val = 0; + if (off & 1 || off >= SZ_1K) + return -EFAULT; + + if (!ub_cap_reg_implemented(uent, cap)) + return -ENXIO; + + ret = ub_cfg_read_word(uent, ub_find_capability(cap) + off, val); + if (ret) + *val = 0; + + return ret; +} + +int ub_cap_read_dword(struct ub_entity *uent, u32 cap, u32 off, u32 *val) +{ + int ret; + + *val = 0; + if (off & DW_CHECK || off >= SZ_1K) + return -EFAULT; + + if (!ub_cap_reg_implemented(uent, cap)) + return -ENXIO; + + ret = ub_cfg_read_dword(uent, ub_find_capability(cap) + off, val); + if (ret) + *val = 0; + + return ret; +} + +int ub_cap_write_byte(struct ub_entity *uent, u32 cap, u32 off, u8 val) +{ + if (off >= SZ_1K) + return -EFAULT; + + if (!ub_cap_reg_implemented(uent, cap)) + return -ENXIO; + + return ub_cfg_write_byte(uent, ub_find_capability(cap) + off, val); +} + +int ub_cap_write_word(struct ub_entity *uent, u32 cap, u32 off, u16 val) +{ + if (off & 1 || off >= SZ_1K) + return -EFAULT; + + if (!ub_cap_reg_implemented(uent, cap)) + return -ENXIO; + + return ub_cfg_write_word(uent, ub_find_capability(cap) + off, val); +} + +int ub_cap_write_dword(struct ub_entity *uent, u32 cap, u32 off, u32 val) +{ + if (off & DW_CHECK || off >= SZ_1K) + return -EFAULT; + + if (!ub_cap_reg_implemented(uent, cap)) + return -ENXIO; + + return ub_cfg_write_dword(uent, ub_find_capability(cap) + off, val); +} + +int ub_cap_clear_and_set_word(struct ub_entity *dev, u32 cap, u32 off, + u16 clear, u16 set) +{ + u16 val; + int ret; + + ret = ub_cap_read_word(dev, cap, off, &val); + if (!ret) { + val &= ~clear; + val |= set; + ret = ub_cap_write_word(dev, cap, off, val); + } + + return ret; +} + +int ub_cap_clear_and_set_dword(struct ub_entity *dev, u32 cap, u32 off, + u32 clear, u32 set) +{ + u32 val; + int ret; + + ret = ub_cap_read_dword(dev, cap, off, &val); + if (!ret) { + val &= ~clear; + val |= set; + ret = ub_cap_write_dword(dev, cap, off, val); + } + + return ret; +} + +static inline int ub_cap_set_word(struct ub_entity *dev, u32 cap, u32 off, + u16 set) +{ + return ub_cap_clear_and_set_word(dev, cap, off, 0, set); +} + +static inline int ub_cap_set_dword(struct ub_entity *dev, u32 cap, u32 off, + u32 set) +{ + return ub_cap_clear_and_set_dword(dev, cap, off, 0, set); +} + +static inline int ub_cap_clear_word(struct ub_entity *dev, u32 cap, u32 off, + u16 clear) +{ + return ub_cap_clear_and_set_word(dev, cap, off, clear, 0); +} + +static inline int ub_cap_clear_dword(struct ub_entity *dev, u32 cap, u32 off, + u32 clear) +{ + return ub_cap_clear_and_set_dword(dev, cap, off, clear, 0); +} + +static int ub_sf_init(struct ub_entity *uent) +{ + u32 support_feature = 0; + int ret; + + ret = ub_cfg_read_dword(uent, UB_FEATURE_SUPPORT_0, &support_feature); + if (ret) { + ub_err(uent, "Read support feature0 failed, ret=%d\n", ret); + return ret; + } + + uent->support_feature = support_feature; + return 0; +} + +static void ub_sw_init(struct ub_entity *uent) +{ + if (!uent || !uent->ubc) + return; + + if (!is_ibus_controller(uent)) + return; + + if (uent->support_feature & UB_SW_SUPPORT) + uent->sw_cap = true; + else + uent->sw_cap = false; +} + +void ub_init_capabilities(struct ub_entity *uent) +{ + /* cfg0 caps */ + if (ub_sf_init(uent)) + goto init_cfg1_cap; + + ub_sw_init(uent); + +init_cfg1_cap: + /* cfg1 caps */ + + uent->reset_fn = 1; +} + +void ub_uninit_capabilities(struct ub_entity *uent) +{ + /* cfg1 cap */ + + uent->reset_fn = 0; +} diff --git a/drivers/ub/ubus/cap.h b/drivers/ub/ubus/cap.h new file mode 100644 index 000000000000..11507c646017 --- /dev/null +++ b/drivers/ub/ubus/cap.h @@ -0,0 +1,14 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Copyright (c) HiSilicon Technologies Co., Ltd. 2025. All rights reserved. + */ + +#ifndef __CAP_H__ +#define __CAP_H__ + +void ub_set_cap_bitmap(struct ub_entity *uent); +u32 ub_find_capability(u32 cap); +void ub_init_capabilities(struct ub_entity *uent); +void ub_uninit_capabilities(struct ub_entity *uent); + +#endif /* __CAP_H__ */ diff --git a/drivers/ub/ubus/ubus_entity.c b/drivers/ub/ubus/ubus_entity.c index 932c21a49615..57bff57ff096 100644 --- a/drivers/ub/ubus/ubus_entity.c +++ b/drivers/ub/ubus/ubus_entity.c @@ -19,6 +19,7 @@ #include "ubus_controller.h" #include "ubus_driver.h" #include "ubus_inner.h" +#include "cap.h" /* * Entity lifecycle @@ -287,6 +288,7 @@ int ub_setup_ent(struct ub_entity *uent) goto free_uent_num; ub_config_eid(uent); + ub_set_cap_bitmap(uent); ub_set_fm_info(uent); ub_get_module_id(uent); @@ -336,6 +338,7 @@ void ub_entity_add(struct ub_entity *uent, void *ctx) dma_set_seg_boundary(&uent->dev, GENMASK(31, 0)); uent->state_saved = false; + ub_init_capabilities(uent); if (is_primary(uent) || is_p_device(uent)) { ubc = (struct ub_bus_controller *)ctx; @@ -451,6 +454,7 @@ void ub_remove_ent(struct ub_entity *uent) list_del(&uent->node); up_write(&ub_bus_sem); + ub_uninit_capabilities(uent); ub_unconfigure_ent(uent); ub_entity_unset_mmio(uent); ub_entity_num_free(uent); diff --git a/include/ub/ubus/ubus.h b/include/ub/ubus/ubus.h index 8bc0ecc57f2b..67f0d44c52d0 100644 --- a/include/ub/ubus/ubus.h +++ b/include/ub/ubus/ubus.h @@ -175,6 +175,10 @@ struct ub_entity { u16 port_nums; struct ub_port *ports; + /* entity capability info */ + unsigned int cfg0_bitmap[8]; + unsigned int cfg1_bitmap[8]; + unsigned int state_saved : 1; /* entity DMA info */ @@ -192,6 +196,8 @@ struct ub_entity { /* UB entity TID */ u32 tid; + bool sw_cap; + /* UB saved config space */ u32 saved_config_space[24]; /* Config space saved at reset time */ -- Gitee From 017b57135863a3a16963ac38453e07e6a065ea69 Mon Sep 17 00:00:00 2001 From: Jianquan Lin Date: Sat, 20 Sep 2025 09:53:18 +0800 Subject: [PATCH 30/45] ub:ubus: Support UBUS Interrupt framework driver inclusion category: feature bugzilla: https://gitee.com/openeuler/kernel/issues/ID34DG CVE: NA ----------------------------------------------------------- Support UBUS Interrupt framework, add basic data structure of UBUS USI and create message signal interrupts domain. Signed-off-by: Jianquan Lin --- drivers/irqchip/Kconfig | 7 ++ drivers/irqchip/Makefile | 1 + drivers/irqchip/irq-gic-v3-its-ub-msi.c | 156 ++++++++++++++++++++++++ drivers/ub/ubus/Kconfig | 12 ++ drivers/ub/ubus/Makefile | 1 + drivers/ub/ubus/msi/Makefile | 3 + drivers/ub/ubus/msi/irqdomain.c | 114 +++++++++++++++++ drivers/ub/ubus/msi/msi.c | 155 +++++++++++++++++++++++ include/linux/irqdomain_defs.h | 3 + include/linux/msi.h | 38 ++++++ include/ub/ubus/ubus.h | 10 ++ kernel/irq/msi.c | 15 ++- 12 files changed, 511 insertions(+), 4 deletions(-) create mode 100644 drivers/irqchip/irq-gic-v3-its-ub-msi.c create mode 100644 drivers/ub/ubus/msi/Makefile create mode 100644 drivers/ub/ubus/msi/irqdomain.c create mode 100644 drivers/ub/ubus/msi/msi.c diff --git a/drivers/irqchip/Kconfig b/drivers/irqchip/Kconfig index 6edafab595e6..a15fbf9fa85f 100644 --- a/drivers/irqchip/Kconfig +++ b/drivers/irqchip/Kconfig @@ -97,6 +97,13 @@ config ARM_GIC_V3_ITS_PCI depends on PCI_MSI default ARM_GIC_V3_ITS +config ARM_GIC_V3_ITS_UBUS + bool + depends on ARM_GIC_V3_ITS + depends on UB_UBUS + depends on UB_UBUS_USI + default ARM_GIC_V3_ITS + config ARM_GIC_V3_ITS_FSL_MC bool depends on ARM_GIC_V3_ITS diff --git a/drivers/irqchip/Makefile b/drivers/irqchip/Makefile index f4697c1a39c0..d8778ff3f260 100644 --- a/drivers/irqchip/Makefile +++ b/drivers/irqchip/Makefile @@ -40,6 +40,7 @@ obj-$(CONFIG_ARM_GIC_V2M) += irq-gic-v2m.o obj-$(CONFIG_ARM_GIC_V3) += irq-gic-v3.o irq-gic-v3-mbi.o irq-gic-common.o obj-$(CONFIG_ARM_GIC_V3_ITS) += irq-gic-v3-its.o irq-gic-v3-its-platform-msi.o irq-gic-v4.o obj-$(CONFIG_ARM_GIC_V3_ITS_PCI) += irq-gic-v3-its-pci-msi.o +obj-$(CONFIG_ARM_GIC_V3_ITS_UBUS) += irq-gic-v3-its-ub-msi.o obj-$(CONFIG_ARM_GIC_V3_ITS_FSL_MC) += irq-gic-v3-its-fsl-mc-msi.o obj-$(CONFIG_ARM_GIC_PHYTIUM_2500) += irq-gic-phytium-2500.o irq-gic-phytium-2500-its.o obj-$(CONFIG_PARTITION_PERCPU) += irq-partition-percpu.o diff --git a/drivers/irqchip/irq-gic-v3-its-ub-msi.c b/drivers/irqchip/irq-gic-v3-its-ub-msi.c new file mode 100644 index 000000000000..4caccd12fdc4 --- /dev/null +++ b/drivers/irqchip/irq-gic-v3-its-ub-msi.c @@ -0,0 +1,156 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (c) HiSilicon Technologies Co., Ltd. 2025. All rights reserved. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static void its_mask_msi_irq(struct irq_data *d) +{ + ub_msi_mask_irq(d); + irq_chip_mask_parent(d); +} + +static void its_unmask_msi_irq(struct irq_data *d) +{ + ub_msi_unmask_irq(d); + irq_chip_unmask_parent(d); +} + +static struct irq_chip its_msi_irq_chip = { + .name = "ITS-MSI", + .irq_unmask = its_unmask_msi_irq, + .irq_mask = its_mask_msi_irq, + .irq_eoi = irq_chip_eoi_parent, +}; + +static int its_ub_msi_prepare(struct irq_domain *domain, struct device *dev, + int nvec, msi_alloc_info_t *info) +{ + struct msi_domain_info *msi_info; + int alias_count = 0, minnvec = 1; + struct ub_entity *uent = to_ub_entity(dev); + int cnt, ret; + + msi_info = msi_get_domain_info(domain->parent); + + ret = ub_interrupt_id_alloc(uent); + if (ret) { + dev_err(&uent->dev, "device id alloc failed, ret = %d.\n", ret); + return ret; + } + + info->scratchpad[0].ul = uent->intr_device_id; + dev_info(&uent->dev, "device id alloc success, id: %u.\n", uent->intr_device_id); + ub_write_interruptid(uent); + + cnt = max(nvec, alias_count); + cnt = max_t(int, minnvec, roundup_pow_of_two((u32)cnt)); + + return msi_info->ops->msi_prepare(domain->parent, dev, cnt, info); +} + +static struct msi_domain_ops its_ub_msi_ops = { + .msi_prepare = its_ub_msi_prepare, +}; + +static struct msi_domain_info its_ub_msi_domain_info = { + .flags = (MSI_FLAG_USE_DEF_DOM_OPS | MSI_FLAG_USE_DEF_CHIP_OPS | + MSI_FLAG_UB_INTR), + .ops = &its_ub_msi_ops, + .chip = &its_msi_irq_chip, +}; + +static int its_ub_msi_init_one(struct fwnode_handle *handle, + const char *name) +{ + struct irq_domain *parent; + + parent = irq_find_matching_fwnode(handle, DOMAIN_BUS_NEXUS); + if (!parent || !msi_get_domain_info(parent)) { + pr_err("%s: Unable to locate ITS domain\n", name); + return -ENXIO; + } + + if (!ub_msi_create_irq_domain(handle, &its_ub_msi_domain_info, parent)) { + pr_err("%s: Unable to create UB domain\n", name); + return -ENOMEM; + } + + return 0; +} + +static int its_ub_msi_parse_madt(union acpi_subtable_headers *header, + const unsigned long end) +{ + struct acpi_madt_generic_translator *its_entry; + struct fwnode_handle *dom_handle; + char *node_name; + int err = -ENXIO; + + its_entry = (struct acpi_madt_generic_translator *)header; + node_name = kasprintf(GFP_KERNEL, "ITS@0x%lx", + (long)its_entry->base_address); + + dom_handle = iort_find_domain_token(its_entry->translation_id); + if (!dom_handle) { + pr_err("%s: Unable to locate ITS domain handle\n", node_name); + goto out; + } + + err = its_ub_msi_init_one(dom_handle, (const char *)node_name); + if (!err) + pr_info("UB/USI: %s domain created\n", node_name); +out: + kfree(node_name); + return err; +} + +static int its_ub_acpi_msi_init(void) +{ + acpi_table_parse_madt(ACPI_MADT_TYPE_GENERIC_TRANSLATOR, + its_ub_msi_parse_madt, 0); + return 0; +} + +static const struct of_device_id its_device_id[] = { + { .compatible = "arm,gic-v3-its", }, + {}, +}; + +static int its_ub_of_msi_init(void) +{ + struct device_node *np; + + for (np = of_find_matching_node(NULL, its_device_id); np; + np = of_find_matching_node(np, its_device_id)) { + if (!of_device_is_available(np)) + continue; + if (!of_property_read_bool(np, "msi-controller")) + continue; + + if (its_ub_msi_init_one(of_node_to_fwnode(np), np->full_name)) + continue; + + pr_info("UB/USI: %pOF domain created\n", np); + } + + return 0; +} + +int __init its_ub_msi_init(void) +{ + its_ub_of_msi_init(); + its_ub_acpi_msi_init(); + + return 0; +} +early_initcall(its_ub_msi_init); diff --git a/drivers/ub/ubus/Kconfig b/drivers/ub/ubus/Kconfig index 059616926ff4..82e9f252b17f 100644 --- a/drivers/ub/ubus/Kconfig +++ b/drivers/ub/ubus/Kconfig @@ -26,4 +26,16 @@ config UB_UBUS_BUS the UB bus device management functionality. Say 'M' here unless you know what you are doing. +config UB_UBUS_USI + default n + bool "UBUS Message Signaled Interrupts (USI)" + depends on UB_UBUS + select GENERIC_MSI_IRQ + help + This allows device drivers to enable MSI (Message Signaled + Interrupts). Message Signaled Interrupts enable a device to + generate an interrupt using an inbound Memory Write on its + UnifiedBus instead of asserting a device IRQ pin.If you + don't know what to do here, say Y. + endif diff --git a/drivers/ub/ubus/Makefile b/drivers/ub/ubus/Makefile index 231f87ce48df..ac8ecfd3c0ce 100644 --- a/drivers/ub/ubus/Makefile +++ b/drivers/ub/ubus/Makefile @@ -1,6 +1,7 @@ # SPDX-License-Identifier: GPL-2.0+ obj-$(CONFIG_UB_UBUS) += ub-driver.o controller.o config.o entity.o +obj-$(CONFIG_UB_UBUS) += msi/ ubus-y := ubus_driver.o sysfs.o ubus_controller.o msg.o ubus_config.o port.o cc.o eid.o cna.o route.o ubus-y += enum.o resource.o ubus_entity.o reset.o cap.o diff --git a/drivers/ub/ubus/msi/Makefile b/drivers/ub/ubus/msi/Makefile new file mode 100644 index 000000000000..9fb340462fc6 --- /dev/null +++ b/drivers/ub/ubus/msi/Makefile @@ -0,0 +1,3 @@ +# SPDX-License-Identifier: GPL-2.0+ + +obj-$(CONFIG_UB_UBUS_USI) += irqdomain.o msi.o diff --git a/drivers/ub/ubus/msi/irqdomain.c b/drivers/ub/ubus/msi/irqdomain.c new file mode 100644 index 000000000000..5a01c40368bc --- /dev/null +++ b/drivers/ub/ubus/msi/irqdomain.c @@ -0,0 +1,114 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (c) HiSilicon Technologies Co., Ltd. 2025. All rights reserved. + */ + +#include +#include +#include +#include + +static irq_hw_number_t ub_msi_domain_calc_hwirq(struct msi_desc *desc) +{ + struct ub_entity *uent = msi_desc_to_ub_entity(desc); + + return (irq_hw_number_t)(desc->msi_index | (uent->intr_device_id << 11)); +} + +static void ub_msi_domain_set_desc(msi_alloc_info_t *arg, struct msi_desc *desc) +{ + arg->desc = desc; + arg->hwirq = ub_msi_domain_calc_hwirq(desc); + pr_info("%s, arg->hwirq: %lu.\n", __func__, arg->hwirq); +} + +static struct msi_domain_ops ub_msi_domain_ops_default = { + .set_desc = ub_msi_domain_set_desc, +}; + +static void ub_msi_domain_update_dom_ops(struct msi_domain_info *info) +{ + struct msi_domain_ops *ops = info->ops; + + if (ops == NULL) + info->ops = &ub_msi_domain_ops_default; + else if (!ops->set_desc) + ops->set_desc = ub_msi_domain_set_desc; +} + +static void ub_msi_domain_write_msg(struct irq_data *irq_data, struct msi_msg *msg) +{ + struct msi_desc *desc = irq_data_get_msi_desc(irq_data); + + if (desc->irq == irq_data->irq) + __ub_write_msi_msg(desc, msg); +} + +static void ub_msi_domain_update_chip_ops(struct msi_domain_info *info) +{ + struct irq_chip *chip = info->chip; + + WARN_ON_ONCE(!chip); + chip->irq_write_msi_msg = chip->irq_write_msi_msg ? + chip->irq_write_msi_msg : + ub_msi_domain_write_msg; + chip->irq_mask = chip->irq_mask ? chip->irq_mask : ub_msi_mask_irq; + chip->irq_unmask = chip->irq_unmask ? chip->irq_unmask : + ub_msi_unmask_irq; +} + +struct irq_domain *ub_msi_create_irq_domain(struct fwnode_handle *fwnode, + struct msi_domain_info *info, + struct irq_domain *parent) +{ + struct irq_domain *domain; + + if (info->flags & MSI_FLAG_USE_DEF_DOM_OPS) + ub_msi_domain_update_dom_ops(info); + if (info->flags & MSI_FLAG_USE_DEF_CHIP_OPS) + ub_msi_domain_update_chip_ops(info); + if (WARN_ON(info->flags & MSI_FLAG_LEVEL_CAPABLE)) + info->flags &= ~MSI_FLAG_LEVEL_CAPABLE; + + info->flags |= MSI_FLAG_FREE_MSI_DESCS | MSI_FLAG_ACTIVATE_EARLY | + MSI_FLAG_DEV_SYSFS; + + if (IS_ENABLED(CONFIG_GENERIC_IRQ_RESERVATION_MODE)) + info->flags |= MSI_FLAG_MUST_REACTIVATE; + + info->chip->flags |= IRQCHIP_ONESHOT_SAFE; + info->bus_token = DOMAIN_BUS_UB_MSI; + + domain = msi_create_irq_domain(fwnode, info, parent); + + return domain; +} +EXPORT_SYMBOL_GPL(ub_msi_create_irq_domain); + +static bool ub_create_device_domain(struct ub_entity *uent, + const struct msi_domain_template *tmpl, + unsigned int hwsize) +{ + struct irq_domain *domain = dev_get_msi_domain(&uent->dev); + + if (!domain || !irq_domain_is_msi_parent(domain)) + return true; + + if (WARN_ON_ONCE(1)) + pr_info("TODO: create device irq domain.\n"); + + return false; +} + +bool ub_setup_usi_device_domain(struct ub_entity *uent, unsigned int hwsize) +{ + if (WARN_ON_ONCE(uent->intr_enabled)) + return false; + + if (msi_match_device_irq_domain(&uent->dev, MSI_DEFAULT_DOMAIN, + DOMAIN_BUS_UB_MSI)) + return true; + + return ub_create_device_domain(uent, NULL, hwsize); +} +EXPORT_SYMBOL_GPL(ub_setup_usi_device_domain); diff --git a/drivers/ub/ubus/msi/msi.c b/drivers/ub/ubus/msi/msi.c new file mode 100644 index 000000000000..d0f3eb6e2731 --- /dev/null +++ b/drivers/ub/ubus/msi/msi.c @@ -0,0 +1,155 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Only support for irqdomain.c. + */ + +#include +#include +#include +#include +#include + +struct ub_device_id_manager { + struct idr device_id_idr; + spinlock_t lock; +}; + +static struct ub_device_id_manager ub_entity_idm = { + .device_id_idr = IDR_INIT(ub_entity_idm.device_id_idr), + .lock = __SPIN_LOCK_UNLOCKED(ub_entity_idm.lock), +}; + +int ub_interrupt_id_alloc(struct ub_entity *uent) +{ + int id; + + idr_preload(GFP_KERNEL); + spin_lock(&ub_entity_idm.lock); + id = idr_alloc(&ub_entity_idm.device_id_idr, uent, + uent->ubc->attr.int_id_start, uent->ubc->attr.int_id_end, + GFP_NOWAIT); + spin_unlock(&ub_entity_idm.lock); + idr_preload_end(); + + if (id < 0) + return id; + uent->intr_device_id = id; + + return 0; +} + +void ub_interrupt_id_free(struct ub_entity *uent) +{ + spin_lock(&ub_entity_idm.lock); + idr_remove(&ub_entity_idm.device_id_idr, uent->intr_device_id); + spin_unlock(&ub_entity_idm.lock); +} +EXPORT_SYMBOL_GPL(ub_interrupt_id_free); + +struct ub_entity *msi_desc_to_ub_entity(struct msi_desc *desc) +{ + return to_ub_entity(desc->dev); +} + +static void __iomem *ub_vector_desc_base_addr(struct msi_desc *desc) +{ + return desc->ub_intr.vector_base; +} + +void __iomem *ub_vector_desc_addr(struct msi_desc *desc) +{ + return desc->ub_intr.vector_base + (desc->ub_intr.intr_attrib.entry_nr * + UB_INTR_VECTOR_ENTRY_SIZE); +} + +static void __iomem *ub_addr_desc_base_addr(struct msi_desc *desc) +{ + return desc->ub_intr.addr_base; +} + +static void __iomem *ub_addr_desc_addr(struct msi_desc *desc) +{ + return desc->ub_intr.addr_base + (desc->ub_intr.intr_attrib.addr_index * + UB_INTR_ADDR_ENTRY_SIZE); +} + +static void ub_int_type1_update_mask(struct msi_desc *desc, u32 clear, u32 set) +{ + raw_spinlock_t *lock = &msi_desc_to_ub_entity(desc)->usi_lock; + unsigned long flags; + + raw_spin_lock_irqsave(lock, flags); + desc->ub_intr.intr_attrib.mask &= ~clear; + desc->ub_intr.intr_attrib.mask |= set; + ub_cfg_write_dword(msi_desc_to_ub_entity(desc), UB_INT_TYPE1_INT_MASK, + desc->ub_intr.intr_attrib.mask); + raw_spin_unlock_irqrestore(lock, flags); +} + +static void ub_int_type1_mask(struct msi_desc *desc, u32 mask) +{ + ub_int_type1_update_mask(desc, 0, mask); +} + +static void ub_int_type2_mask(struct msi_desc *desc) +{ + void __iomem *addr = ub_vector_desc_addr(desc); + u32 reg_val = readl(addr + UB_INTR_VECTOR_ADDR_INDEX) | UB_INTR_VECTOR_MASK_MASK; + + desc->ub_intr.intr_attrib.mask = 1; + writel(reg_val, addr + UB_INTR_VECTOR_ADDR_INDEX); +} + +static void ub_usi_desc_mask(struct msi_desc *desc, u32 nr) +{ + if (desc->ub_intr.intr_attrib.is_type1) + ub_int_type1_mask(desc, nr); + else + ub_int_type2_mask(desc); +} + +void ub_msi_mask_irq(struct irq_data *data) +{ + struct msi_desc *desc = irq_data_get_msi_desc(data); + + ub_usi_desc_mask(desc, (u32)BIT(data->irq - desc->irq)); +} + +static void ub_int_type1_unmask_irq(struct msi_desc *desc, u32 mask) +{ + ub_int_type1_update_mask(desc, mask, 0); +} + +static void ub_int_type2_unmask_irq(struct msi_desc *desc) +{ + void __iomem *addr = ub_vector_desc_addr(desc); + u32 reg_val = readl(addr + UB_INTR_VECTOR_ADDR_INDEX) & (~UB_INTR_VECTOR_MASK_MASK); + + desc->ub_intr.intr_attrib.mask = 0; + writel(reg_val, addr + UB_INTR_VECTOR_ADDR_INDEX); +} + +static void ub_usi_desc_unmask(struct msi_desc *desc, u32 nr) +{ + if (desc->ub_intr.intr_attrib.is_type1) + ub_int_type1_unmask_irq(desc, nr); + else + ub_int_type2_unmask_irq(desc); +} + +void ub_msi_unmask_irq(struct irq_data *data) +{ + struct msi_desc *desc = irq_data_get_msi_desc(data); + + ub_usi_desc_unmask(desc, (u32)BIT(data->irq - desc->irq)); +} + +void ub_write_interruptid(struct ub_entity *uent) +{ + if (!uent->intr_type1) + ub_cfg_write_dword(uent, UB_INT_ID, uent->intr_device_id); + else + ub_cfg_write_dword(uent, UB_INT_TYPE1_INT_ID, uent->intr_device_id); +} + +void __ub_write_msi_msg(struct msi_desc *entry, struct msi_msg *msg) {} diff --git a/include/linux/irqdomain_defs.h b/include/linux/irqdomain_defs.h index c29921fd8cd1..7a82107b0f3a 100644 --- a/include/linux/irqdomain_defs.h +++ b/include/linux/irqdomain_defs.h @@ -2,6 +2,8 @@ #ifndef _LINUX_IRQDOMAIN_DEFS_H #define _LINUX_IRQDOMAIN_DEFS_H +#include + /* * Should several domains have the same device node, but serve * different purposes (for example one domain is for PCI/MSI, and the @@ -26,6 +28,7 @@ enum irq_domain_bus_token { DOMAIN_BUS_DMAR, DOMAIN_BUS_AMDVI, DOMAIN_BUS_PCI_DEVICE_IMS, + KABI_EXTEND_ENUM(DOMAIN_BUS_UB_MSI) }; #endif /* _LINUX_IRQDOMAIN_DEFS_H */ diff --git a/include/linux/msi.h b/include/linux/msi.h index 5fd8a6caae98..7d1dde97fdd0 100644 --- a/include/linux/msi.h +++ b/include/linux/msi.h @@ -167,6 +167,19 @@ struct msi_desc_data { #define MSI_MAX_INDEX ((unsigned int)USHRT_MAX) +struct ub_intr_desc { + struct { + u32 mask; + u8 is_type1 : 1; + u16 entry_nr; + u16 addr_index; + } intr_attrib; + u16 addr_num; + u16 vec_num; + void __iomem *vector_base; + void __iomem *addr_base; +}; + /** * struct msi_desc - Descriptor structure for MSI based interrupts * @irq: The base interrupt number @@ -205,7 +218,11 @@ struct msi_desc { union { struct pci_msi_desc pci; struct msi_desc_data data; +#ifndef __GENKSYMS__ + struct ub_intr_desc ub_intr; +#else KABI_EXTEND_WITH_SIZE(KABI_RESERVE(1), 5) +#endif }; KABI_RESERVE(2) KABI_RESERVE(3) @@ -574,6 +591,7 @@ enum { MSI_FLAG_PCI_MSIX_ALLOC_DYN = (1 << 20), /* Support for PCI/IMS */ MSI_FLAG_PCI_IMS = (1 << 21), + KABI_EXTEND_ENUM(MSI_FLAG_UB_INTR = (1 << 22)) }; /** @@ -708,4 +726,24 @@ static inline struct irq_domain *pci_msi_get_device_domain(struct pci_dev *pdev) static inline void pci_write_msi_msg(unsigned int irq, struct msi_msg *msg) { } #endif /* !CONFIG_PCI_MSI */ +struct ub_usi_entry { + u32 vector; /* Kernel uses to write allocated vector */ + u16 entry; /* Driver uses to specify entry, OS writes */ +}; +struct ub_entity; +struct irq_affinity; +#ifdef CONFIG_UB_UBUS_USI +struct ub_entity *msi_desc_to_ub_entity(struct msi_desc *desc); +void ub_msi_mask_irq(struct irq_data *data); +void ub_msi_unmask_irq(struct irq_data *data); +void ub_write_interruptid(struct ub_entity *uent); +struct irq_domain *ub_msi_create_irq_domain(struct fwnode_handle *fwnode, + struct msi_domain_info *info, + struct irq_domain *parent); +void __ub_write_msi_msg(struct msi_desc *entry, struct msi_msg *msg); +int ub_interrupt_id_alloc(struct ub_entity *uent); +void ub_interrupt_id_free(struct ub_entity *uent); +bool ub_setup_usi_device_domain(struct ub_entity *uent, unsigned int hwsize); +#endif /* CONFIG_UB_UBUS_USI */ + #endif /* LINUX_MSI_H */ diff --git a/include/ub/ubus/ubus.h b/include/ub/ubus/ubus.h index 67f0d44c52d0..8b62f55bef9f 100644 --- a/include/ub/ubus/ubus.h +++ b/include/ub/ubus/ubus.h @@ -185,6 +185,16 @@ struct ub_entity { u64 dma_mask; struct device_dma_parameters dma_parms; + /* UB interrupt info */ + raw_spinlock_t usi_lock; + unsigned int no_intr : 1; + unsigned int intr_enabled : 1; + unsigned int intr_type1 : 1; + void __iomem *intr_addr_base; + void __iomem *intr_vector_base; + u32 intr_device_id; + const struct attribute_group **msi_irq_groups; + /* UB reset info */ unsigned int reset_fn : 1; diff --git a/kernel/irq/msi.c b/kernel/irq/msi.c index 5a4717a82024..038a05ae83dd 100644 --- a/kernel/irq/msi.c +++ b/kernel/irq/msi.c @@ -16,6 +16,7 @@ #include #include #include +#include #include "internals.h" @@ -142,6 +143,7 @@ int msi_domain_insert_msi_desc(struct device *dev, unsigned int domid, /* Copy type specific data to the new descriptor. */ desc->pci = init_desc->pci; + desc->ub_intr = init_desc->ub_intr; return msi_insert_desc(dev, desc, domid, init_desc->msi_index); } @@ -432,7 +434,7 @@ unsigned int msi_domain_get_virq(struct device *dev, unsigned int domid, unsigne { struct msi_desc *desc; unsigned int ret = 0; - bool pcimsi = false; + bool devmsi = false; struct xarray *xa; if (!dev->msi.data) @@ -443,18 +445,23 @@ unsigned int msi_domain_get_virq(struct device *dev, unsigned int domid, unsigne /* This check is only valid for the PCI default MSI domain */ if (dev_is_pci(dev) && domid == MSI_DEFAULT_DOMAIN) - pcimsi = to_pci_dev(dev)->msi_enabled; + devmsi = to_pci_dev(dev)->msi_enabled; +#ifdef CONFIG_UB_UBUS + /* This check is only valid for the UBUS default MSI domain */ + if (dev_is_ub(dev) && domid == MSI_DEFAULT_DOMAIN) + devmsi = to_ub_entity(dev)->intr_type1; +#endif msi_lock_descs(dev); xa = &dev->msi.data->__domains[domid].store; - desc = xa_load(xa, pcimsi ? 0 : index); + desc = xa_load(xa, devmsi ? 0 : index); if (desc && desc->irq) { /* * PCI-MSI has only one descriptor for multiple interrupts. * PCI-MSIX and platform MSI use a descriptor per * interrupt. */ - if (pcimsi) { + if (devmsi) { if (index < desc->nvec_used) ret = desc->irq + index; } else { -- Gitee From 561ebaa8501d143984f7ec4950954012eb0c23d4 Mon Sep 17 00:00:00 2001 From: Jianquan Lin Date: Wed, 24 Sep 2025 17:15:50 +0800 Subject: [PATCH 31/45] ub:ubus: Add UBUS MSI basic functions driver inclusion category: feature bugzilla: https://gitee.com/openeuler/kernel/issues/ID34DG CVE: NA ----------------------------------------------------------- Add UBUS MSI basic functions, including set-up interrupt, count interrupt number, write and set msi message and disable UBUS interrupts. Signed-off-by: Jianquan Lin --- drivers/ub/ubus/msi/msi.c | 398 +++++++++++++++++++++++++++++++++++++- include/linux/msi.h | 7 + include/ub/ubus/ubus.h | 36 ++++ 3 files changed, 440 insertions(+), 1 deletion(-) diff --git a/drivers/ub/ubus/msi/msi.c b/drivers/ub/ubus/msi/msi.c index d0f3eb6e2731..3ea005eb08ab 100644 --- a/drivers/ub/ubus/msi/msi.c +++ b/drivers/ub/ubus/msi/msi.c @@ -9,6 +9,14 @@ #include #include +unsigned int ub_irq_calc_affinity_vectors(unsigned int minvec, + unsigned int maxvec, + const struct irq_affinity *affd) +{ + return irq_calc_affinity_vectors(minvec, maxvec, affd); +} +EXPORT_SYMBOL_GPL(ub_irq_calc_affinity_vectors); + struct ub_device_id_manager { struct idr device_id_idr; spinlock_t lock; @@ -144,6 +152,169 @@ void ub_msi_unmask_irq(struct irq_data *data) ub_usi_desc_unmask(desc, (u32)BIT(data->irq - desc->irq)); } +static void __iomem *ub_intr_find_one_addr(struct msi_msg *msg, + struct msi_desc *entry, + u8 *is_new) +{ + void __iomem *first_unused_base = NULL; + void __iomem *base; + u32 addr_l, addr_h, valid, i; + u16 first_unused_id; + u32 addr_cnt = entry->ub_intr.addr_num + 1; + + base = ub_addr_desc_base_addr(entry); + + for (i = 0; i < addr_cnt; i++, base += UB_INTR_ADDR_ENTRY_SIZE) { + valid = readl(base + UB_INTR_ADDR_TOKENID) & UB_INTR_ADDR_VALID_MASK; + if (!valid) { + if (!first_unused_base) { + first_unused_base = base; + first_unused_id = i; + } + continue; + } + addr_l = readl(base + UB_INTR_ADDR_ADDR_L); + addr_h = readl(base + UB_INTR_ADDR_ADDR_H); + if (msg->address_lo == addr_l && msg->address_hi == addr_h) { + entry->ub_intr.intr_attrib.addr_index = i; + *is_new = 0; + return base; + } + } + + if (!first_unused_base) + return NULL; + *is_new = 1; + + entry->ub_intr.intr_attrib.addr_index = first_unused_id; + + return first_unused_base; +} + +static bool ub_intr_check_addr_used(struct msi_desc *entry) +{ + void __iomem *base, *vec_addr; + u16 addr_index; + u32 i, vec_cnt; + + base = ub_vector_desc_base_addr(entry); + vec_addr = ub_vector_desc_addr(entry); + vec_cnt = entry->ub_intr.vec_num + 1; + addr_index = entry->ub_intr.intr_attrib.addr_index; + + for (i = 0; i < vec_cnt; i++, base += UB_INTR_VECTOR_ENTRY_SIZE) { + if ((readl(base + UB_INTR_VECTOR_ADDR_INDEX) & UB_INTR_VECTOR_MASK_MASK) || + base == vec_addr) + continue; + + if ((readl(base + UB_INTR_VECTOR_ADDR_INDEX) & + UB_INTR_VECTOR_ADDR_INDEX_MASK) == addr_index) + return true; + } + + return false; +} + +static void ub_int_type1_clear_msg(struct msi_desc *entry, struct msi_msg *msg) +{ + struct ub_entity *uent = msi_desc_to_ub_entity(entry); + + ub_cfg_write_byte(uent, UB_INT_TYPE1_ENABLE, 0); + ub_cfg_write_dword(uent, UB_INT_TYPE1_EN_INT_NUM, 0); + ub_cfg_write_dword(uent, UB_INT_TYPE1_INT_DATA, 0); + ub_cfg_write_dword(uent, UB_INT_TYPE1_INT_ADDR_L, 0); + ub_cfg_write_dword(uent, UB_INT_TYPE1_INT_ADDR_H, 0); +} + +static void ub_int_type2_clear_msg(struct msi_desc *entry, struct msi_msg *msg) +{ + void __iomem *vector_base, *addr_base; + u32 reg_val; + + addr_base = ub_addr_desc_addr(entry); + vector_base = ub_vector_desc_addr(entry); + if (!vector_base || !addr_base) { + WARN_ON(1); + return; + } + + writel(0, vector_base + UB_INTR_VECTOR_ID); + reg_val = readl(vector_base + UB_INTR_VECTOR_ADDR_INDEX) & + ~UB_INTR_VECTOR_ADDR_INDEX_MASK; + writel(reg_val, vector_base + UB_INTR_VECTOR_ADDR_INDEX); + + if (!ub_intr_check_addr_used(entry)) { + writel(0, addr_base + UB_INTR_ADDR_TOKENID); + writel(0, addr_base + UB_INTR_ADDR_DSTEID_0); + writel(0, addr_base + UB_INTR_ADDR_ADDR_L); + writel(0, addr_base + UB_INTR_ADDR_ADDR_H); + } +} + +static void __ub_clear_msi_msg(struct msi_desc *entry, struct msi_msg *msg) +{ + if (entry->ub_intr.intr_attrib.is_type1) + ub_int_type1_clear_msg(entry, msg); + else + ub_int_type2_clear_msg(entry, msg); +} + +static void ub_int_type1_set_msg(struct msi_desc *entry, struct msi_msg *msg) +{ + struct ub_entity *uent = msi_desc_to_ub_entity(entry); + + ub_cfg_write_byte(uent, UB_INT_TYPE1_ENABLE, 1); + ub_cfg_write_dword(uent, UB_INT_TYPE1_EN_INT_NUM, + (u32)ilog2(__roundup_pow_of_two(entry->nvec_used))); + ub_cfg_write_dword(uent, UB_INT_TYPE1_INT_DATA, msg->data); + ub_cfg_write_dword(uent, UB_INT_TYPE1_INT_ADDR_L, msg->address_lo); + ub_cfg_write_dword(uent, UB_INT_TYPE1_INT_ADDR_H, msg->address_hi); +} + +static void ub_int_type2_set_msg(struct msi_desc *entry, struct msi_msg *msg) +{ + struct ub_entity *uent = msi_desc_to_ub_entity(entry); + void __iomem *vector_base, *addr_base; + unsigned long val; + u32 reg_val; + u8 is_new; + + vector_base = ub_vector_desc_addr(entry); + if (!vector_base) { + WARN_ON(1); + return; + } + + addr_base = ub_intr_find_one_addr(msg, entry, &is_new); + if (addr_base) { + writel(msg->data, vector_base + UB_INTR_VECTOR_ID); + reg_val = readl(vector_base + UB_INTR_VECTOR_ADDR_INDEX) & + ~UB_INTR_VECTOR_ADDR_INDEX_MASK; + reg_val |= entry->ub_intr.intr_attrib.addr_index; + writel(reg_val, vector_base + UB_INTR_VECTOR_ADDR_INDEX); + if (is_new) { + writel(msg->address_lo, addr_base + UB_INTR_ADDR_ADDR_L); + writel(msg->address_hi, addr_base + UB_INTR_ADDR_ADDR_H); + writel(ub_get_dst_eid(uent), addr_base + UB_INTR_ADDR_DSTEID_0); + val = uent->tid & UB_INTR_ADDR_TOKENID_MASK; + writel(val, addr_base + UB_INTR_ADDR_TOKENID); + /* valid bit must be placed at the end. */ + set_bit(UB_INTR_ADDR_VALID_BIT, &val); + writel(val, addr_base + UB_INTR_ADDR_TOKENID); + } + } else { + WARN_ON(1); + } +} + +static void __ub_set_msi_msg(struct msi_desc *entry, struct msi_msg *msg) +{ + if (entry->ub_intr.intr_attrib.is_type1) + ub_int_type1_set_msg(entry, msg); + else + ub_int_type2_set_msg(entry, msg); +} + void ub_write_interruptid(struct ub_entity *uent) { if (!uent->intr_type1) @@ -152,4 +323,229 @@ void ub_write_interruptid(struct ub_entity *uent) ub_cfg_write_dword(uent, UB_INT_TYPE1_INT_ID, uent->intr_device_id); } -void __ub_write_msi_msg(struct msi_desc *entry, struct msi_msg *msg) {} +void __ub_write_msi_msg(struct msi_desc *entry, struct msi_msg *msg) +{ + bool unmasked = false; + + if (!entry->ub_intr.intr_attrib.is_type1) + unmasked = !(entry->ub_intr.intr_attrib.mask); + + if (unmasked) + ub_usi_desc_mask(entry, 0); + + if (msg->address_lo == 0 && msg->address_hi == 0) + __ub_clear_msi_msg(entry, msg); + else + __ub_set_msi_msg(entry, msg); + + if (unmasked) + ub_usi_desc_unmask(entry, 0); + + entry->msg = *msg; + if (entry->write_msi_msg) + entry->write_msi_msg(entry, entry->write_msi_msg_data); +} + +u32 ub_intr_addr_count(struct ub_entity *uent) +{ + u32 addr_num = 0; + int ret; + + ret = ub_cfg_read_word(uent, UB_NUM_OF_INTR_ADDR_TBL, (u16 *)&addr_num); + if (ret) + return 0; + /* + * The value of this field is N, indicating that the interrupt address table + * supported by the function entity supports N+1 interrupt address. + */ + addr_num += 1; + + return addr_num; +} +EXPORT_SYMBOL_GPL(ub_intr_addr_count); + +u32 ub_intr_vec_count(struct ub_entity *uent) +{ + u32 vector_num = 0; + int ret; + + ret = ub_cfg_read_word(uent, UB_NUM_OF_INTR_VECTOR_TBL, (u16 *)&vector_num); + if (ret) + return 0; + /* + * The value of this field is N, indicating that the interrupt vector table + * supported by the function entity supports N+1 interrupt vectors. + */ + vector_num += 1; + + return vector_num; +} +EXPORT_SYMBOL_GPL(ub_intr_vec_count); + +static int ub_prepare_msi_desc(struct ub_entity *uent, struct msi_desc *desc) +{ + void __iomem *vec_desc_addr; + u32 addr_cnt, vec_cnt; + + addr_cnt = ub_intr_addr_count(uent); + if (addr_cnt == 0) + return -EINVAL; + + vec_cnt = ub_intr_vec_count(uent); + if (vec_cnt == 0) + return -EINVAL; + + desc->nvec_used = 1; + desc->ub_intr.intr_attrib.is_type1 = 0; + desc->ub_intr.addr_base = uent->intr_addr_base; + desc->ub_intr.vector_base = uent->intr_vector_base; + desc->ub_intr.addr_num = (u16)(addr_cnt - 1); + desc->ub_intr.vec_num = (u16)(vec_cnt - 1); + + vec_desc_addr = ub_vector_desc_addr(desc); + desc->ub_intr.intr_attrib.mask = !!(readl(vec_desc_addr + UB_INTR_VECTOR_ADDR_INDEX) & + UB_INTR_VECTOR_MASK_MASK); + + return 0; +} + +static int ub_setup_msi_descs(struct ub_entity *uent, + struct ub_usi_entry *entries, int nvec, + struct irq_affinity_desc *masks) +{ + struct irq_affinity_desc *curmsk; + struct msi_desc desc; + int ret = 0, i; + + memset(&desc, 0, sizeof(desc)); + + if (uent->intr_type1) { + desc.ub_intr.intr_attrib.is_type1 = 1; + desc.nvec_used = nvec; + desc.affinity = masks; + ret = ub_cfg_read_dword(uent, UB_INT_TYPE1_INT_MASK, + &desc.ub_intr.intr_attrib.mask); + if (ret) + return ret; + + return msi_insert_msi_desc(&uent->dev, &desc); + } + + for (i = 0, curmsk = masks; i < nvec; i++, curmsk++) { + desc.msi_index = entries ? entries[i].entry : i; + desc.ub_intr.intr_attrib.entry_nr = desc.msi_index; + desc.affinity = masks ? curmsk : NULL; + ret = ub_prepare_msi_desc(uent, &desc); + if (ret) + break; + ret = msi_insert_msi_desc(&uent->dev, &desc); + if (ret) + break; + } + return ret; +} + +static int ub_setup_intr_irqs(struct ub_entity *uent, int nvec) +{ + struct irq_domain *domain; + + domain = dev_get_msi_domain(&uent->dev); + if (domain && irq_domain_is_hierarchy(domain)) + return msi_domain_alloc_irqs_all_locked(&uent->dev, 0, nvec); + + pr_info("default irq alloc to be done...\n"); + + return -ENOSPC; +} + +static void ub_update_entries(struct ub_entity *uent, struct ub_usi_entry *entries) +{ + struct msi_desc *desc; + + if (entries) { + msi_for_each_desc(desc, &uent->dev, MSI_DESC_ALL) { + entries->vector = desc->irq; + entries++; + } + } +} + +static void ub_msi_teardown_msi_irqs(struct ub_entity *uent) +{ + struct irq_domain *domain; + + domain = dev_get_msi_domain(&uent->dev); + if (domain && irq_domain_is_hierarchy(domain)) + msi_domain_free_irqs_all_locked(&uent->dev, 0); + else + pr_info("default irq free to be done.\n"); +} + +static void free_msi_irqs(struct ub_entity *uent) +{ + ub_msi_teardown_msi_irqs(uent); + + if (uent->intr_addr_base) { + iounmap(uent->intr_addr_base); + uent->intr_addr_base = NULL; + } + if (uent->intr_vector_base) { + iounmap(uent->intr_vector_base); + uent->intr_vector_base = NULL; + } +} + +int usi_setup_interrupts(struct ub_entity *uent, struct ub_usi_entry *entries, + int nvec, struct irq_affinity *affd) +{ + struct irq_affinity_desc *masks = NULL; + int ret; + + if (affd) + masks = irq_create_affinity_masks(nvec, affd); + + msi_lock_descs(&uent->dev); + ret = ub_setup_msi_descs(uent, entries, nvec, masks); + if (ret) + goto out_free; + + ret = ub_setup_intr_irqs(uent, nvec); + if (ret) + goto out_free; + + ub_update_entries(uent, entries); + goto out_unlock; + +out_free: + free_msi_irqs(uent); +out_unlock: + msi_unlock_descs(&uent->dev); + kfree(masks); + return ret; +} +EXPORT_SYMBOL_GPL(usi_setup_interrupts); + +void ub_disable_intr(struct ub_entity *uent) +{ + if (!uent || !uent->intr_enabled) + return; + + if (uent->intr_type1) { + ub_cfg_write_byte(uent, UB_INT_TYPE1_ENABLE, 0); + ub_cfg_write_dword(uent, UB_INT_TYPE1_INT_MASK, GENMASK(31, 0)); + } else { + ub_cfg_write_byte(uent, UB_INT_MASK, 1); + ub_cfg_write_byte(uent, UB_INT_EN, 0); + } + + uent->intr_enabled = 0; + ub_interrupt_id_free(uent); + free_msi_irqs(uent); +} +EXPORT_SYMBOL_GPL(ub_disable_intr); + +int ub_setup_msi_context(struct ub_entity *uent) +{ + return msi_setup_device_data(&uent->dev); +} +EXPORT_SYMBOL_GPL(ub_setup_msi_context); diff --git a/include/linux/msi.h b/include/linux/msi.h index 7d1dde97fdd0..468319fac33d 100644 --- a/include/linux/msi.h +++ b/include/linux/msi.h @@ -744,6 +744,13 @@ void __ub_write_msi_msg(struct msi_desc *entry, struct msi_msg *msg); int ub_interrupt_id_alloc(struct ub_entity *uent); void ub_interrupt_id_free(struct ub_entity *uent); bool ub_setup_usi_device_domain(struct ub_entity *uent, unsigned int hwsize); +void __iomem *ub_vector_desc_addr(struct msi_desc *desc); +u32 ub_intr_addr_count(struct ub_entity *uent); +u32 ub_intr_vec_count(struct ub_entity *uent); +int usi_setup_interrupts(struct ub_entity *uent, + struct ub_usi_entry *entries, int nvec, + struct irq_affinity *affd); +int ub_setup_msi_context(struct ub_entity *uent); #endif /* CONFIG_UB_UBUS_USI */ #endif /* LINUX_MSI_H */ diff --git a/include/ub/ubus/ubus.h b/include/ub/ubus/ubus.h index 8b62f55bef9f..1cc4adec574a 100644 --- a/include/ub/ubus/ubus.h +++ b/include/ub/ubus/ubus.h @@ -469,6 +469,33 @@ int ub_reset_entity(struct ub_entity *ent); */ int ub_device_reset(struct ub_entity *ent); +/* Only for ubus module */ +unsigned int ub_irq_calc_affinity_vectors(unsigned int minvec, + unsigned int maxvec, + const struct irq_affinity *affd); + +/** + * ub_disable_intr() - Free entity interrupt vectors. + * @uent: UB entity. + * + * Free interrupt vectors of the device. + * + * Context: Any context. + */ +void ub_disable_intr(struct ub_entity *uent); + +/** + * ub_intr_vec_count() - Interrupt Vectors Supported by a entity. + * @uent: UB entity. + * + * Querying the Number of Interrupt Vectors Supported by a entity. + * For interrupt type 2. + * + * Context: Any context. + * Return: Number of Interrupts Supported if success, or 0 if failed. + */ +u32 ub_intr_vec_count(struct ub_entity *uent); + /** * ub_cfg_read_byte() - 1 byte configuration access read. * @uent: UB entity. @@ -656,6 +683,15 @@ static inline int ub_reset_entity(struct ub_entity *uent) { return -ENODEV; } static inline int ub_device_reset(struct ub_entity *uent) { return -ENODEV; } +static inline unsigned int +ub_irq_calc_affinity_vectors(unsigned int minvec, unsigned int maxvec, + const struct irq_affinity *affd) +{ + return maxvec; +} +static inline void ub_disable_intr(struct ub_entity *uent) {} +static inline u32 ub_intr_vec_count(struct ub_entity *uent) +{ return 0; } static inline int __ub_register_driver(struct ub_driver *drv, struct module *owner, const char *mod_name) -- Gitee From 6effa8887ed4654f8fb903da980b8dae3a483b88 Mon Sep 17 00:00:00 2001 From: Jianquan Lin Date: Sat, 20 Sep 2025 10:53:37 +0800 Subject: [PATCH 32/45] ub:ubus: Add MSI capability for UBUS driver driver inclusion category: feature bugzilla: https://gitee.com/openeuler/kernel/issues/ID34DG CVE: NA ----------------------------------------------------------- Add MSI capability for UBUS driver, providing type1 and type2 interrupts. Type1 interrupt is mainly used for ub bus controller, and type2 interrupt is used for ub device except ub bus controller. Finally, export some interfaces UBUS USI for UB kernel. Signed-off-by: Jianquan Lin --- drivers/ub/ubus/Makefile | 2 +- drivers/ub/ubus/cap.c | 2 + drivers/ub/ubus/interrupt.c | 316 ++++++++++++++++++++++++++++++++++ drivers/ub/ubus/interrupt.h | 11 ++ drivers/ub/ubus/ubus_entity.c | 2 + include/ub/ubus/ubus.h | 87 ++++++++++ 6 files changed, 419 insertions(+), 1 deletion(-) create mode 100644 drivers/ub/ubus/interrupt.c create mode 100644 drivers/ub/ubus/interrupt.h diff --git a/drivers/ub/ubus/Makefile b/drivers/ub/ubus/Makefile index ac8ecfd3c0ce..d90034f0003f 100644 --- a/drivers/ub/ubus/Makefile +++ b/drivers/ub/ubus/Makefile @@ -4,6 +4,6 @@ obj-$(CONFIG_UB_UBUS) += ub-driver.o controller.o config.o entity.o obj-$(CONFIG_UB_UBUS) += msi/ ubus-y := ubus_driver.o sysfs.o ubus_controller.o msg.o ubus_config.o port.o cc.o eid.o cna.o route.o -ubus-y += enum.o resource.o ubus_entity.o reset.o cap.o +ubus-y += enum.o resource.o ubus_entity.o reset.o cap.o interrupt.o obj-$(CONFIG_UB_UBUS_BUS) += ubus.o diff --git a/drivers/ub/ubus/cap.c b/drivers/ub/ubus/cap.c index 03f719422a2a..a9073de22fbe 100644 --- a/drivers/ub/ubus/cap.c +++ b/drivers/ub/ubus/cap.c @@ -6,6 +6,7 @@ #define pr_fmt(fmt) "ubus cap: " fmt #include "ubus.h" +#include "interrupt.h" #define DW_CHECK 3 @@ -231,6 +232,7 @@ void ub_init_capabilities(struct ub_entity *uent) init_cfg1_cap: /* cfg1 caps */ + ub_intr_init(uent); uent->reset_fn = 1; } diff --git a/drivers/ub/ubus/interrupt.c b/drivers/ub/ubus/interrupt.c new file mode 100644 index 000000000000..d1ca667250fe --- /dev/null +++ b/drivers/ub/ubus/interrupt.c @@ -0,0 +1,316 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (c) HiSilicon Technologies Co., Ltd. 2025. All rights reserved. + */ + +#define pr_fmt(fmt) "ubus interrupt: " fmt + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "ubus.h" + +enum ub_intr_tbl_type { + UB_INTR_VECTOR_TBL, + UB_INTR_ADDR_TBL +}; + +struct ub_intr_vector_tbl { + unsigned int vector; + unsigned int addr_index : 16; + unsigned int mask : 1; + unsigned int reserved : 15; +}; + +struct ub_intr_addr_tbl { + unsigned int intr_addr_l; + unsigned int intr_addr_h; + unsigned int token_id : 20; + unsigned int valid : 1; + unsigned int reserved0 : 11; + unsigned int dst_eid_0; + unsigned int dst_eid_1; + unsigned int dst_eid_2; + unsigned int dst_eid_3; +}; + +void ub_intr_init(struct ub_entity *uent) +{ + if (uent->cfg1_bitmap[0] & UB_INT_TYPE2_CAP_BIT) + uent->intr_type1 = 0; + else if (uent->cfg1_bitmap[0] & UB_INT_TYPE1_CAP_BIT) + uent->intr_type1 = 1; + else + uent->no_intr = 1; +} + +u32 ub_int_type1_vec_count(struct ub_entity *uent) +{ + u16 vector_num = 0; + int ret; + + ret = ub_cfg_read_word(uent, UB_INT_TYPE1_SUP_INT_NUM, &vector_num); + if (ret) + return 0; + + /* Mapping between the number of bitmaps and vectors defined in the protocol */ + vector_num = 1 << vector_num; + + return vector_num; +} +EXPORT_SYMBOL_GPL(ub_int_type1_vec_count); + +static void __iomem *ub_intr_map_region(struct ub_entity *uent, + enum ub_intr_tbl_type type, int nr_entries) +{ + u32 addr_reg_l, addr_reg_h, offset_l, offset_h; + resource_size_t tb_phys_addr; + unsigned long flags, sz; + u64 offset; + int ret; + + if (nr_entries <= 0) + return NULL; + if (type == UB_INTR_VECTOR_TBL) { + addr_reg_l = UB_INT_VECTOR_TBL_SA_L; + addr_reg_h = UB_INT_VECTOR_TBL_SA_H; + sz = (unsigned long)(nr_entries * UB_INTR_VECTOR_ENTRY_SIZE); + } else { + addr_reg_l = UB_INT_ADDR_TBL_SA_L; + addr_reg_h = UB_INT_ADDR_TBL_SA_H; + sz = (unsigned long)(nr_entries * UB_INTR_ADDR_ENTRY_SIZE); + } + + ret = ub_cfg_read_dword(uent, addr_reg_l, &offset_l); + ret |= ub_cfg_read_dword(uent, addr_reg_h, &offset_h); + if (ret) { + ub_err(uent, "read addr register failed, ret=%d\n", ret); + return NULL; + } + + offset = ((u64)offset_h << 32) | offset_l; + + flags = ub_resource_flags(uent, 0); + if (!flags || (flags & IORESOURCE_UNSET)) + return NULL; + + if (offset + sz > ub_resource_len(uent, 0)) { + ub_err(uent, "UB interrupt table(type:%u) off:%#llx + sz:%#lx is out of range!\n", + type, offset, sz); + return NULL; + } + tb_phys_addr = ub_resource_start(uent, 0) + offset; + + return ioremap(tb_phys_addr, sz); +} + +static int int_type1_capability_init(struct ub_entity *uent, + struct ub_usi_entry *entries, int nvec, + struct irq_affinity *affd) +{ + int ret; + + uent->intr_enabled = 1; + + ret = usi_setup_interrupts(uent, entries, nvec, affd); + if (ret) + goto out_disable; + + ub_cfg_write_byte(uent, UB_INT_TYPE1_ENABLE, 1); + return 0; + +out_disable: + uent->intr_enabled = 0; + + return ret; +} + +static int int_type2_capability_init(struct ub_entity *uent, + struct ub_usi_entry *entries, int nvec, + struct irq_affinity *affd) +{ + void __iomem *vector_base, *addr_base; + int vector_num, addr_num; + int ret; + + uent->intr_enabled = 1; + + vector_num = (int)ub_intr_vec_count(uent); + /* Request & Map UB interrupt table region */ + vector_base = ub_intr_map_region(uent, UB_INTR_VECTOR_TBL, vector_num); + if (!vector_base) { + ret = -ENOMEM; + goto out_disable; + } + addr_num = (int)ub_intr_addr_count(uent); + addr_base = ub_intr_map_region(uent, UB_INTR_ADDR_TBL, addr_num); + if (!addr_base) { + iounmap(vector_base); + ret = -ENOMEM; + goto out_disable; + } + uent->intr_vector_base = vector_base; + uent->intr_addr_base = addr_base; + + ret = usi_setup_interrupts(uent, entries, nvec, affd); + if (ret) + goto out_disable; + + ub_cfg_write_byte(uent, UB_INT_MASK, 0); + ub_cfg_write_byte(uent, UB_INT_EN, 1); + + return 0; + +out_disable: + uent->intr_enabled = 0; + + return ret; +} + +static int ub_msi_supported(struct ub_entity *uent, int nvec) +{ + if (!uent || uent->no_intr || nvec < 1) + return 0; + + return 1; +} + +static int ub_usi_entry_invalid_check(struct ub_usi_entry *ents, + int nvec, int nr_ents) +{ + int i, j; + + if (!ents) + return 0; + + for (i = 0; i < nvec; i++) { + if (ents[i].entry >= nr_ents) + return -EINVAL; + + for (j = i + 1; j < nvec; j++) + if (ents[i].entry == ents[j].entry) + return -EINVAL; + } + + return 0; +} + +static int __ub_enable_usi_range(struct ub_entity *uent, + struct ub_usi_entry *entries, int minvec, + int maxvec, struct irq_affinity *affd) +{ + int hwsize, nvec = maxvec; + int ret; + + if (maxvec < minvec) + return -ERANGE; + + if (!ub_msi_supported(uent, nvec)) + return -EINVAL; + + if (uent->intr_type1) + hwsize = (int)ub_int_type1_vec_count(uent); + else + hwsize = (int)ub_intr_vec_count(uent); + if (hwsize == 0) { + ub_err(uent, "int vector cnt is zero.\n"); + return -ENOSPC; + } + + ret = ub_usi_entry_invalid_check(entries, nvec, hwsize); + if (ret) + return ret; + + if (hwsize < nvec) + nvec = hwsize; + if (nvec < minvec) + return -ENOSPC; + + ret = ub_setup_msi_context(uent); + if (ret) + return ret; + + if (!ub_setup_usi_device_domain(uent, hwsize)) { + ub_err(uent, "ub setup device domain failed.\n"); + return -ENODEV; + } + + if (affd) { + nvec = ub_irq_calc_affinity_vectors(minvec, nvec, affd); + if (nvec < minvec) { + ub_err(uent, "irq calc affd failed.\n"); + return -ENOSPC; + } + } + + if (uent->intr_type1) + ret = int_type1_capability_init(uent, entries, nvec, affd); + else + ret = int_type2_capability_init(uent, entries, nvec, affd); + if (ret) + return ret; + + return nvec; +} + +int ub_alloc_irq_vectors_affinity(struct ub_entity *uent, unsigned int min_vecs, + unsigned int max_vecs, unsigned int flags, + struct irq_affinity *affd) +{ + struct irq_affinity msi_default_affd = {}; + + if (flags & UB_IRQ_AFFINITY) { + if (!affd) + affd = &msi_default_affd; + } else { + if (WARN_ON(affd)) + affd = NULL; + } + + return __ub_enable_usi_range(uent, NULL, min_vecs, max_vecs, affd); +} +EXPORT_SYMBOL_GPL(ub_alloc_irq_vectors_affinity); + +int ub_irq_vector(struct ub_entity *uent, unsigned int nr) +{ + unsigned int irq; + + if (!uent) + return -EINVAL; + + if (uent->intr_enabled) { + irq = msi_get_virq(&uent->dev, nr); + return irq ? (int)irq : -EINVAL; + } + + return -EINVAL; +} +EXPORT_SYMBOL_GPL(ub_irq_vector); + +const struct cpumask *ub_irq_get_affinity(struct ub_entity *dev, int nr) +{ + int idx, irq = ub_irq_vector(dev, nr); + struct msi_desc *desc; + + if (WARN_ON_ONCE(irq <= 0)) + return NULL; + + desc = irq_get_msi_desc(irq); + /* Interrupts can be allocated without affinity descriptor */ + if (!desc->affinity) + return NULL; + + /* + * INT_TYPE1 has a mask array in the descriptor. + * INT_TYPE2 has a single mask. + */ + idx = dev->intr_type1 ? nr : 0; + return &desc->affinity[idx].mask; +} +EXPORT_SYMBOL_GPL(ub_irq_get_affinity); diff --git a/drivers/ub/ubus/interrupt.h b/drivers/ub/ubus/interrupt.h new file mode 100644 index 000000000000..9e7a5173793a --- /dev/null +++ b/drivers/ub/ubus/interrupt.h @@ -0,0 +1,11 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Copyright (c) HiSilicon Technologies Co., Ltd. 2025. All rights reserved. + */ + +#ifndef __INTERRUPT_H__ +#define __INTERRUPT_H__ + +void ub_intr_init(struct ub_entity *uent); + +#endif /* __INTERRUPT_H__ */ diff --git a/drivers/ub/ubus/ubus_entity.c b/drivers/ub/ubus/ubus_entity.c index 57bff57ff096..3f3acb176f10 100644 --- a/drivers/ub/ubus/ubus_entity.c +++ b/drivers/ub/ubus/ubus_entity.c @@ -357,6 +357,8 @@ void ub_entity_add(struct ub_entity *uent, void *ctx) list_add_tail(&uent->node, list); up_write(&ub_bus_sem); + dev_set_msi_domain(&uent->dev, uent->ubc->dev.msi.domain); + uent->match_driver = false; ret = device_add(&uent->dev); WARN_ON(ret < 0); diff --git a/include/ub/ubus/ubus.h b/include/ub/ubus/ubus.h index 1cc4adec574a..197d49f7c6e3 100644 --- a/include/ub/ubus/ubus.h +++ b/include/ub/ubus/ubus.h @@ -496,6 +496,78 @@ void ub_disable_intr(struct ub_entity *uent); */ u32 ub_intr_vec_count(struct ub_entity *uent); +/** + * ub_int_type1_vec_count() - Interrupt Vectors Supported by a entity. + * @uent: UB entity. + * + * Querying the Number of Interrupt Vectors Supported by a entity. + * For interrupt type 1. + * + * Context: Any context. + * Return: Number of Interrupts Supported if success, or 0 if failed. + */ +u32 ub_int_type1_vec_count(struct ub_entity *uent); + +#define UB_IRQ_AFFINITY (1 << 0) /* Auto-assign affinity */ +/** + * ub_alloc_irq_vectors_affinity() - Allocate multiple entity interrupt vectors. + * @uent: UB entity. + * @min_vecs: minimum required number of vectors (must be >= 1). + * @max_vecs: maximum desired number of vectors. + * + * @flags: allocation flags(can be 0): + * + * * %UB_IRQ_AFFINITY Auto-manage IRQs affinity by spreading + * the vectors around available CPUs + * + * @affd: affinity requirements (can be %NULL). + * + * Allocate interrupt vectors for the entity and set the affinity. + * + * Context: Any context. + * Return: the number of vectors allocated (which might be smaller than + * @max_vecs) if success, or a negative error code on error. If less than + * @min_vecs interrupt vectors are available for @uent the function will + * fail with -ENOSPC + */ +int ub_alloc_irq_vectors_affinity(struct ub_entity *uent, unsigned int min_vecs, + unsigned int max_vecs, unsigned int flags, + struct irq_affinity *affd); +static inline int ub_alloc_irq_vectors(struct ub_entity *uent, + unsigned int min_vecs, + unsigned int max_vecs) +{ + return ub_alloc_irq_vectors_affinity(uent, min_vecs, max_vecs, 0, NULL); +} + +/** + * ub_irq_vector() - Obtaining the Linux IRQ number. + * @uent: UB entity. + * @nr: Interrupt vector. + * + * Translate from Interrupt Vectors to Linux IRQ number. + * + * Context: Any context. + * Return: IRQ number if success, or %-EINVAL if failed. + */ +int ub_irq_vector(struct ub_entity *uent, unsigned int nr); + +/** + * ub_irq_get_affinity() - Get a entity interrupt vector affinity + * @uent: the UB entity to operate on + * @nr: entity-relative interrupt vector index (0-based); has different + * meanings, depending on interrupt mode: + * + * * INTR_TYPE2 the index in the USI vector table + * * INTR_TYPE1 the index of the enabled USI vectors + * + * Return: USI vector affinity, NULL if @nr is out of range or if + * the USI vector was allocated without explicit affinity + * requirements (e.g., by ub_alloc_irq_vectors(), or + * ub_alloc_irq_vectors_affinity() without the %UB_IRQ_AFFINITY flag). + */ +const struct cpumask *ub_irq_get_affinity(struct ub_entity *uent, int nr); + /** * ub_cfg_read_byte() - 1 byte configuration access read. * @uent: UB entity. @@ -692,6 +764,21 @@ ub_irq_calc_affinity_vectors(unsigned int minvec, unsigned int maxvec, static inline void ub_disable_intr(struct ub_entity *uent) {} static inline u32 ub_intr_vec_count(struct ub_entity *uent) { return 0; } +static inline u32 ub_int_type1_vec_count(struct ub_entity *uent) +{ return 0; } +static inline int +ub_alloc_irq_vectors_affinity(struct ub_entity *uent, unsigned int min_vecs, + unsigned int max_vecs, unsigned int flags, + struct irq_affinity *affd) +{ return -ENODEV; } +static inline int ub_alloc_irq_vectors(struct ub_entity *uent, + unsigned int min_vecs, + unsigned int max_vecs) +{ return -ENODEV; } +static inline int ub_irq_vector(struct ub_entity *uent, unsigned int nr) +{ return -EINVAL; } +static inline const struct cpumask * +ub_irq_get_affinity(struct ub_entity *uent, int nr) { return cpu_possible_mask; } static inline int __ub_register_driver(struct ub_driver *drv, struct module *owner, const char *mod_name) -- Gitee From d73e09b2edd93bedd57076edd94079a77b0ec627 Mon Sep 17 00:00:00 2001 From: Junlong Zheng Date: Sat, 20 Sep 2025 09:52:26 +0800 Subject: [PATCH 33/45] ub:ubus: Support for enabling and disabling ue driver inclusion category: feature bugzilla: https://gitee.com/openeuler/kernel/issues/ID34DG CVE: NA ----------------------------------------------------------- Implement dynamic creation and destruction of ue. Signed-off-by: Junlong Zheng --- drivers/ub/ubus/ubus_driver.c | 12 +++ drivers/ub/ubus/ubus_entity.c | 181 +++++++++++++++++++++++++++++++++- drivers/ub/ubus/ubus_entity.h | 3 + include/ub/ubus/ubus.h | 73 ++++++++++++++ 4 files changed, 268 insertions(+), 1 deletion(-) diff --git a/drivers/ub/ubus/ubus_driver.c b/drivers/ub/ubus/ubus_driver.c index 9e504806fd33..da0d5030ee5b 100644 --- a/drivers/ub/ubus/ubus_driver.c +++ b/drivers/ub/ubus/ubus_driver.c @@ -18,6 +18,11 @@ #include "ubus_config.h" #include "ubus_controller.h" #include "ubus_inner.h" +#include "ubus_entity.h" + +bool entity_flex_en; +module_param(entity_flex_en, bool, 0444); +MODULE_PARM_DESC(entity_flex_en, "Entity Flexible enable: default: 0"); DECLARE_RWSEM(ub_bus_sem); @@ -485,6 +490,11 @@ static struct attribute *ub_drv_attrs[] = { }; ATTRIBUTE_GROUPS(ub_drv); +static int ub_bus_num_ue(struct device *dev) +{ + return ub_num_ue(to_ub_entity(dev)); +} + void ub_bus_type_init(void) { ub_bus_type.match = ub_bus_match; @@ -495,6 +505,7 @@ void ub_bus_type_init(void) ub_bus_type.dev_groups = ub_entity_groups; ub_bus_type.bus_groups = ub_bus_groups; ub_bus_type.drv_groups = ub_drv_groups; + ub_bus_type.num_vf = ub_bus_num_ue; } void ub_bus_type_uninit(void) @@ -507,6 +518,7 @@ void ub_bus_type_uninit(void) ub_bus_type.dev_groups = NULL; ub_bus_type.bus_groups = NULL; ub_bus_type.drv_groups = NULL; + ub_bus_type.num_vf = NULL; } int ub_host_probe(void) diff --git a/drivers/ub/ubus/ubus_entity.c b/drivers/ub/ubus/ubus_entity.c index 3f3acb176f10..fc72b9a12587 100644 --- a/drivers/ub/ubus/ubus_entity.c +++ b/drivers/ub/ubus/ubus_entity.c @@ -6,6 +6,7 @@ #define pr_fmt(fmt) "ubus entity: " fmt #include +#include #include "ubus.h" #include "sysfs.h" @@ -20,6 +21,7 @@ #include "ubus_driver.h" #include "ubus_inner.h" #include "cap.h" +#include "ubus_entity.h" /* * Entity lifecycle @@ -43,6 +45,7 @@ struct ub_entity *ub_alloc_ent(void) INIT_LIST_HEAD(&uent->node); INIT_LIST_HEAD(&uent->mue_list); + INIT_LIST_HEAD(&uent->ue_list); INIT_LIST_HEAD(&uent->cna_list); uent->dev.type = &ub_dev_type; @@ -312,12 +315,22 @@ static void ub_unconfigure_ent(struct ub_entity *uent) { } +static int ub_ue_sort_by_ent_idx(void *priv, const struct list_head *a, + const struct list_head *b) +{ + struct ub_entity *uent_a = container_of(a, struct ub_entity, node); + struct ub_entity *uent_b = container_of(b, struct ub_entity, node); + + return uent_a->entity_idx > uent_b->entity_idx; +} + static void ub_release_ent(struct device *dev); void ub_entity_add(struct ub_entity *uent, void *ctx) { struct ub_bus_controller *ubc; struct list_head *list; struct ub_entity *pue; + u8 need_sort = 0; int ret, node; if (!uent || !ctx) @@ -344,10 +357,15 @@ void ub_entity_add(struct ub_entity *uent, void *ctx) ubc = (struct ub_bus_controller *)ctx; node = ub_ubc_to_node(ubc); list = &ubc->devs; - } else { + } else if (uent->is_mue) { pue = (struct ub_entity *)ctx; node = dev_to_node(&pue->dev); list = &pue->mue_list; + } else { + pue = (struct ub_entity *)ctx; + node = dev_to_node(&pue->dev); + list = &pue->ue_list; + need_sort = 1; } set_dev_node(&uent->dev, node); @@ -355,6 +373,8 @@ void ub_entity_add(struct ub_entity *uent, void *ctx) down_write(&ub_bus_sem); list_add_tail(&uent->node, list); + if (need_sort) + list_sort(NULL, list, ub_ue_sort_by_ent_idx); up_write(&ub_bus_sem); dev_set_msi_domain(&uent->dev, uent->ubc->dev.msi.domain); @@ -428,6 +448,9 @@ void ub_stop_ent(struct ub_entity *uent) return; ub_entity_assign_priv_flag(uent, UB_ENTITY_START, false); + /* Stop ue in mue, when uent is not mue, ue_list is NULL */ + list_for_each_entry_safe_reverse(ent, tmp, &uent->ue_list, node) + ub_stop_ent(ent); /* Stop mue in primary dev, when uent is entN, mue_list is NULL */ list_for_each_entry_safe_reverse(ent, tmp, &uent->mue_list, node) ub_stop_ent(ent); @@ -444,6 +467,9 @@ void ub_remove_ent(struct ub_entity *uent) if (!uent->dev.kobj.parent) return; + /* Remove ue in mue, when uent is not mue, ue_list is NULL */ + list_for_each_entry_safe_reverse(ent, tmp, &uent->ue_list, node) + ub_remove_ent(ent); /* Remove mue in primary dev, when uent is entN, mue_list is NULL */ list_for_each_entry_safe_reverse(ent, tmp, &uent->mue_list, node) ub_remove_ent(ent); @@ -698,3 +724,156 @@ static void ub_disable_mues(struct ub_entity *pue) list_for_each_entry_safe_reverse(mue, tmp, &pue->mue_list, node) ub_disable_ent(mue); } + +void ub_disable_ues(struct ub_entity *mue); +static int ub_enable_ues(struct ub_entity *mue, int nums) +{ + int ret; + int i; + + if (nums > mue->total_ues) + return -EINVAL; + + for (i = 0; i < nums; i++) { + ret = ub_enable_ent(mue, mue->uem.start_entity_idx + i, 0, + NULL); + if (ret) + goto failed; + } + mue->num_ues = nums; + return 0; +failed: + ub_disable_ues(mue); + return ret; +} + +void ub_disable_ues(struct ub_entity *mue) +{ + struct ub_entity *ue, *tmp; + u16 pool_ues = 0; + + list_for_each_entry_safe_reverse(ue, tmp, &mue->ue_list, + node) { + if (is_p_device(ue) || is_p_idevice(ue)) + pool_ues++; + else + ub_disable_ent(ue); + } + + mue->num_ues = pool_ues; +} + +static int ub_check_ue_para(struct ub_entity *pue, int entity_idx) +{ + if (!pue->is_mue || !pue->total_ues) { + ub_err(pue, "It's not mue or ues 0.\n"); + return -EINVAL; + } + + if (entity_idx < pue->uem.start_entity_idx || entity_idx > pue->uem.end_entity_idx) { + ub_err(pue, "Entity idx is err, start=%d, pre=%d, end=%d.\n", + pue->uem.start_entity_idx, entity_idx, pue->uem.end_entity_idx); + return -EINVAL; + } + + return 0; +} + +/* ub_enable_ue does not support parallel execution */ +int ub_enable_ue(struct ub_entity *pue, int entity_idx) +{ + struct ub_entity *ue; + int ret; + + if (!pue) + return -EINVAL; + + ret = ub_check_ue_para(pue, entity_idx); + if (ret) + return ret; + + list_for_each_entry(ue, &pue->ue_list, node) + if (ue->entity_idx == entity_idx) { + ub_err(ue, "entity_idx[%d] already exists, eid=%#05x\n", + entity_idx, ue->eid); + return -EEXIST; + } + + ret = ub_enable_ent(pue, entity_idx, 0, NULL); + if (ret) + return ret; + + pue->num_ues += 1; + + return ret; +} +EXPORT_SYMBOL_GPL(ub_enable_ue); + +int ub_disable_ue(struct ub_entity *pue, int entity_idx) +{ + struct ub_entity *vd_dev; + int ret; + + if (!pue) + return -EINVAL; + + ret = ub_check_ue_para(pue, entity_idx); + if (ret) + return ret; + + list_for_each_entry(vd_dev, &pue->ue_list, node) + if (vd_dev->entity_idx == entity_idx) { + ub_disable_ent(vd_dev); + pue->num_ues -= 1; + return 0; + } + + ub_err(pue, "No matching entity_idx[%d] found\n", entity_idx); + + return -ENODEV; +} +EXPORT_SYMBOL_GPL(ub_disable_ue); + +bool ub_get_entity_flex_en(void) +{ + return entity_flex_en; +} +EXPORT_SYMBOL_GPL(ub_get_entity_flex_en); + +int ub_enable_entities(struct ub_entity *uent, int nums) +{ + if (!uent) + return -EINVAL; + + if (!uent->is_mue) { + ub_err(uent, "It's not mue.\n"); + return -EINVAL; + } + + return ub_enable_ues(uent, nums); +} +EXPORT_SYMBOL_GPL(ub_enable_entities); + +void ub_disable_entities(struct ub_entity *uent) +{ + if (!uent) + return; + + if (!uent->is_mue) + return; + + ub_disable_ues(uent); +} +EXPORT_SYMBOL_GPL(ub_disable_entities); + +int ub_num_ue(struct ub_entity *uent) +{ + if (!uent) + return -EINVAL; + + if (!uent->is_mue) + return 0; + + return uent->num_ues; +} +EXPORT_SYMBOL_GPL(ub_num_ue); diff --git a/drivers/ub/ubus/ubus_entity.h b/drivers/ub/ubus/ubus_entity.h index d463269318fe..8f4c0fb4bcc5 100644 --- a/drivers/ub/ubus/ubus_entity.h +++ b/drivers/ub/ubus/ubus_entity.h @@ -6,6 +6,8 @@ #ifndef __UBUS_ENTITY_H__ #define __UBUS_ENTITY_H__ +extern bool entity_flex_en; + struct ub_entity *ub_alloc_ent(void); int ub_setup_ent(struct ub_entity *uent); void ub_entity_add(struct ub_entity *uent, void *ctx); @@ -13,6 +15,7 @@ void ub_start_ent(struct ub_entity *uent); void ub_remove_ent(struct ub_entity *uent); void ub_stop_entities(void); void ub_remove_entities(void); +int ub_num_ue(struct ub_entity *uent); void ub_disable_ent(struct ub_entity *uent); #endif /* __UBUS_ENTITY_H__ */ diff --git a/include/ub/ubus/ubus.h b/include/ub/ubus/ubus.h index 197d49f7c6e3..6cadca0bd0a2 100644 --- a/include/ub/ubus/ubus.h +++ b/include/ub/ubus/ubus.h @@ -164,6 +164,7 @@ struct ub_entity { u16 num_ues; struct ue_map uem; struct list_head mue_list; /* management ub entity list */ + struct list_head ue_list; /* entity list in management ub entity */ /* entity topology info */ struct list_head node; @@ -360,6 +361,69 @@ extern struct bus_type ub_bus_type; void ub_bus_type_iommu_ops_set(const struct iommu_ops *ops); const struct iommu_ops *ub_bus_type_iommu_ops_get(void); +/** + * ub_enable_entities() - Enable ues of mue in batches. + * @pue: UB mue. + * @nums: Number of enabled entities. + * + * Create ues in batches, initialize them, and add them to the system. + * + * Context: Any context. + * Return: 0 if success, or %-EINVAL if @pue type is not mue or nums over + * mue's total ue nums, or %-ENOMEM if the system is out of memory, + * or other failed negative values. + */ +int ub_enable_entities(struct ub_entity *pue, int nums); + +/** + * ub_disable_entities() - Disable ues of mue in batches. + * @pue: UB mue. + * + * Remove all enabled ues under the mue from the system. + * + * Context: Any context. + */ +void ub_disable_entities(struct ub_entity *pue); + +/** + * ub_enable_ue() - Enable a single ue. + * @pue: UB mue. + * @entity_idx: Number of the entity to be enabled. + * + * Create a specified ue under mue, initialize the ue, + * and add it to the system. + * + * Context: Any context. + * Return: 0 if success, or %-EINVAL if @pue type is not mue or @entity_idx + * is no longer in the ue range of mue, or %-EEXIST if entity has been + * enabled, or other failed negative values. + */ +int ub_enable_ue(struct ub_entity *pue, int entity_idx); + +/** + * ub_disable_ue() - Disable a single ue. + * @pue: UB mue. + * @entity_idx: Number of the entity to be disabled. + * + * Remove a specified ue. + * + * Context: Any context. + * Return: 0 if success, or %-EINVAL if @pue type is not mue or @entity_idx + * is no longer in the ue range of mue, or %-ENODEV if entity hasn't + * been enabled. + */ +int ub_disable_ue(struct ub_entity *pue, int entity_idx); + +/** + * ub_get_entity_flex_en() - Acquire the capability to enable flexible functionality. + * + * Return whether flexible enabling of entity is supported. + * + * Context: Any context. + * Return: true if support, or false if not support. + */ +bool ub_get_entity_flex_en(void); + /** * ub_get_dst_eid() - Obtain the Dest EID of the entity. * @uent: UB entity. @@ -710,6 +774,15 @@ void ub_stop_and_remove_ent(struct ub_entity *uent); static inline struct ub_entity *ub_entity_get(struct ub_entity *uent) { return NULL; } static inline void ub_entity_put(struct ub_entity *uent) {} +static inline int ub_enable_entities(struct ub_entity *pue, int nums) +{ return -ENODEV; } +static inline void ub_disable_entities(struct ub_entity *pue) {} +static inline int ub_enable_ue(struct ub_entity *pue, int entity_idx) +{ return -ENODEV; } +static inline int ub_disable_ue(struct ub_entity *pue, int entity_idx) +{ return -ENODEV; } +static inline bool ub_get_entity_flex_en(void) +{ return false; } static inline int ub_get_dst_eid(struct ub_entity *uent) { return -ENODEV; } static inline void __iomem * -- Gitee From 0f836cf40c9118c07e8da9509a26112130cef32f Mon Sep 17 00:00:00 2001 From: Junlong Zheng Date: Sat, 20 Sep 2025 10:23:15 +0800 Subject: [PATCH 34/45] ub:ubus: Support configuration of device information interface driver inclusion category: feature bugzilla: https://gitee.com/openeuler/kernel/issues/ID34DG CVE: NA ----------------------------------------------------------- Provide entity enablement, host information configuration, and device stop flow interface. Signed-off-by: Junlong Zheng --- drivers/ub/ubus/reset.c | 2 + drivers/ub/ubus/ubus.h | 1 + drivers/ub/ubus/ubus_controller.h | 4 +- drivers/ub/ubus/ubus_entity.c | 174 ++++++++++++++++++++++++++++++ include/ub/ubus/ubus.h | 69 ++++++++++++ 5 files changed, 249 insertions(+), 1 deletion(-) diff --git a/drivers/ub/ubus/reset.c b/drivers/ub/ubus/reset.c index 97cf00001d7a..34b2bbecee01 100644 --- a/drivers/ub/ubus/reset.c +++ b/drivers/ub/ubus/reset.c @@ -218,6 +218,8 @@ int ub_reset_entity(struct ub_entity *dev) return ret; } + ub_entity_enable(dev, 0); + rc = ub_elr(dev); ub_restore_state(dev); diff --git a/drivers/ub/ubus/ubus.h b/drivers/ub/ubus/ubus.h index b2463fccee7a..e25235f4003f 100644 --- a/drivers/ub/ubus/ubus.h +++ b/drivers/ub/ubus/ubus.h @@ -43,6 +43,7 @@ enum ub_entity_type { #define UB_ENTITY_DETACHED 1 /* Flag indicate uent is detached */ #define UB_ENTITY_ROUTE_UPDATED 2 /* Flag indicate uent's route is updated */ #define UB_ENTITY_SETUP 4 /* Flag indicate uent is setup */ +#define UB_ENTITY_ACTIVE 5 /* Flag indicate uent is in normal or disable state */ static inline void ub_entity_assign_priv_flag(struct ub_entity *uent, int bit, bool flag) diff --git a/drivers/ub/ubus/ubus_controller.h b/drivers/ub/ubus/ubus_controller.h index 532307abbff0..31a79840966d 100644 --- a/drivers/ub/ubus/ubus_controller.h +++ b/drivers/ub/ubus/ubus_controller.h @@ -7,7 +7,9 @@ #define __UBUS_CONTROLLER_H__ struct ub_bus_controller; -struct ub_bus_controller_ops {}; +struct ub_bus_controller_ops { + int (*entity_enable)(struct ub_entity *uent, u8 enable); +}; struct ub_bus_controller *ub_find_bus_controller_by_cna(u32 cna); void ub_bus_controllers_remove(void); diff --git a/drivers/ub/ubus/ubus_entity.c b/drivers/ub/ubus/ubus_entity.c index fc72b9a12587..c17db8decaae 100644 --- a/drivers/ub/ubus/ubus_entity.c +++ b/drivers/ub/ubus/ubus_entity.c @@ -877,3 +877,177 @@ int ub_num_ue(struct ub_entity *uent) return uent->num_ues; } EXPORT_SYMBOL_GPL(ub_num_ue); + +void ub_entity_enable(struct ub_entity *uent, u8 enable) +{ + int ret; + + if (!uent) + return; + + ub_cfg_write_byte(uent, UB_BUS_ACCESS_EN, enable); + ub_cfg_write_byte(uent, UB_ENTITY_RS_ACCESS_EN, enable); + + if (!enable && !ub_entity_test_priv_flag(uent, UB_ENTITY_ACTIVE)) + return; + + if (uent->ubc && uent->ubc->ops && uent->ubc->ops->entity_enable) { + ret = uent->ubc->ops->entity_enable(uent, enable); + if (ret) { + ub_err(uent, "entity enable, ret=%d, enable=%u\n", + ret, enable); + return; + } + } + + ub_info(uent, "modify active status to %u\n", enable); + + if (enable) + ub_entity_assign_priv_flag(uent, UB_ENTITY_ACTIVE, true); + else + ub_entity_assign_priv_flag(uent, UB_ENTITY_ACTIVE, false); +} +EXPORT_SYMBOL_GPL(ub_entity_enable); + +int ub_set_user_info(struct ub_entity *uent) +{ + if (!uent || !uent->ubc || !uent->ubc->uent) + return -EINVAL; + + u32 eid = uent->ubc->uent->eid; + + if (is_p_device(uent)) + goto cfg1; + + /* set dsteid to device */ + ub_cfg_write_dword(uent, UB_UEID_0, eid); + ub_cfg_write_dword(uent, UB_UEID_1, 0); + ub_cfg_write_dword(uent, UB_UEID_2, 0); + ub_cfg_write_dword(uent, UB_UEID_3, 0); + ub_cfg_write_dword(uent, UB_UCNA, uent->ubc->uent->cna); + +cfg1: + /* set tid to device */ + ub_cfg_write_dword(uent, UB_ENTITY_TOKEN_ID, uent->tid); + + return 0; +} +EXPORT_SYMBOL_GPL(ub_set_user_info); + +void ub_unset_user_info(struct ub_entity *uent) +{ + if (!uent) + return; + + if (is_p_device(uent)) + goto cfg1; + + ub_cfg_write_dword(uent, UB_UCNA, 0); + /* clear eid */ + ub_cfg_write_dword(uent, UB_UEID_0, 0); + ub_cfg_write_dword(uent, UB_UEID_1, 0); + ub_cfg_write_dword(uent, UB_UEID_2, 0); + ub_cfg_write_dword(uent, UB_UEID_3, 0); + +cfg1: + /* clear tid */ + ub_cfg_write_dword(uent, UB_ENTITY_TOKEN_ID, 0); +} +EXPORT_SYMBOL_GPL(ub_unset_user_info); + +static struct ub_entity *ub_get_ue_by_entity_idx(struct ub_entity *pue, u32 entity_idx) +{ + struct ub_entity *ue; + + if (ub_check_ue_para(pue, entity_idx)) + return NULL; + + list_for_each_entry(ue, &pue->ue_list, node) { + if (ue->entity_idx == entity_idx) + return ue; + } + + return NULL; +} + +int ub_activate_entity(struct ub_entity *uent, u32 entity_idx) +{ + struct ub_entity *target_dev; + struct ub_driver *udrv; + int ret; + + if (uent && uent->entity_idx != entity_idx && uent->is_mue) + target_dev = ub_get_ue_by_entity_idx(uent, entity_idx); + else + target_dev = uent; + if (!target_dev) + return -EINVAL; + + udrv = uent->driver; + if (!udrv || !udrv->activate) { + ub_err(uent, "udrv or activate is null\n"); + return -EINVAL; + } + + if (!device_trylock(&target_dev->dev)) + return -EBUSY; + + if (ub_entity_test_priv_flag(target_dev, UB_ENTITY_ACTIVE)) { + ub_warn(uent, "entity_idx[%u] is already in normal state\n", entity_idx); + device_unlock(&target_dev->dev); + return 0; + } + + ret = udrv->activate(uent, entity_idx); + if (ret) { + ub_err(uent, "udrv activate entity_idx[%u] failed, ret=%d\n", entity_idx, ret); + } else { + ub_entity_assign_priv_flag(target_dev, UB_ENTITY_ACTIVE, true); + ub_info(uent, "udrv activate entity_idx[%u] success\n", entity_idx); + } + + device_unlock(&target_dev->dev); + return ret; +} +EXPORT_SYMBOL_GPL(ub_activate_entity); + +int ub_deactivate_entity(struct ub_entity *uent, u32 entity_idx) +{ + struct ub_entity *target_dev; + struct ub_driver *udrv; + int ret; + + if (uent && uent->entity_idx != entity_idx && uent->is_mue) + target_dev = ub_get_ue_by_entity_idx(uent, entity_idx); + else + target_dev = uent; + if (!target_dev) + return -EINVAL; + + udrv = uent->driver; + if (!udrv || !udrv->deactivate) { + ub_err(uent, "udrv or deactivate is null\n"); + return -EINVAL; + } + + if (!device_trylock(&target_dev->dev)) + return -EBUSY; + + if (!ub_entity_test_priv_flag(target_dev, UB_ENTITY_ACTIVE)) { + ub_warn(uent, "entity_idx[%u] is already in disable state\n", entity_idx); + device_unlock(&target_dev->dev); + return 0; + } + + ret = udrv->deactivate(uent, entity_idx); + if (ret) { + ub_err(uent, "udrv deactivate entity_idx[%u] failed, ret=%d\n", entity_idx, ret); + } else { + ub_entity_assign_priv_flag(target_dev, UB_ENTITY_ACTIVE, false); + ub_info(uent, "udrv deactivate entity_idx[%u] success\n", entity_idx); + } + + device_unlock(&target_dev->dev); + return ret; +} +EXPORT_SYMBOL_GPL(ub_deactivate_entity); diff --git a/include/ub/ubus/ubus.h b/include/ub/ubus/ubus.h index 6cadca0bd0a2..08364cc9bf6a 100644 --- a/include/ub/ubus/ubus.h +++ b/include/ub/ubus/ubus.h @@ -258,6 +258,10 @@ struct ub_dynids { * context, so it can sleep. * @shutdown: Hook into reboot_notifier_list (kernel/sys.c). * Intended to stop any idling operations. + * @activate: Activate a specific entity. This function is called to + * activate an entity by its index. + * @deactivate: Deactivate a specific entity. This function is called to + * deactivate an entity by its index. * @err_handler: Error handling callbacks. * @groups: Sysfs attribute groups. * @dev_groups: Attributes attached to the device that will be @@ -281,6 +285,8 @@ struct ub_driver { /* entity removed (NULL if not a hot-plug capable driver) */ void (*remove)(struct ub_entity *uent); void (*shutdown)(struct ub_entity *uent); + int (*activate)(struct ub_entity *uent, u32 entity_idx); + int (*deactivate)(struct ub_entity *uent, u32 entity_idx); const struct ub_error_handlers *err_handler; const struct attribute_group **groups; const struct attribute_group **dev_groups; @@ -361,6 +367,39 @@ extern struct bus_type ub_bus_type; void ub_bus_type_iommu_ops_set(const struct iommu_ops *ops); const struct iommu_ops *ub_bus_type_iommu_ops_get(void); +/** + * ub_entity_enable() - Enable or disable ub entity + * @uent: UB entity. + * @enable: Enable or disable. + * + * Enables or disables the entity access bus and the path through which + * the bus accesses the entity. + * + * Context: Any context. + */ +void ub_entity_enable(struct ub_entity *uent, u8 enable); + +/** + * ub_set_user_info() - Initialize host information for the entity. + * @uent: UB entity. + * + * Initialize necessary host information for the entity for communication + * purposes, such as the host EID, CNA, and token ID. + * + * Context: Any context. + */ +int ub_set_user_info(struct ub_entity *uent); + +/** + * ub_unset_user_info() - Deinitialize host information for the entity. + * @uent: UB entity. + * + * Clearing the Host Information of a entity. + * + * Context: Any context. + */ +void ub_unset_user_info(struct ub_entity *uent); + /** * ub_enable_entities() - Enable ues of mue in batches. * @pue: UB mue. @@ -632,6 +671,28 @@ int ub_irq_vector(struct ub_entity *uent, unsigned int nr); */ const struct cpumask *ub_irq_get_affinity(struct ub_entity *uent, int nr); +/** + * ub_activate_entity() - Activate entity. + * @uent: UB entity. + * @entity_idx: Number of the entity to be activated. + * + * Context: Any context, It will take device_trylock()/device_unlock() + * Return: 0 if success, or %-EINVAL if the device doesn't match the driver, + * or %-EBUSY if can't get device_trylock(), or other failed negative values. + */ +int ub_activate_entity(struct ub_entity *uent, u32 entity_idx); + +/** + * ub_deactivate_entity() - Deactivate entity. + * @uent: UB entity. + * @entity_idx: Number of the entity to be deactivated. + * + * Context: Any context, It will take device_trylock()/device_unlock() + * Return: 0 if success, or %-EINVAL if the entity doesn't match the driver, + * or %-EBUSY if can't get device_trylock(), or other failed negative values. + */ +int ub_deactivate_entity(struct ub_entity *uent, u32 entity_idx); + /** * ub_cfg_read_byte() - 1 byte configuration access read. * @uent: UB entity. @@ -774,6 +835,10 @@ void ub_stop_and_remove_ent(struct ub_entity *uent); static inline struct ub_entity *ub_entity_get(struct ub_entity *uent) { return NULL; } static inline void ub_entity_put(struct ub_entity *uent) {} +static inline void ub_entity_enable(struct ub_entity *uent, u8 enable) {} +static inline int ub_set_user_info(struct ub_entity *uent) +{ return -ENODEV; } +static inline void ub_unset_user_info(struct ub_entity *uent) {} static inline int ub_enable_entities(struct ub_entity *pue, int nums) { return -ENODEV; } static inline void ub_disable_entities(struct ub_entity *pue) {} @@ -852,6 +917,10 @@ static inline int ub_irq_vector(struct ub_entity *uent, unsigned int nr) { return -EINVAL; } static inline const struct cpumask * ub_irq_get_affinity(struct ub_entity *uent, int nr) { return cpu_possible_mask; } +static inline int ub_activate_entity(struct ub_entity *uent, u32 entity_idx) +{ return -ENODEV; } +static inline int ub_deactivate_entity(struct ub_entity *uent, u32 entity_idx) +{ return -ENODEV; } static inline int __ub_register_driver(struct ub_driver *drv, struct module *owner, const char *mod_name) -- Gitee From 458b64d73c4a53bb9cc55927f45bfe0a22ad914b Mon Sep 17 00:00:00 2001 From: Jianquan Lin Date: Sat, 20 Sep 2025 11:37:54 +0800 Subject: [PATCH 35/45] ub:ubus: Add UBUS RAS framework driver inclusion category: feature bugzilla: https://gitee.com/openeuler/kernel/issues/ID34DG CVE: NA ----------------------------------------------------------- Add UBUS Reliability, Availability and Serviceability framework, and add UBUS error handle in Generic Hardware Error Source of Linux. Signed-off-by: Jianquan Lin --- drivers/acpi/apei/ghes.c | 19 ++++++++++++++++--- drivers/ub/ubus/Makefile | 2 +- drivers/ub/ubus/ras.c | 31 +++++++++++++++++++++++++++++++ include/linux/cper.h | 18 ++++++++++++++++++ include/ub/ubus/uber.h | 24 ++++++++++++++++++++++++ 5 files changed, 90 insertions(+), 4 deletions(-) create mode 100644 drivers/ub/ubus/ras.c create mode 100644 include/ub/ubus/uber.h diff --git a/drivers/acpi/apei/ghes.c b/drivers/acpi/apei/ghes.c index 8c12d7d98ef4..3adaabb9c924 100644 --- a/drivers/acpi/apei/ghes.c +++ b/drivers/acpi/apei/ghes.c @@ -41,7 +41,7 @@ #include #include #include - +#include #include #include #include @@ -649,6 +649,18 @@ static void ghes_handle_aer(struct acpi_hest_generic_data *gdata) #endif } +static void ghes_handle_ubus_err(struct acpi_hest_generic_data *gdata) +{ + struct cper_sec_ubus *ubus_err = acpi_hest_get_payload(gdata); + ub_ras_recover_func_t ras_recover_func; + + ras_recover_func = ub_ras_get_recover_func(); + if (ras_recover_func) + ras_recover_func(ubus_err, gdata->error_severity); + else + pr_warn(GHES_PFX "UBUS error occurs when the ras driver is not loaded.\n"); +} + static BLOCKING_NOTIFIER_HEAD(vendor_record_notify_list); int ghes_register_vendor_record_notifier(struct notifier_block *nb) @@ -730,8 +742,9 @@ static void ghes_do_proc(struct ghes *ghes, } else if (guid_equal(sec_type, &CPER_SEC_PCIE)) { ghes_handle_aer(gdata); - } - else if (guid_equal(sec_type, &CPER_SEC_PROC_ARM)) { + } else if (guid_equal(sec_type, &CPER_SEC_UBUS)) { + ghes_handle_ubus_err(gdata); + } else if (guid_equal(sec_type, &CPER_SEC_PROC_ARM)) { queued = ghes_handle_arm_hw_error(gdata, sev, sync); } else { void *err = acpi_hest_get_payload(gdata); diff --git a/drivers/ub/ubus/Makefile b/drivers/ub/ubus/Makefile index d90034f0003f..b7991115a3d3 100644 --- a/drivers/ub/ubus/Makefile +++ b/drivers/ub/ubus/Makefile @@ -1,6 +1,6 @@ # SPDX-License-Identifier: GPL-2.0+ -obj-$(CONFIG_UB_UBUS) += ub-driver.o controller.o config.o entity.o +obj-$(CONFIG_UB_UBUS) += ub-driver.o controller.o config.o entity.o ras.o obj-$(CONFIG_UB_UBUS) += msi/ ubus-y := ubus_driver.o sysfs.o ubus_controller.o msg.o ubus_config.o port.o cc.o eid.o cna.o route.o diff --git a/drivers/ub/ubus/ras.c b/drivers/ub/ubus/ras.c new file mode 100644 index 000000000000..a6553a661ac5 --- /dev/null +++ b/drivers/ub/ubus/ras.c @@ -0,0 +1,31 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (c) HiSilicon Technologies Co., Ltd. 2025. All rights reserved. + */ + +#include +#include + +static ub_ras_recover_func_t ub_ras_recover_func; +static DEFINE_SPINLOCK(ubus_ghes_lock); + +void ub_ras_register_recover_func(ub_ras_recover_func_t func) + +{ + spin_lock(&ubus_ghes_lock); + ub_ras_recover_func = func; + spin_unlock(&ubus_ghes_lock); +} +EXPORT_SYMBOL_GPL(ub_ras_register_recover_func); + +ub_ras_recover_func_t ub_ras_get_recover_func(void) +{ + ub_ras_recover_func_t tmp_func; + + spin_lock(&ubus_ghes_lock); + tmp_func = ub_ras_recover_func; + spin_unlock(&ubus_ghes_lock); + + return tmp_func; +} +EXPORT_SYMBOL_GPL(ub_ras_get_recover_func); diff --git a/include/linux/cper.h b/include/linux/cper.h index bd8e65c56e26..d0d496ff1f1b 100644 --- a/include/linux/cper.h +++ b/include/linux/cper.h @@ -197,6 +197,10 @@ enum { #define CPER_SEC_DMAR_IOMMU \ GUID_INIT(0x036F84E1, 0x7F37, 0x428c, 0xA7, 0x9E, 0x57, 0x5F, \ 0xDF, 0xAA, 0x84, 0xEC) +/* UBUS */ +#define CPER_SEC_UBUS \ + GUID_INIT(0x74D255B0, 0x73E8, 0x488A, 0xB8, 0x67, 0x90, 0x65, \ + 0x4A, 0x35, 0x86, 0x5E) #define CPER_PROC_VALID_TYPE 0x0001 #define CPER_PROC_VALID_ISA 0x0002 @@ -541,6 +545,20 @@ struct cper_sec_pcie { u8 aer_info[96]; }; +struct cper_sec_ubus { + u64 validation_bits; + u32 eid; + u32 cna; + u32 port; + u32 vendor; + u16 device; + u16 type; + u16 class_code; + u16 reserved; + u32 overflow_flag; + u32 err_info[72]; +}; + /* Firmware Error Record Reference, UEFI v2.7 sec N.2.10 */ struct cper_sec_fw_err_rec_ref { u8 record_type; diff --git a/include/ub/ubus/uber.h b/include/ub/ubus/uber.h new file mode 100644 index 000000000000..73a4418d5894 --- /dev/null +++ b/include/ub/ubus/uber.h @@ -0,0 +1,24 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Copyright (c) HiSilicon Technologies Co., Ltd. 2025. All rights reserved. + */ + +#ifndef _UB_UBUS_UBER_H_ +#define _UB_UBUS_UBER_H_ + +#include + +typedef void (*ub_ras_recover_func_t)(struct cper_sec_ubus *err, int serverity); + +#ifdef CONFIG_UB_UBUS +void ub_ras_register_recover_func(ub_ras_recover_func_t func); +ub_ras_recover_func_t ub_ras_get_recover_func(void); +#else +static inline void ub_ras_register_recover_func(ub_ras_recover_func_t func) {} +static inline ub_ras_recover_func_t ub_ras_get_recover_func(void) +{ + return NULL; +} +#endif /* CONFIG_UB_UBUS */ + +#endif /* _UB_UBUS_UBER_H_ */ -- Gitee From e35bcb75bbc42e9f563851b7ac7e3fff51b49320 Mon Sep 17 00:00:00 2001 From: Yuhao Xiang Date: Fri, 19 Sep 2025 17:04:58 +0800 Subject: [PATCH 36/45] ub:ubus: Enable the ub decoder device support driver inclusion category: feature bugzilla: https://gitee.com/openeuler/kernel/issues/ID34DG CVE: NA ----------------------------------------------------------- Configure the various registers required for the ub decoder hardware to function properly. Signed-off-by: Yuhao Xiang --- drivers/ub/ubus/Makefile | 2 +- drivers/ub/ubus/cap.c | 3 + drivers/ub/ubus/decoder.c | 263 ++++++++++++++++++++++++++++++++++++++ drivers/ub/ubus/decoder.h | 47 +++++++ include/ub/ubus/ubus.h | 1 + 5 files changed, 315 insertions(+), 1 deletion(-) create mode 100644 drivers/ub/ubus/decoder.c create mode 100644 drivers/ub/ubus/decoder.h diff --git a/drivers/ub/ubus/Makefile b/drivers/ub/ubus/Makefile index b7991115a3d3..8c35ed594e96 100644 --- a/drivers/ub/ubus/Makefile +++ b/drivers/ub/ubus/Makefile @@ -4,6 +4,6 @@ obj-$(CONFIG_UB_UBUS) += ub-driver.o controller.o config.o entity.o ras.o obj-$(CONFIG_UB_UBUS) += msi/ ubus-y := ubus_driver.o sysfs.o ubus_controller.o msg.o ubus_config.o port.o cc.o eid.o cna.o route.o -ubus-y += enum.o resource.o ubus_entity.o reset.o cap.o interrupt.o +ubus-y += enum.o resource.o ubus_entity.o reset.o cap.o interrupt.o decoder.o obj-$(CONFIG_UB_UBUS_BUS) += ubus.o diff --git a/drivers/ub/ubus/cap.c b/drivers/ub/ubus/cap.c index a9073de22fbe..3eca70059195 100644 --- a/drivers/ub/ubus/cap.c +++ b/drivers/ub/ubus/cap.c @@ -6,6 +6,7 @@ #define pr_fmt(fmt) "ubus cap: " fmt #include "ubus.h" +#include "decoder.h" #include "interrupt.h" #define DW_CHECK 3 @@ -232,6 +233,7 @@ void ub_init_capabilities(struct ub_entity *uent) init_cfg1_cap: /* cfg1 caps */ + ub_decoder_init(uent); ub_intr_init(uent); uent->reset_fn = 1; @@ -240,6 +242,7 @@ void ub_init_capabilities(struct ub_entity *uent) void ub_uninit_capabilities(struct ub_entity *uent) { /* cfg1 cap */ + ub_decoder_uninit(uent); uent->reset_fn = 0; } diff --git a/drivers/ub/ubus/decoder.c b/drivers/ub/ubus/decoder.c new file mode 100644 index 000000000000..f79706161d6e --- /dev/null +++ b/drivers/ub/ubus/decoder.c @@ -0,0 +1,263 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (c) HiSilicon Technologies Co., Ltd. 2025. All rights reserved. + */ + +#define pr_fmt(fmt) "ubus decoder: " fmt + +#include +#include + +#include "ubus.h" +#include "decoder.h" + +#define MMIO_SIZE_MASK GENMASK_ULL(18, 16) +#define MMIO_SIZE_OFFSET 16 + +static void ub_decoder_uninit_queue(struct ub_decoder *decoder) +{} + +static int ub_decoder_init_queue(struct ub_bus_controller *ubc, + struct ub_decoder *decoder) +{ + return 0; +} + +static u32 set_mmio_base_reg(struct ub_decoder *decoder) +{ + u32 ret; + + ret = (u32)ub_cfg_write_dword(decoder->uent, DECODER_MMIO_BA0, + lower_32_bits(decoder->mmio_base_addr)); + ret |= (u32)ub_cfg_write_dword(decoder->uent, DECODER_MMIO_BA1, + upper_32_bits(decoder->mmio_base_addr)); + if (ret) + ub_err(decoder->uent, "set decoder mmio base fail\n"); + + return ret; +} + +static u32 set_page_table_reg(struct ub_decoder *decoder) +{ + u32 ret; + + ret = (u32)ub_cfg_write_dword(decoder->uent, DECODER_MATT_BA0, + lower_32_bits(decoder->pgtlb.pgtlb_dma)); + ret |= (u32)ub_cfg_write_dword(decoder->uent, DECODER_MATT_BA1, + upper_32_bits(decoder->pgtlb.pgtlb_dma)); + if (ret) + ub_err(decoder->uent, "set decoder page table reg fail\n"); + + return ret; +} + +static u32 set_queue_reg(struct ub_decoder *decoder) +{ + return 0; +} + +static u32 set_decoder_enable(struct ub_decoder *decoder) +{ + u32 ret = (u32)ub_cfg_write_dword(decoder->uent, DECODER_CTRL, 1); + + if (ret) + ub_err(decoder->uent, "set decoder enable fail\n"); + + return ret; +} + +static u32 ub_decoder_device_set(struct ub_decoder *decoder) +{ + u32 ret; + + ret = set_mmio_base_reg(decoder); + ret |= set_page_table_reg(decoder); + ret |= set_queue_reg(decoder); + ret |= set_decoder_enable(decoder); + + return ret; +} + +static int ub_decoder_create_page_table(struct ub_decoder *decoder) +{ + return 0; +} + +static void ub_decoder_free_page_table(struct ub_decoder *decoder) +{} + +static void ub_get_decoder_mmio_base(struct ub_bus_controller *ubc, + struct ub_decoder *decoder) +{ + struct resource_entry *entry; + + decoder->mmio_base_addr = -1; + resource_list_for_each_entry(entry, &ubc->resources) { + if (entry->res->flags == IORESOURCE_MEM && + strstr(entry->res->name, "UB_BUS_CTL") && + entry->res->start < decoder->mmio_base_addr) + decoder->mmio_base_addr = entry->res->start; + } + + ub_info(decoder->uent, "decoder mmio base is %#llx\n", + decoder->mmio_base_addr); +} + +static const char * const mmio_size_desc[] = { + "128Gbyte", "256Gbyte", "512Gbyte", "1Tbyte", + "2Tbyte", "4Tbyte", "8Tbyte", "16Tbyte" +}; + +static int ub_get_decoder_cap(struct ub_decoder *decoder) +{ + struct ub_entity *uent = decoder->uent; + u32 val; + int ret; + + ret = ub_cfg_read_dword(uent, DECODER_CAP, &val); + if (ret) { + ub_err(uent, "read decoder cap fail\n"); + return ret; + } + + decoder->mmio_size_sup = (val & MMIO_SIZE_MASK) >> MMIO_SIZE_OFFSET; + + return 0; +} + +static int ub_create_decoder(struct ub_bus_controller *ubc) +{ + struct ub_entity *uent = ubc->uent; + struct ub_decoder *decoder; + int ret; + + decoder = kzalloc(sizeof(*decoder), GFP_KERNEL); + if (!decoder) + return -ENOMEM; + + decoder->dev = &ubc->dev; + decoder->uent = uent; + mutex_init(&decoder->table_lock); + + ub_get_decoder_mmio_base(ubc, decoder); + + ret = ub_get_decoder_cap(decoder); + if (ret) + goto release_decoder; + + ret = ub_decoder_init_queue(ubc, decoder); + if (ret) + goto release_decoder; + + ret = ub_decoder_create_page_table(decoder); + if (ret) { + ub_err(uent, "decoder create page table fail\n"); + goto release_queue; + } + + ret = ub_decoder_device_set(decoder); + if (ret) + goto release_page_table; + + ubc->decoder = decoder; + + decoder->irq_num = -1; + decoder->rg_size = SZ_4G; + + ub_info(uent, "decoder create success\n"); + return ret; + +release_page_table: + ub_decoder_free_page_table(decoder); +release_queue: + ub_decoder_uninit_queue(decoder); +release_decoder: + kfree(decoder); + return ret; +} + +static void unset_mmio_base_reg(struct ub_decoder *decoder) +{ + struct ub_entity *uent = decoder->uent; + u32 ret; + + ret = (u32)ub_cfg_write_dword(uent, DECODER_MMIO_BA0, 0); + ret |= (u32)ub_cfg_write_dword(uent, DECODER_MMIO_BA1, 0); + if (ret) + ub_err(uent, "unset mmio base reg fail\n"); +} + +static void unset_queue_reg(struct ub_decoder *decoder) +{} + +static void unset_page_table_reg(struct ub_decoder *decoder) +{ + struct ub_entity *uent = decoder->uent; + u32 ret; + + ret = (u32)ub_cfg_write_dword(uent, DECODER_MATT_BA0, 0); + ret |= (u32)ub_cfg_write_dword(uent, DECODER_MATT_BA1, 0); + if (ret) + ub_err(uent, "unset page table reg fail\n"); +} + +static void unset_decoder_enable(struct ub_decoder *decoder) +{ + struct ub_entity *uent = decoder->uent; + + if (ub_cfg_write_dword(uent, DECODER_CTRL, 0)) + ub_err(uent, "unset decoder enable fail\n"); +} + +static void ub_decoder_device_unset(struct ub_decoder *decoder) +{ + unset_decoder_enable(decoder); + unset_queue_reg(decoder); + unset_page_table_reg(decoder); + unset_mmio_base_reg(decoder); +} + +static void ub_remove_decoder(struct ub_bus_controller *ubc) +{ + struct ub_decoder *decoder = ubc->decoder; + + if (!decoder) { + ub_err(ubc->uent, "remove decoder, decoder is null\n"); + return; + } + + ub_decoder_device_unset(decoder); + + ub_decoder_free_page_table(decoder); + + ub_decoder_uninit_queue(decoder); + + kfree(decoder); + + ubc->decoder = NULL; +} + +void ub_decoder_init(struct ub_entity *uent) +{ + int ret; + + if (!uent || !uent->ubc) + return; + + if (!is_ibus_controller(uent)) + return; + + ret = ub_create_decoder(uent->ubc); + WARN_ON(ret); +} + +void ub_decoder_uninit(struct ub_entity *uent) +{ + if (!uent || !uent->ubc) + return; + + if (!is_ibus_controller(uent)) + return; + + ub_remove_decoder(uent->ubc); +} diff --git a/drivers/ub/ubus/decoder.h b/drivers/ub/ubus/decoder.h new file mode 100644 index 000000000000..1b9ea5dafd24 --- /dev/null +++ b/drivers/ub/ubus/decoder.h @@ -0,0 +1,47 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Copyright (c) HiSilicon Technologies Co., Ltd. 2025. All rights reserved. + */ + +#ifndef __DECODER_H__ +#define __DECODER_H__ + +#include + +struct reg_default_val { + u32 cmdq_cfg_val; + u32 evtq_cfg_val; +}; + +struct page_table_desc { + void *page_base; + dma_addr_t page_dma; + u32 count; +}; + +struct page_table { + void *pgtlb_base; + dma_addr_t pgtlb_dma; + struct page_table_desc *desc_base; +}; + +struct ub_decoder { + struct device *dev; + struct ub_entity *uent; + phys_addr_t mmio_base_addr; + u32 mmio_size_sup; + u64 rg_size; + struct page_table pgtlb; + struct reg_default_val vals; + int irq_num; + + struct page_table_desc invalid_desc; + u64 invalid_page_dma; + + struct mutex table_lock; +}; + +void ub_decoder_init(struct ub_entity *uent); +void ub_decoder_uninit(struct ub_entity *uent); + +#endif /* __DECODER_H__ */ diff --git a/include/ub/ubus/ubus.h b/include/ub/ubus/ubus.h index 08364cc9bf6a..3eeded783ad8 100644 --- a/include/ub/ubus/ubus.h +++ b/include/ub/ubus/ubus.h @@ -322,6 +322,7 @@ struct ub_bus_controller { u32 ctl_no; struct message_device *mdev; + struct ub_decoder *decoder; struct list_head resources; struct list_head node; struct list_head devs; -- Gitee From d9c7651e4e60b0d92397eceeb05d6bdd1552adbc Mon Sep 17 00:00:00 2001 From: Junlong Zheng Date: Sat, 20 Sep 2025 11:01:15 +0800 Subject: [PATCH 37/45] ub:ubus: Adding a ue Creation Destruction Mechanism driver inclusion category: feature bugzilla: https://gitee.com/openeuler/kernel/issues/ID34DG CVE: NA ----------------------------------------------------------- Implement the ability to trigger the enabling of ue through a sysfs entry, adding ue change notification mechanism and add interfaces to access the device via different keys. Signed-off-by: Junlong Zheng --- drivers/ub/ubus/sysfs.c | 260 ++++++++++++++++++++++++++++++++++ drivers/ub/ubus/ubus_driver.c | 98 +++++++++++++ drivers/ub/ubus/ubus_entity.c | 30 ++++ drivers/ub/ubus/ubus_entity.h | 1 + include/ub/ubus/ubus.h | 68 +++++++++ 5 files changed, 457 insertions(+) diff --git a/drivers/ub/ubus/sysfs.c b/drivers/ub/ubus/sysfs.c index eca8d3f7f58c..b816e82e4c0d 100644 --- a/drivers/ub/ubus/sysfs.c +++ b/drivers/ub/ubus/sysfs.c @@ -7,6 +7,7 @@ #include "ubus.h" #include "sysfs.h" +#include "ubus_entity.h" #define ub_config_attr(field, format_string) \ static ssize_t field##_show(struct device *dev, struct device_attribute *attr, char *buf) \ @@ -147,3 +148,262 @@ const struct attribute_group *ub_bus_groups[] = { const struct device_type ub_dev_type = { .groups = NULL, }; + +static ssize_t ub_total_entities_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct ub_entity *uent; + + uent = to_ub_entity(dev); + return sysfs_emit(buf, "%u\n", uent->total_funcs); +} +static DEVICE_ATTR_RO(ub_total_entities); + +static ssize_t ub_totalues_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct ub_entity *uent; + + uent = to_ub_entity(dev); + return sysfs_emit(buf, "%u\n", uent->total_ues); +} +static DEVICE_ATTR_RO(ub_totalues); + +static ssize_t ub_numues_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct ub_entity *uent; + + uent = to_ub_entity(dev); + return sysfs_emit(buf, "%u\n", uent->num_ues); +} + +static int ub_numues_store_driver_check(struct ub_entity *uent) +{ + if (!uent->driver) { + ub_err(uent, "no driver bound to device, enable failed\n"); + return -ENOENT; + } + + if (!uent->driver->virt_configure) { + ub_err(uent, "driver does not support virt_configure via sysfs\n"); + return -ENOENT; + } + + return 0; +} + +static void ub_numues_store_ues_process(struct ub_entity *uent, int nums) +{ + int ue_start_idx, ue_end_idx, i, cnt, ret; + struct ub_entity *ue; + + /* + * The software finds "cnt" disabled ues for "ue_start_idx" to + * "ue_end_idx" based on the ordered ue_list. "ue_start_idx" is + * the entity_idx of first ues and "ue_end_idx" is calculated based + * on the total number(that is, "nums") ues to be enabled. + */ + ue_start_idx = uent->uem.start_entity_idx; + ue_end_idx = ue_start_idx + nums - 1; + i = ue_start_idx; + cnt = nums - uent->num_ues; + list_for_each_entry(ue, &uent->ue_list, node) { + for (; i < ue->entity_idx; i++) { + /* + * The "ue_list" is sorted by entity_idx in ascending order. + * Ensure that others ues before this ue are enabled. + */ + ret = uent->driver->virt_configure(uent, i, 1); + if (ret) + ub_warn(uent, "driver virt_configure, ret=%d\n", ret); + if (--cnt == 0) + return; + } + /* Skip this enabled ue. */ + i++; + } + + /* Ensure that the remaining ues enabled. */ + for (; i <= ue_end_idx; i++) { + ret = uent->driver->virt_configure(uent, i, 1); + if (ret) + ub_warn(uent, "driver virt_configure, ret=%d\n", ret); + if (--cnt == 0) + return; + } +} + +static ssize_t ub_numues_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct ub_entity *uent, *ue, *tmp; + int nums, ret; + + uent = to_ub_entity(dev); + + ret = kstrtoint(buf, 0, &nums); + if (ret < 0) + return ret; + + ub_info(uent, "enable %d ue, total_ue=%u\n", + nums, uent->total_ues); + + if (nums < 0 || nums > uent->total_ues) + return -EINVAL; + + device_lock(&uent->dev); + ret = ub_numues_store_driver_check(uent); + if (ret) + goto exit; + + if (nums == 0) { + list_for_each_entry_safe_reverse(ue, tmp, &uent->ue_list, node) + (void)uent->driver->virt_configure(uent, ue->entity_idx, 0); + goto exit; + } + + if (nums <= uent->num_ues || (!entity_flex_en && uent->num_ues)) { + ub_info(uent, "ue already enabled, num=%u.\n", uent->num_ues); + goto exit; + } + + ub_numues_store_ues_process(uent, nums); +exit: + device_unlock(&uent->dev); + if (ret < 0) + return ret; + + return count; +} +static DEVICE_ATTR_RW(ub_numues); + +static ssize_t ub_release_ue_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct ub_entity *uent, *ue, *tmp; + unsigned long uent_num; + int ret; + +#define BASE_16 16 + ret = kstrtoul(buf, BASE_16, &uent_num); + if (ret < 0) + return ret; + + uent = to_ub_entity(dev); + device_lock(&uent->dev); + ret = ub_numues_store_driver_check(uent); + if (ret) + goto exit; + + list_for_each_entry_safe_reverse(ue, tmp, &uent->ue_list, node) + if (ue->uent_num == (u32)uent_num) { + (void)uent->driver->virt_configure(uent, ue->entity_idx, 0); + device_unlock(&uent->dev); + return count; + } + + ub_err(uent, "uent %#05lx does not belong to this pue\n", uent_num); +exit: + device_unlock(&uent->dev); + return -EIO; +} +static DEVICE_ATTR_WO(ub_release_ue); + +struct dev_attr_creat_group { + struct device_attribute *attr; + bool conditions; +}; + +static ssize_t ue_list_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct ub_entity *uent = to_ub_entity(dev); + struct ub_entity *ent; + int cnt = 0; + + list_for_each_entry(ent, &uent->ue_list, node) + cnt += sysfs_emit_at(buf, cnt, "%s\n", dev_name(&ent->dev)); + + return cnt; +} +DEVICE_ATTR_RO(ue_list); + +static ssize_t mue_list_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct ub_entity *uent = to_ub_entity(dev); + struct ub_entity *ent; + int cnt = 0; + + list_for_each_entry(ent, &uent->mue_list, node) + cnt += sysfs_emit_at(buf, cnt, "%s\n", dev_name(&ent->dev)); + + return cnt; +} +DEVICE_ATTR_RO(mue_list); + +#define CREATE_CONDITIONS(c) ((c) ? 1 : 0) + +static int ub_create_capabilities_sysfs(struct ub_entity *uent) +{ + struct dev_attr_creat_group grp[] = { + { &dev_attr_ub_total_entities, CREATE_CONDITIONS(uent->entity_idx == 0) }, + { &dev_attr_ub_totalues, CREATE_CONDITIONS(uent->is_mue) }, + { &dev_attr_ub_numues, CREATE_CONDITIONS(uent->is_mue) }, + { &dev_attr_ub_release_ue, CREATE_CONDITIONS(uent->is_mue && entity_flex_en) }, + { &dev_attr_mue_list, CREATE_CONDITIONS(uent->entity_idx == 0) }, + { &dev_attr_ue_list, CREATE_CONDITIONS(uent->is_mue) }, + }; + int retval, i; + + for (i = 0; i < ARRAY_SIZE(grp); i++) { + if (grp[i].conditions) { + retval = device_create_file(&uent->dev, grp[i].attr); + if (retval) + goto err; + } + } + + return 0; +err: + for (i = i - 1; i >= 0; i--) + if (grp[i].conditions) + device_remove_file(&uent->dev, grp[i].attr); + return retval; +} + +static void ub_remove_capabilities_sysfs(struct ub_entity *uent) +{ + struct dev_attr_creat_group grp[] = { + { &dev_attr_ub_total_entities, CREATE_CONDITIONS(uent->entity_idx == 0) }, + { &dev_attr_ub_totalues, CREATE_CONDITIONS(uent->is_mue) }, + { &dev_attr_ub_numues, CREATE_CONDITIONS(uent->is_mue) }, + { &dev_attr_ub_release_ue, CREATE_CONDITIONS(uent->is_mue && entity_flex_en) }, + { &dev_attr_mue_list, CREATE_CONDITIONS(uent->entity_idx == 0) }, + { &dev_attr_ue_list, CREATE_CONDITIONS(uent->is_mue) }, + }; + int i; + + for (i = 0; i < ARRAY_SIZE(grp); i++) + if (grp[i].conditions) + device_remove_file(&uent->dev, grp[i].attr); +} + +int ub_create_sysfs_dev_files(struct ub_entity *uent) +{ + int retval; + + retval = ub_create_capabilities_sysfs(uent); + if (retval) + return retval; + + return 0; +} + +void ub_remove_sysfs_ent_files(struct ub_entity *uent) +{ + ub_remove_capabilities_sysfs(uent); +} diff --git a/drivers/ub/ubus/ubus_driver.c b/drivers/ub/ubus/ubus_driver.c index da0d5030ee5b..fe5591c33999 100644 --- a/drivers/ub/ubus/ubus_driver.c +++ b/drivers/ub/ubus/ubus_driver.c @@ -173,6 +173,96 @@ const struct ub_device_id *ub_match_id(const struct ub_device_id *ids, return NULL; } +static struct ub_entity * +ub_get_dev_by_id_inner(struct ub_entity *from, const void *data, + int (*match)(struct device *dev, const void *data)) +{ + struct device *dev, *dev_start = NULL; + struct ub_entity *fdev = NULL; + + if (from) + dev_start = &from->dev; + + dev = bus_find_device(&ub_bus_type, dev_start, (void *)data, match); + if (dev) + fdev = to_ub_entity(dev); + + ub_entity_put(from); + return fdev; +} + +static int ub_entity_match_by_id(struct device *dev, const void *data) +{ + const struct ub_device_id *id = (const struct ub_device_id *)data; + struct ub_entity *pue = to_ub_entity(dev); + + if (ub_match_one_device(id, pue)) + return 1; + return 0; +} + +struct ub_entity *ub_get_entity(unsigned int vendor, unsigned int device, + struct ub_entity *from) +{ + struct ub_device_id id = { + .vendor = vendor, + .device = device, + }; + + return ub_get_dev_by_id_inner(from, &id, ub_entity_match_by_id); +} +EXPORT_SYMBOL_GPL(ub_get_entity); + +static int ub_entity_match_by_guid(struct device *dev, const void *data) +{ + const struct ub_guid *guid = (const struct ub_guid *)data; + struct ub_entity *uent = to_ub_entity(dev); + + if (guid_equal(&uent->guid.id, &guid->id)) + return 1; + return 0; +} + +struct ub_entity *ub_get_ent_by_guid(const struct ub_guid *guid) +{ + return ub_get_dev_by_id_inner(NULL, guid, ub_entity_match_by_guid); +} +EXPORT_SYMBOL_GPL(ub_get_ent_by_guid); + +static int ub_entity_match_by_eid(struct device *dev, const void *data) +{ + struct ub_entity *uent = to_ub_entity(dev); + const u32 eid = *((const u32 *)data); + + if (uent->eid == eid) + return 1; + + return 0; +} + +struct ub_entity *ub_get_ent_by_eid(unsigned int eid) +{ + return ub_get_dev_by_id_inner(NULL, &eid, ub_entity_match_by_eid); +} +EXPORT_SYMBOL_GPL(ub_get_ent_by_eid); + +static int ub_entity_match_by_uent_num(struct device *dev, const void *data) +{ + const u32 uent_num = *((const u32 *)data); + struct ub_entity *uent = to_ub_entity(dev); + + if (uent->uent_num == uent_num) + return 1; + + return 0; +} + +struct ub_entity *ub_get_ent_by_uent_num(unsigned int uent_num) +{ + return ub_get_dev_by_id_inner(NULL, &uent_num, ub_entity_match_by_uent_num); +} +EXPORT_SYMBOL_GPL(ub_get_ent_by_uent_num); + static const struct ub_device_id *ub_match_device(struct ub_driver *drv, struct ub_entity *dev) { @@ -271,9 +361,17 @@ static int ub_entity_probe(struct device *dev) { struct ub_driver *drv = to_ub_driver(dev->driver); struct ub_entity *ub_entity = to_ub_entity(dev); + struct ub_entity *pue; + u16 entity_idx; int ret; ub_entity_get(ub_entity); + if (!ub_entity->is_mue) { + pue = ub_entity->pue; + entity_idx = ub_entity->entity_idx; + ub_virt_notify(pue, entity_idx, true); + } + ret = __ub_entity_probe(drv, ub_entity); if (ret) ub_entity_put(ub_entity); diff --git a/drivers/ub/ubus/ubus_entity.c b/drivers/ub/ubus/ubus_entity.c index c17db8decaae..d7be521e1772 100644 --- a/drivers/ub/ubus/ubus_entity.c +++ b/drivers/ub/ubus/ubus_entity.c @@ -710,10 +710,40 @@ static int ub_enable_mues(struct ub_entity *pue, int nums, struct ue_map *map) return ret; } +void ub_virt_notify(struct ub_entity *pue, u16 entity_idx, bool is_en) +{ + const char *operate = is_en ? "enable" : "disable"; + struct ub_driver *pdrv; + int ret; + + if (pue) { + pdrv = pue->driver; + if (pdrv && pdrv->virt_notify) { + ret = pdrv->virt_notify(pue, entity_idx, is_en); + if (ret) + ub_warn(pue, "drv virt notify %s ue with entity_idx %u failed, ret=%d\n", + operate, entity_idx, ret); + } + } +} + void ub_disable_ent(struct ub_entity *uent) { + struct ub_entity *pue; + bool ue_flag = false; + u16 entity_idx; + ub_info(uent, "disable entity, eid=%#05x\n", uent->eid); + if (!uent->is_mue) { + pue = uent->pue; + entity_idx = uent->entity_idx; + ue_flag = true; + } + ub_stop_and_remove_ent(uent); + + if (ue_flag) + ub_virt_notify(pue, entity_idx, false); } EXPORT_SYMBOL_GPL(ub_disable_ent); diff --git a/drivers/ub/ubus/ubus_entity.h b/drivers/ub/ubus/ubus_entity.h index 8f4c0fb4bcc5..aa535e6adf70 100644 --- a/drivers/ub/ubus/ubus_entity.h +++ b/drivers/ub/ubus/ubus_entity.h @@ -17,5 +17,6 @@ void ub_stop_entities(void); void ub_remove_entities(void); int ub_num_ue(struct ub_entity *uent); void ub_disable_ent(struct ub_entity *uent); +void ub_virt_notify(struct ub_entity *pue, u16 entity_idx, bool is_en); #endif /* __UBUS_ENTITY_H__ */ diff --git a/include/ub/ubus/ubus.h b/include/ub/ubus/ubus.h index 3eeded783ad8..826af33e892c 100644 --- a/include/ub/ubus/ubus.h +++ b/include/ub/ubus/ubus.h @@ -258,6 +258,11 @@ struct ub_dynids { * context, so it can sleep. * @shutdown: Hook into reboot_notifier_list (kernel/sys.c). * Intended to stop any idling operations. + * @virt_configure: Optional driver callback to allow configuration of + * ues. This function is called to enable or disable ues. + * @virt_notify: Optional driver callback to notify the driver about + * changes in ue status. This function is called + * when the status of a ue changes. * @activate: Activate a specific entity. This function is called to * activate an entity by its index. * @deactivate: Deactivate a specific entity. This function is called to @@ -285,6 +290,8 @@ struct ub_driver { /* entity removed (NULL if not a hot-plug capable driver) */ void (*remove)(struct ub_entity *uent); void (*shutdown)(struct ub_entity *uent); + int (*virt_configure)(struct ub_entity *uent, int entity_idx, bool is_en); + int (*virt_notify)(struct ub_entity *uent, int entity_idx, bool is_en); int (*activate)(struct ub_entity *uent, u32 entity_idx); int (*deactivate)(struct ub_entity *uent, u32 entity_idx); const struct ub_error_handlers *err_handler; @@ -368,6 +375,58 @@ extern struct bus_type ub_bus_type; void ub_bus_type_iommu_ops_set(const struct iommu_ops *ops); const struct iommu_ops *ub_bus_type_iommu_ops_get(void); +/** + * ub_get_ent_by_eid() - Searching for UB Devices by EID. + * @eid: entity EID. + * + * Traverse the UB bus device linked list and search for the device with + * the target EID. You need to call ub_entity_put() after using it. + * + * Context: Any context. + * Return: The device found, or NULL if not found. + */ +struct ub_entity *ub_get_ent_by_eid(unsigned int eid); + +/** + * ub_get_ent_by_uent_num() - Searching for UB entities by entity Num. + * @uent_num: entity Num. + * + * Traverse the UB bus device linked list and search for the entity with + * the target entity Num. You need to call ub_entity_put() after using it. + * + * Context: Any context. + * Return: The entity found, or NULL if not found. + */ +struct ub_entity *ub_get_ent_by_uent_num(unsigned int uent_num); + +/** + * ub_get_ent_by_guid() - Searching for UB entities by GUID. + * @guid: GUID. + * + * Traverse the UB bus entity linked list and search for the entity with + * the target GUID. You need to call ub_entity_put() after using it. + * + * Context: Any context. + * Return: The entity found, or NULL if not found. + */ +struct ub_entity *ub_get_ent_by_guid(const struct ub_guid *guid); + +/** + * ub_get_entity() - Searching for UB entities by Vendor and entity ID. + * @vendor: Vendor ID. + * @entity: entity ID. + * @from: Previous UB entity found in search, or %NULL for new search. + * + * Traverse the UB bus entity linked list and search for the entity with + * the target Vendor and entity ID. You need to call ub_entity_put() + * after using it. + * + * Context: Any context. + * Return: The entity found, or NULL if not found. + */ +struct ub_entity *ub_get_entity(unsigned int vendor, unsigned int entity, + struct ub_entity *from); + /** * ub_entity_enable() - Enable or disable ub entity * @uent: UB entity. @@ -833,6 +892,15 @@ void ub_stop_and_remove_ent(struct ub_entity *uent); #else /* CONFIG_UB_UBUS is not enabled */ #define dev_is_ub(d) (false) +static inline struct ub_entity *ub_get_ent_by_eid(unsigned int eid) +{ return NULL; } +static inline struct ub_entity *ub_get_ent_by_uent_num(unsigned int uent_num) +{ return NULL; } +static inline struct ub_entity *ub_get_ent_by_guid(const struct ub_guid *guid) +{ return NULL; } +static inline struct ub_entity * +ub_get_entity(unsigned int vendor, unsigned int entity, struct ub_entity *from) +{ return NULL; } static inline struct ub_entity *ub_entity_get(struct ub_entity *uent) { return NULL; } static inline void ub_entity_put(struct ub_entity *uent) {} -- Gitee From c0627d7f9d4a296fa94ff8a60104f70c696036a6 Mon Sep 17 00:00:00 2001 From: Yuhao Xiang Date: Fri, 19 Sep 2025 17:24:14 +0800 Subject: [PATCH 38/45] ub:ubus: Creating and deleting ub decoder page table mappings driver inclusion category: feature bugzilla: https://gitee.com/openeuler/kernel/issues/ID34DG CVE: NA ----------------------------------------------------------- Construct address mapping and populate the page table with information retrieved from the decoder hardware lookup. Clear the established mapping relationships. Signed-off-by: Yuhao Xiang --- drivers/ub/ubus/Makefile | 2 +- drivers/ub/ubus/decoder.c | 61 ++++++++++- drivers/ub/ubus/decoder.h | 28 +++++ drivers/ub/ubus/omm.c | 209 ++++++++++++++++++++++++++++++++++++++ drivers/ub/ubus/omm.h | 28 +++++ 5 files changed, 326 insertions(+), 2 deletions(-) create mode 100644 drivers/ub/ubus/omm.c create mode 100644 drivers/ub/ubus/omm.h diff --git a/drivers/ub/ubus/Makefile b/drivers/ub/ubus/Makefile index 8c35ed594e96..e0f036870acb 100644 --- a/drivers/ub/ubus/Makefile +++ b/drivers/ub/ubus/Makefile @@ -4,6 +4,6 @@ obj-$(CONFIG_UB_UBUS) += ub-driver.o controller.o config.o entity.o ras.o obj-$(CONFIG_UB_UBUS) += msi/ ubus-y := ubus_driver.o sysfs.o ubus_controller.o msg.o ubus_config.o port.o cc.o eid.o cna.o route.o -ubus-y += enum.o resource.o ubus_entity.o reset.o cap.o interrupt.o decoder.o +ubus-y += enum.o resource.o ubus_entity.o reset.o cap.o interrupt.o decoder.o omm.o obj-$(CONFIG_UB_UBUS_BUS) += ubus.o diff --git a/drivers/ub/ubus/decoder.c b/drivers/ub/ubus/decoder.c index f79706161d6e..366dcb24d130 100644 --- a/drivers/ub/ubus/decoder.c +++ b/drivers/ub/ubus/decoder.c @@ -9,11 +9,14 @@ #include #include "ubus.h" +#include "omm.h" #include "decoder.h" #define MMIO_SIZE_MASK GENMASK_ULL(18, 16) #define MMIO_SIZE_OFFSET 16 +#define DECODER_PAGE_TABLE_ENTRY_SIZE 8 + static void ub_decoder_uninit_queue(struct ub_decoder *decoder) {} @@ -80,11 +83,67 @@ static u32 ub_decoder_device_set(struct ub_decoder *decoder) static int ub_decoder_create_page_table(struct ub_decoder *decoder) { + struct page_table_desc *invalid_desc = &decoder->invalid_desc; + struct ub_entity *uent = decoder->uent; + struct page_table *pgtlb; + void *pgtlb_base; + size_t size; + + size = DECODER_PAGE_TABLE_ENTRY_SIZE * DECODER_PAGE_TABLE_SIZE; + pgtlb = &decoder->pgtlb; + pgtlb_base = dmam_alloc_coherent(decoder->dev, size, + &pgtlb->pgtlb_dma, GFP_KERNEL); + if (!pgtlb_base) { + ub_err(uent, "allocate ub decoder page table fail\n"); + return -ENOMEM; + } + pgtlb->pgtlb_base = pgtlb_base; + + size = sizeof(*pgtlb->desc_base) * DECODER_PAGE_TABLE_SIZE; + pgtlb->desc_base = kzalloc(size, GFP_KERNEL); + if (!pgtlb->desc_base) { + ub_err(uent, "allocate ub decoder page table desc fail\n"); + goto release_pgtlb; + } + + invalid_desc->page_base = dmam_alloc_coherent(decoder->dev, + RANGE_TABLE_PAGE_SIZE, + &invalid_desc->page_dma, + GFP_KERNEL); + if (!invalid_desc->page_base) { + ub_err(uent, "decoder alloc free page fail\n"); + goto release_desc; + } + decoder->invalid_page_dma = (invalid_desc->page_dma & + DECODER_PGTBL_PGPRT_MASK) >> + DECODER_DMA_PAGE_ADDR_OFFSET; + + ub_decoder_init_page_table(decoder, pgtlb_base); + return 0; + +release_desc: + kfree(pgtlb->desc_base); + pgtlb->desc_base = NULL; +release_pgtlb: + size = DECODER_PAGE_TABLE_ENTRY_SIZE * DECODER_PAGE_TABLE_SIZE; + dmam_free_coherent(decoder->dev, size, pgtlb_base, pgtlb->pgtlb_dma); + return -ENOMEM; } static void ub_decoder_free_page_table(struct ub_decoder *decoder) -{} +{ + struct page_table_desc *invalid_desc = &decoder->invalid_desc; + size_t size; + + dmam_free_coherent(decoder->dev, RANGE_TABLE_PAGE_SIZE, + invalid_desc->page_base, invalid_desc->page_dma); + kfree(decoder->pgtlb.desc_base); + + size = DECODER_PAGE_TABLE_ENTRY_SIZE * DECODER_PAGE_TABLE_SIZE; + dmam_free_coherent(decoder->dev, size, decoder->pgtlb.pgtlb_base, + decoder->pgtlb.pgtlb_dma); +} static void ub_get_decoder_mmio_base(struct ub_bus_controller *ubc, struct ub_decoder *decoder) diff --git a/drivers/ub/ubus/decoder.h b/drivers/ub/ubus/decoder.h index 1b9ea5dafd24..6b0fcee2681e 100644 --- a/drivers/ub/ubus/decoder.h +++ b/drivers/ub/ubus/decoder.h @@ -41,6 +41,34 @@ struct ub_decoder { struct mutex table_lock; }; +#define DECODER_PGTBL_PGPRT_MASK GENMASK_ULL(47, 12) +#define DECODER_DMA_PAGE_ADDR_OFFSET 12 + +#define PGTLB_CACHE_IR_NC 0b00 +#define PGTLB_CACHE_IR_WBRA 0b01 +#define PGTLB_CACHE_IR_WT 0b10 +#define PGTLB_CACHE_IR_WB 0b11 +#define PGTLB_CACHE_OR_NC 0b0000 +#define PGTLB_CACHE_OR_WBRA 0b0100 +#define PGTLB_CACHE_OR_WT 0b1000 +#define PGTLB_CACHE_OR_WB 0b1100 +#define PGTLB_CACHE_SH_NSH 0b000000 +#define PGTLB_CACHE_SH_OSH 0b100000 +#define PGTLB_CACHE_SH_ISH 0b110000 + +#define PGTLB_ATTR_DEFAULT (PGTLB_CACHE_IR_WBRA | \ + PGTLB_CACHE_OR_WBRA | \ + PGTLB_CACHE_SH_ISH) + +#define RGTLB_TO_PGTLB 8 +#define DECODER_PAGE_ENTRY_SIZE 64 +#define DECODER_PAGE_SIZE (1 << 12) +#define DECODER_PAGE_TABLE_SIZE (1 << 12) +#define PAGE_TABLE_PAGE_SIZE (DECODER_PAGE_ENTRY_SIZE * DECODER_PAGE_SIZE) +#define RANGE_TABLE_PAGE_SIZE (DECODER_PAGE_ENTRY_SIZE * \ + DECODER_PAGE_SIZE * \ + RGTLB_TO_PGTLB) + void ub_decoder_init(struct ub_entity *uent); void ub_decoder_uninit(struct ub_entity *uent); diff --git a/drivers/ub/ubus/omm.c b/drivers/ub/ubus/omm.c new file mode 100644 index 000000000000..d4c84b585f93 --- /dev/null +++ b/drivers/ub/ubus/omm.c @@ -0,0 +1,209 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (c) HiSilicon Technologies Co., Ltd. 2025. All rights reserved. + */ + +#define pr_fmt(fmt) "ubus omm: " fmt + +#include "decoder.h" +#include "omm.h" + +enum entry_type { + INVALID_ENTRY = 0x0, + PAGE_TABLE = 0x1, + PAGE = 0x2, + RANGE_TABLE = 0x3, +}; + +struct page_table_entry { + u64 entry_type : 2; + u64 reserve1 : 10; + u64 next_lv_addr : 36; + u64 reserve2 : 8; + u64 pgtlb_attr : 8; +}; + +struct page_entry { + /* dw0 ~ dw1 */ + u64 entry_type : 2; + u64 reserve0 : 6; + u64 token_vld : 1; + u64 reserve1 : 3; + u64 ub_addr : 52; + /* dw2 ~ dw3 */ + u64 reserve2 : 32; + u64 tpg_num : 20; + u64 order_type : 2; + u64 order_id : 8; + u64 reserve3 : 2; + /* dw4 ~ dw5 */ + u64 dst_eid_low; + /* dw6 ~ dw7 */ + u64 dst_eid_high; + /* dw8 ~ dw9 */ + u64 token_id : 20; + u64 reserve4 : 12; + u64 token_value : 32; + /* dw10 ~ dw11 */ + u64 upi : 16; + u64 reserve5 : 48; + /* dw12 ~ dw13 */ + u64 reserve6; + /* dw14 ~ dw15 */ + u64 reserve7 : 32; + u64 src_eid : 20; + u64 reserve8 : 12; +}; + +struct range_table_entry { + /* dw0 ~ dw1 */ + u64 entry_type : 2; + u64 reserve0 : 6; + u64 token_vld : 1; + u64 reserve1 : 3; + u64 next_lv_addr : 36; + u64 reserve2 : 8; + u64 page_table_attr : 8; + /* dw2 ~ dw3 */ + u64 mem_base : 15; + u64 reserve3 : 1; + u64 mem_limit : 15; + u64 reserve4 : 1; + u64 reserve5 : 32; + /* dw4 ~ dw5 */ + u64 reserve6 : 12; + u64 ub_addr : 52; + /* dw6 ~ dw7 */ + u64 reserve7 : 32; + u64 tpg_num : 20; + u64 order_type : 2; + u64 order_id : 8; + u64 reserve8 : 2; + /* dw8 ~ dw9 */ + u64 dst_eid_low; + /* dw10 ~ dw11 */ + u64 dst_eid_high; + /* dw12 ~ dw13 */ + u64 token_id : 20; + u64 reserve9 : 12; + u64 token_value : 32; + /* dw14 ~ dw15 */ + u64 upi : 16; + u64 reserve10 : 16; + u64 src_eid : 20; + u64 reserve11 : 12; +}; + +#define DECODER_PAGE_TABLE_LOC 35 +#define DECODER_PAGE_TABLE_MASK GENMASK_ULL(43, 35) + +#define get_pgtlb_idx(decoder, pa) ((((pa) - (decoder)->mmio_base_addr) & \ + DECODER_PAGE_TABLE_MASK) >> \ + DECODER_PAGE_TABLE_LOC) + +static int handle_range_table(struct ub_decoder *decoder, u64 *offset, + struct decoder_map_info *info, bool is_map) +{ + return 0; +} + +static int handle_page_table(struct ub_decoder *decoder, u64 *offset, + struct decoder_map_info *info, bool is_map) +{ + return 0; +} + +static int handle_pgrg_table(struct ub_decoder *decoder, u64 *offset, + struct decoder_map_info *info, bool is_map) +{ + return 0; +} + +static int handle_table(struct ub_decoder *decoder, + struct decoder_map_info *info, bool is_map) +{ + struct page_table *pgtlb = &decoder->pgtlb; + struct range_table_entry *range_tlb_entry; + u64 offset, rollback_size; + u32 table_idx; + int ret; + + for (offset = 0; offset < info->size;) { + rollback_size = offset; + + mutex_lock(&decoder->table_lock); + table_idx = get_pgtlb_idx(decoder, info->pa + offset); + range_tlb_entry = (struct range_table_entry *) + pgtlb->pgtlb_base + table_idx; + + if (range_tlb_entry->entry_type == RANGE_TABLE) + ret = handle_range_table(decoder, &offset, info, + is_map); + else if (range_tlb_entry->next_lv_addr == + decoder->invalid_page_dma) + ret = handle_pgrg_table(decoder, &offset, info, is_map); + else + ret = handle_page_table(decoder, &offset, info, is_map); + mutex_unlock(&decoder->table_lock); + + if (ret) { + ub_err(decoder->uent, "decoder table occur fatal error ret=%d\n", + ret); + /* if it is map operation, revert it. unmap operation can't revert */ + if (is_map) + (void)ub_decoder_unmap(decoder, info->pa, + rollback_size); + break; + } + } + return ret; +} + +int ub_decoder_unmap(struct ub_decoder *decoder, phys_addr_t addr, u64 size) +{ + int ret; + struct decoder_map_info info = { + .pa = addr, + .size = size, + }; + + if (!decoder) { + pr_err("unmap mmio decoder ptr is null\n"); + return -EINVAL; + } + if (size < SZ_1M) + size = SZ_1M; + ret = handle_table(decoder, &info, false); + + return ret; +} + +int ub_decoder_map(struct ub_decoder *decoder, struct decoder_map_info *info) +{ + if (!decoder || !info) { + pr_err("decoder or map info is null\n"); + return -EINVAL; + } + if (info->size < SZ_1M) + info->size = SZ_1M; + ub_info(decoder->uent, + "decoder map, pa=%#llx, uba=%#llx, size=%#llx, cna=%#x, orderid=%#x, ordertype=%#x, eid_l=%#llx, eid_h=%#llx, upi=%#x src_eid=%#x\n", + info->pa, info->uba, info->size, info->tpg_num, info->order_id, + info->order_type, info->eid_low, info->eid_high, info->upi, + info->src_eid); + + return handle_table(decoder, info, true); +} + +void ub_decoder_init_page_table(struct ub_decoder *decoder, void *pgtlb_base) +{ + struct page_table_entry *pgtlb_entry; + int i; + + for (i = 0; i < DECODER_PAGE_TABLE_SIZE; i++) { + pgtlb_entry = (struct page_table_entry *)pgtlb_base + i; + pgtlb_entry->entry_type = PAGE_TABLE; + pgtlb_entry->next_lv_addr = decoder->invalid_page_dma; + pgtlb_entry->pgtlb_attr = PGTLB_ATTR_DEFAULT; + } +} diff --git a/drivers/ub/ubus/omm.h b/drivers/ub/ubus/omm.h new file mode 100644 index 000000000000..4258a7609736 --- /dev/null +++ b/drivers/ub/ubus/omm.h @@ -0,0 +1,28 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Copyright (c) HiSilicon Technologies Co., Ltd. 2025. All rights reserved. + */ + +#ifndef __OMM_H__ +#define __OMM_H__ + +struct decoder_map_info { + phys_addr_t pa; + phys_addr_t uba; + u64 size; + u32 tpg_num; + u8 order_id; + u8 order_type; + u64 eid_low; + u64 eid_high; + u32 token_id; + u32 token_value; + u32 upi; + u32 src_eid; +}; + +void ub_decoder_init_page_table(struct ub_decoder *decoder, void *pgtlb_base); +int ub_decoder_unmap(struct ub_decoder *decoder, phys_addr_t addr, u64 size); +int ub_decoder_map(struct ub_decoder *decoder, struct decoder_map_info *info); + +#endif /* __OMM_H__ */ -- Gitee From 305dc281d43e18fc0c2ba69f30ffc1edb789a2b4 Mon Sep 17 00:00:00 2001 From: Jianquan Lin Date: Sat, 20 Sep 2025 14:05:39 +0800 Subject: [PATCH 39/45] ub:ubus: Support UBUS RAS capability driver inclusion category: feature bugzilla: https://gitee.com/openeuler/kernel/issues/ID34DG CVE: NA ----------------------------------------------------------- Support UBUS Reliability, Availability and Serviceability capability, including error report, ras recover and so on. Signed-off-by: Jianquan Lin --- drivers/ub/ubus/Makefile | 2 + drivers/ub/ubus/services/ras.c | 337 +++++++++++++++++++++++++++++++++ drivers/ub/ubus/services/ras.h | 40 ++++ include/ub/ubus/ubus.h | 19 ++ 4 files changed, 398 insertions(+) create mode 100644 drivers/ub/ubus/services/ras.c create mode 100644 drivers/ub/ubus/services/ras.h diff --git a/drivers/ub/ubus/Makefile b/drivers/ub/ubus/Makefile index e0f036870acb..b5731e2082b7 100644 --- a/drivers/ub/ubus/Makefile +++ b/drivers/ub/ubus/Makefile @@ -6,4 +6,6 @@ obj-$(CONFIG_UB_UBUS) += msi/ ubus-y := ubus_driver.o sysfs.o ubus_controller.o msg.o ubus_config.o port.o cc.o eid.o cna.o route.o ubus-y += enum.o resource.o ubus_entity.o reset.o cap.o interrupt.o decoder.o omm.o +ubus-y += services/ras.o + obj-$(CONFIG_UB_UBUS_BUS) += ubus.o diff --git a/drivers/ub/ubus/services/ras.c b/drivers/ub/ubus/services/ras.c new file mode 100644 index 000000000000..5c96bf5e2244 --- /dev/null +++ b/drivers/ub/ubus/services/ras.c @@ -0,0 +1,337 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (c) HiSilicon Technologies Co., Ltd. 2025. All rights reserved. + */ + +#define pr_fmt(fmt) "ubus ras: " fmt + +#include +#include +#include + +#include "../ubus.h" +#include "../msg.h" +#include "../reset.h" +#include "ras.h" + +#define RAS_CE 0 +#define RAS_UC_NFE 1 +#define RAS_UC_FE 2 +#define RAS_RECOVER_RING_SIZE 16 +#define RAS_SEVERITY_STR(severity) \ + ((severity) == RAS_CE ? "CORRECTABLE" : \ + (severity) == RAS_UC_NFE ? "UNCORRECTABLE NONFATAL" : \ + (severity) == RAS_UC_FE ? "UNCORRECTABLE FATAL" : \ + "UNKNOWN") + +enum ras_err_level { + RAS_ERR_PORT_LEVEL, + RAS_ERR_DEVICE_LEVEL, +}; + +int cper_severity_to_ub_ras(int cper_severity) +{ + switch (cper_severity) { + case CPER_SEV_FATAL: + return RAS_UC_FE; + case CPER_SEV_RECOVERABLE: + return RAS_UC_NFE; + default: + return RAS_CE; + } +} + +static void print_ub_err_info(struct ub_entity *uent, u32 *spec_info, u32 *vendor_info) +{ +#define DW_EACH_ROUND 4 +#define DW_0 0 +#define DW_1 1 +#define DW_2 2 +#define DW_3 3 + for (int i = 0; i < RAS_SPEC_ERR_PKG_LEN; i += DW_EACH_ROUND) + ub_err(uent, "SPEC Err Pkg Header=%08x %08x %08x %08x\n", + spec_info[i], spec_info[i + DW_1], + spec_info[i + DW_2], spec_info[i + DW_3]); + + ub_err(uent, "VENDOR Err Pkg Header=%08x %08x\n", vendor_info[DW_0], vendor_info[DW_1]); +} + +static void cper_print_ub_err(struct ub_entity *uent, struct ras_recover_entry *entry) +{ + struct ras_cap_regs *reg = entry->regs; + + ub_err(uent, "UBUS ERR type=%s.\n", RAS_SEVERITY_STR(entry->severity)); + + if (entry->err_level == RAS_ERR_PORT_LEVEL) + ub_err(uent, "Error level: PORT ERR, port=%u.\n", entry->port); + else + ub_err(uent, "Error level: DEVICE ERR.\n"); + + if (!reg) { + ub_err(uent, "No error detail need to show.\n"); + return; + } + + ub_err(uent, "Uncorrectable ERR status=%#08llx, mask=%#08llx, severity=%#08llx\n", + reg->uncor_status, reg->uncor_mask, reg->uncor_severity); + ub_err(uent, "Correctable ERR status=%#08llx, mask=%#08llx, severity=%#08llx\n", + reg->cor_status, reg->cor_mask, reg->cor_severity); + + print_ub_err_info(uent, reg->spec_def_err_info, reg->vendor_def_err_info); +} + +static ub_ers_result_t ras_merge_result(ub_ers_result_t orig, + ub_ers_result_t new_one) +{ + ub_ers_result_t ret = orig; + + if (new_one == UB_ERS_RESULT_NO_ERR_DRIVER) + return UB_ERS_RESULT_NO_ERR_DRIVER; + + if (new_one == UB_ERS_RESULT_NONE) + return ret; + + switch (orig) { + case UB_ERS_RESULT_CAN_RECOVER: + case UB_ERS_RESULT_RECOVERED: + ret = new_one; + break; + case UB_ERS_RESULT_DISCONNECT: + if (new_one == UB_ERS_RESULT_NEED_RESET) + ret = new_one; + break; + default: + pr_err("unused ras error result %d\n", new_one); + break; + } + + return ret; +} + +static ub_ers_result_t report_resource_enabled(struct ub_entity *uent, + ub_ers_result_t orig) +{ + ub_ers_result_t result, new_one; + struct ub_driver *udrv; + + device_lock(&uent->dev); + udrv = uent->driver; + if (!udrv || !udrv->err_handler || !udrv->err_handler->resource_enabled) { + result = orig; + goto out; + } + + new_one = udrv->err_handler->resource_enabled(uent); + result = ras_merge_result(orig, new_one); +out: + device_unlock(&uent->dev); + return result; +} + +static void ub_uevent_ers(struct ub_entity *uent, ub_ers_result_t err_type) +{ +#define MAX_ENVP 3 + char *envp[MAX_ENVP]; + int idx = 0; + + switch (err_type) { + case UB_ERS_RESULT_NONE: + case UB_ERS_RESULT_CAN_RECOVER: + envp[idx++] = "ERROR_EVENT=BEGIN_RECOVERY"; + envp[idx++] = "DEVICE_ONLINE=0"; + break; + case UB_ERS_RESULT_RECOVERED: + envp[idx++] = "ERROR_EVENT=SUCCESSFUL_RECOVERY"; + envp[idx++] = "DEVICE_ONLINE=1"; + break; + case UB_ERS_RESULT_DISCONNECT: + envp[idx++] = "ERROR_EVENT=FAILED_RECOVERY"; + envp[idx++] = "DEVICE_ONLINE=0"; + break; + default: + pr_err("unused uevent ras err_type %d\n", err_type); + break; + } + + if (idx > 0) { + envp[idx++] = NULL; + kobject_uevent_env(&uent->dev.kobj, KOBJ_CHANGE, envp); + } +} + +static ub_ers_result_t ub_error_report(struct ub_entity *uent, ub_channel_state_t state) +{ + struct ub_driver *udrv; + ub_ers_result_t result; + + device_lock(&uent->dev); + udrv = uent->driver; + if (!udrv || !udrv->err_handler || !udrv->err_handler->error_detected) { + ub_warn(uent, "No driver bounded or no handler provided.\n"); + result = UB_ERS_RESULT_NO_ERR_DRIVER; + goto exit; + } + + result = udrv->err_handler->error_detected(uent, state); + ub_uevent_ers(uent, result); + result = ras_merge_result(UB_ERS_RESULT_CAN_RECOVER, result); +exit: + device_unlock(&uent->dev); + return result; +} + +static void ub_do_recovery(struct ub_entity *uent, ub_channel_state_t state, + struct ras_recover_entry *entry) +{ + u32 port_id = entry->port; + ub_ers_result_t ret; + int recovery; + + ret = ub_error_report(uent, state); + if (ret == UB_ERS_RESULT_NEED_RESET) { + if (entry->err_level == RAS_ERR_PORT_LEVEL) + recovery = ub_port_reset(uent, port_id); + else + recovery = is_ibus_controller(uent) ? 0 : ub_device_reset(uent); + + if (!recovery) + ret = UB_ERS_RESULT_RECOVERED; + else + goto failed; + } + + if (ret == UB_ERS_RESULT_CAN_RECOVER) + ret = report_resource_enabled(uent, UB_ERS_RESULT_RECOVERED); + + if (ret != UB_ERS_RESULT_RECOVERED) + goto failed; + + ub_info(uent, "uent ras recovery succeeded\n"); + return; +failed: + ub_uevent_ers(uent, UB_ERS_RESULT_DISCONNECT); + ub_err(uent, "uent ras recovery failed\n"); +} + +static inline void ras_cap_regs_free(struct ras_cap_regs **regs) +{ + if (!regs || !(*regs)) + return; + + kfree(*regs); + *regs = NULL; +} + +static DEFINE_KFIFO(ras_recover_ring, struct ras_recover_entry, + RAS_RECOVER_RING_SIZE); + +static void ub_ras_recover_work_func(struct work_struct *work) +{ + struct ras_recover_entry entry; + struct ub_entity *uent; + + while (kfifo_get(&ras_recover_ring, &entry)) { + uent = ub_get_ent_by_eid(entry.eid); + if (!uent) { + pr_err("can not find ub entity by eid=%#05x\n", + entry.eid); + ras_cap_regs_free(&entry.regs); + continue; + } + + cper_print_ub_err(uent, &entry); + if (uent->ubc->cluster) { + ub_err(uent, "UB RAS: no need to recover with cluster mode\n"); + } else { + if (entry.severity == RAS_UC_FE) + ub_do_recovery(uent, ub_channel_io_frozen, &entry); + else if (entry.severity == RAS_UC_NFE) + ub_do_recovery(uent, ub_channel_io_normal, &entry); + } + + ras_cap_regs_free(&entry.regs); + ub_entity_put(uent); + } +} + +static inline void ras_recover_entry_init(struct ras_recover_entry *entry, + struct cper_sec_ubus *ubus_err) +{ + entry->eid = ubus_err->eid; + entry->cna = ubus_err->cna; + entry->port = ubus_err->port; + entry->vendor = ubus_err->vendor; + entry->device = ubus_err->device; + entry->type = ubus_err->type; + entry->class_code = ubus_err->class_code; +} + +static DEFINE_SPINLOCK(ub_ras_recover_ring_lock); +static DECLARE_WORK(ub_ras_recover_work, ub_ras_recover_work_func); + +void ub_ras_recover_queue(struct cper_sec_ubus *ubus_err, int severity) +{ +#define PORT_VALID_BIT 0b100ULL +#define OVERFLOW_FLAG_BIT 0b10000ULL + int ub_severity = cper_severity_to_ub_ras(severity); + struct ras_cap_regs *ras_cap, *tmp_cap; + struct ras_recover_entry entry = {}; + u32 overflow = 0; + u32 err_status; + u32 err_level; + + if (ubus_err->validation_bits & OVERFLOW_FLAG_BIT) { + overflow = ubus_err->overflow_flag; + pr_info("severity %s is overflowed, flag=%#x\n", + RAS_SEVERITY_STR(ub_severity), overflow); + } + + tmp_cap = (struct ras_cap_regs *)ubus_err->err_info; + err_status = (ub_severity == RAS_CE) ? + tmp_cap->cor_status : tmp_cap->uncor_status; + if (err_status == 0 && overflow == 0) { + pr_info("no need to handle ras with err_status=0x%x, severity=%s\n", + err_status, RAS_SEVERITY_STR(ub_severity)); + return; + } + + ras_cap = kzalloc(sizeof(*ras_cap), GFP_ATOMIC); + if (!ras_cap) + return; + + memcpy(ras_cap, tmp_cap, sizeof(struct ras_cap_regs)); + err_level = (ubus_err->validation_bits & PORT_VALID_BIT) ? + RAS_ERR_PORT_LEVEL : RAS_ERR_DEVICE_LEVEL; + ras_recover_entry_init(&entry, ubus_err); + entry.err_level = err_level; + entry.severity = ub_severity; + entry.overflow = overflow; + entry.regs = ras_cap; + + if (kfifo_in_spinlocked(&ras_recover_ring, &entry, 1, + &ub_ras_recover_ring_lock)) { + schedule_work(&ub_ras_recover_work); + return; + } + + if (ub_severity == RAS_UC_FE) + pr_err("FIFO ring overflow when recovering error for eid:%05x, severity:%s\n", + ubus_err->eid, RAS_SEVERITY_STR(ub_severity)); + else + pr_err("FIFO ring overflow when recovering error for eid:%05x, port:%u, severity:%s\n", + ubus_err->eid, ubus_err->port, + RAS_SEVERITY_STR(ub_severity)); + + kfree(ras_cap); +} + +void ub_ras_init(void) +{ + ub_ras_register_recover_func(ub_ras_recover_queue); +} + +void ub_ras_uninit(void) +{ + ub_ras_register_recover_func(NULL); + (void)flush_work(&ub_ras_recover_work); +} diff --git a/drivers/ub/ubus/services/ras.h b/drivers/ub/ubus/services/ras.h new file mode 100644 index 000000000000..c68c2dce8471 --- /dev/null +++ b/drivers/ub/ubus/services/ras.h @@ -0,0 +1,40 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Copyright (c) HiSilicon Technologies Co., Ltd. 2025. All rights reserved. + */ + +#ifndef __SERVICE_RAS_H__ +#define __SERVICE_RAS_H__ + +#define RAS_SPEC_ERR_PKG_LEN 32 + +#pragma pack(4) +struct ras_cap_regs { + u32 err_ctrl_reg; + u64 uncor_status; + u64 uncor_mask; + u64 uncor_severity; + u64 cor_status; + u64 cor_mask; + u64 cor_severity; + u32 err_info_ctrl_reg; + u32 spec_def_err_info[RAS_SPEC_ERR_PKG_LEN]; + u32 vendor_def_err_info[2]; +}; +#pragma pack() + +struct ras_recover_entry { + u32 eid; + u32 cna; + u32 port; + u32 vendor; + u16 device; + u16 type; + u16 class_code; + u16 err_level; + int severity; + u32 overflow; + struct ras_cap_regs *regs; +}; + +#endif /* __SERVICE_RAS_H__ */ diff --git a/include/ub/ubus/ubus.h b/include/ub/ubus/ubus.h index 826af33e892c..09fe5395db41 100644 --- a/include/ub/ubus/ubus.h +++ b/include/ub/ubus/ubus.h @@ -38,6 +38,23 @@ (ub_resource_end((dev), (resno)) - \ ub_resource_start((dev), (resno)) + 1)) +typedef unsigned int __bitwise ub_channel_state_t; +enum { + ub_channel_io_normal = (__force ub_channel_state_t) 1, + ub_channel_io_frozen = (__force ub_channel_state_t) 2, + ub_channel_io_perm_failure = (__force ub_channel_state_t) 3, +}; + +typedef unsigned int __bitwise ub_ers_result_t; +enum ub_ers_result { + UB_ERS_RESULT_NONE = (__force ub_ers_result_t) 1, + UB_ERS_RESULT_CAN_RECOVER = (__force ub_ers_result_t) 2, + UB_ERS_RESULT_NEED_RESET = (__force ub_ers_result_t) 3, + UB_ERS_RESULT_DISCONNECT = (__force ub_ers_result_t) 4, + UB_ERS_RESULT_RECOVERED = (__force ub_ers_result_t) 5, + UB_ERS_RESULT_NO_ERR_DRIVER = (__force ub_ers_result_t) 6, +}; + #define UB_GUID_DW_NUM SZ_4 struct ub_bus_region { @@ -222,6 +239,8 @@ struct ub_error_handlers { /* UB function reset prepare or completed */ void (*reset_prepare)(struct ub_entity *uent); void (*reset_done)(struct ub_entity *uent); + ub_ers_result_t (*error_detected)(struct ub_entity *uent, ub_channel_state_t state); + ub_ers_result_t (*resource_enabled)(struct ub_entity *uent); }; struct ub_dynids { -- Gitee From 73cf3d2fe98eb319a463b422e54da99a2200f13e Mon Sep 17 00:00:00 2001 From: Junlong Zheng Date: Sat, 20 Sep 2025 13:45:02 +0800 Subject: [PATCH 40/45] ub:ubus: Support for UB Character Device Driver driver inclusion category: feature bugzilla: https://gitee.com/openeuler/kernel/issues/ID34DG CVE: NA ----------------------------------------------------------- Implement the character device driver for UB, providing an ioctl operation entry for user space. Signed-off-by: Junlong Zheng --- drivers/ub/ubus/Makefile | 2 +- drivers/ub/ubus/ioctl.c | 103 ++++++++++++++++++++++++++++++++++++ drivers/ub/ubus/ioctl.h | 12 +++++ include/uapi/ub/ubus/ubus.h | 31 +++++++++++ 4 files changed, 147 insertions(+), 1 deletion(-) create mode 100644 drivers/ub/ubus/ioctl.c create mode 100644 drivers/ub/ubus/ioctl.h create mode 100644 include/uapi/ub/ubus/ubus.h diff --git a/drivers/ub/ubus/Makefile b/drivers/ub/ubus/Makefile index b5731e2082b7..9419727937bb 100644 --- a/drivers/ub/ubus/Makefile +++ b/drivers/ub/ubus/Makefile @@ -4,7 +4,7 @@ obj-$(CONFIG_UB_UBUS) += ub-driver.o controller.o config.o entity.o ras.o obj-$(CONFIG_UB_UBUS) += msi/ ubus-y := ubus_driver.o sysfs.o ubus_controller.o msg.o ubus_config.o port.o cc.o eid.o cna.o route.o -ubus-y += enum.o resource.o ubus_entity.o reset.o cap.o interrupt.o decoder.o omm.o +ubus-y += enum.o resource.o ubus_entity.o reset.o cap.o interrupt.o decoder.o omm.o ioctl.o ubus-y += services/ras.o diff --git a/drivers/ub/ubus/ioctl.c b/drivers/ub/ubus/ioctl.c new file mode 100644 index 000000000000..88da1eccbb5c --- /dev/null +++ b/drivers/ub/ubus/ioctl.c @@ -0,0 +1,103 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (c) HiSilicon Technologies Co., Ltd. 2025. All rights reserved. + */ + +#define pr_fmt(fmt) "ubus ioctl: " fmt + +#include +#include + +#include "ubus.h" + +#define UBUS_MAX_DEVICES 1 +#define UBUS_DEVICE_NAME "unified_bus" +#define UBUS_CLASS_NAME "unified_bus" + +struct unified_bus_ctx { + dev_t devno; + struct cdev cdev; + struct class *ub_class; + struct device *dev; +}; +static struct unified_bus_ctx ubus_ctx; + +static int ubus_fops_open(struct inode *inode, struct file *filep) +{ + return 0; +} + +static int ubus_fops_release(struct inode *inode, struct file *filep) +{ + return 0; +} + +static long ubus_fops_unl_ioctl(struct file *filep, + unsigned int cmd, unsigned long arg) +{ + switch (cmd) { + case UBUS_IOCTL_GET_API_VERSION: + return UBUS_API_VERSION; + default: + pr_err("ubus ioctl cmd %#x not support\n", cmd); + return -ENOTTY; + } +} + +static const struct file_operations ubus_fops = { + .owner = THIS_MODULE, + .unlocked_ioctl = ubus_fops_unl_ioctl, + .open = ubus_fops_open, + .release = ubus_fops_release, +}; + +int ub_cdev_init(void) +{ + struct cdev *cdev = &ubus_ctx.cdev; + struct device *dev; + struct class *cls; + dev_t devno; + int ret; + + ret = alloc_chrdev_region(&devno, 0, UBUS_MAX_DEVICES, UBUS_DEVICE_NAME); + if (ret) + return ret; + + cdev_init(cdev, &ubus_fops); + ret = cdev_add(cdev, devno, UBUS_MAX_DEVICES); + if (ret) + goto out_unregister; + + cls = class_create(UBUS_CLASS_NAME); + if (IS_ERR(cls)) { + ret = PTR_ERR(cls); + goto out_del; + } + + dev = device_create(cls, NULL, devno, NULL, UBUS_DEVICE_NAME); + if (IS_ERR(dev)) { + ret = PTR_ERR(dev); + goto out_destroy; + } + + ubus_ctx.ub_class = cls; + ubus_ctx.dev = dev; + ubus_ctx.devno = devno; + return 0; + +out_destroy: + class_destroy(cls); +out_del: + cdev_del(cdev); +out_unregister: + unregister_chrdev_region(devno, UBUS_MAX_DEVICES); + return ret; +} + +void ub_cdev_uninit(void) +{ + device_destroy(ubus_ctx.ub_class, ubus_ctx.devno); + class_destroy(ubus_ctx.ub_class); + cdev_del(&ubus_ctx.cdev); + unregister_chrdev_region(ubus_ctx.devno, UBUS_MAX_DEVICES); +} diff --git a/drivers/ub/ubus/ioctl.h b/drivers/ub/ubus/ioctl.h new file mode 100644 index 000000000000..139a5bfc0112 --- /dev/null +++ b/drivers/ub/ubus/ioctl.h @@ -0,0 +1,12 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Copyright (c) HiSilicon Technologies Co., Ltd. 2025. All rights reserved. + */ + +#ifndef __IOCTL_H__ +#define __IOCTL_H__ + +int ub_cdev_init(void); +void ub_cdev_uninit(void); + +#endif /* __IOCTL_H__ */ diff --git a/include/uapi/ub/ubus/ubus.h b/include/uapi/ub/ubus/ubus.h new file mode 100644 index 000000000000..c2de27e016db --- /dev/null +++ b/include/uapi/ub/ubus/ubus.h @@ -0,0 +1,31 @@ +/* SPDX-License-Identifier: GPL-2.0+ WITH Linux-syscall-note */ +/* + * Copyright (c) HiSilicon Technologies Co., Ltd. 2025. All rights reserved. + */ + +#ifndef _UAPI_UB_UBUS_UBUS_H_ +#define _UAPI_UB_UBUS_UBUS_H_ + +#include +#include + +#define UBUS_API_VERSION 0 + +/* Kernel & User level defines for UBUS IOCTLs. */ + +#define UBUS_TYPE ('U') + +/* -------- IOCTLs for UBUS file descriptor (/dev/unified_bus) -------- */ + +/** + * UBUS_GET_API_VERSION - _IO(UBUS_TYPE, 0) + * + * Report the version of the UBUS API. This allows us to bump the entire + * API version should we later need to add or change features in incompatible + * ways. + * Return: UBUS_API_VERSION + * Availability: Always + */ +#define UBUS_IOCTL_GET_API_VERSION _IO(UBUS_TYPE, 0) + +#endif /* _UAPI_UB_UBUS_UBUS_H_ */ -- Gitee From 318920161d2ae0f59105298626751664f0d389bf Mon Sep 17 00:00:00 2001 From: Junlong Zheng Date: Sat, 20 Sep 2025 14:05:19 +0800 Subject: [PATCH 41/45] ub:ubus: Support for Bus EID-UPI Table Configuration driver inclusion category: feature bugzilla: https://gitee.com/openeuler/kernel/issues/ID34DG CVE: NA ----------------------------------------------------------- Implement the initialization and deinitialization of the EID-UPI table, as well as the corresponding configuration table item interfaces. Signed-off-by: Junlong Zheng --- drivers/ub/ubus/Makefile | 2 +- drivers/ub/ubus/eu.c | 149 ++++++++++++++++++++++++++++++ drivers/ub/ubus/eu.h | 29 ++++++ drivers/ub/ubus/ubus_controller.h | 3 + drivers/ub/ubus/ubus_entity.c | 3 + include/ub/ubus/ubus.h | 3 + 6 files changed, 188 insertions(+), 1 deletion(-) create mode 100644 drivers/ub/ubus/eu.c create mode 100644 drivers/ub/ubus/eu.h diff --git a/drivers/ub/ubus/Makefile b/drivers/ub/ubus/Makefile index 9419727937bb..36ab665f4f84 100644 --- a/drivers/ub/ubus/Makefile +++ b/drivers/ub/ubus/Makefile @@ -4,7 +4,7 @@ obj-$(CONFIG_UB_UBUS) += ub-driver.o controller.o config.o entity.o ras.o obj-$(CONFIG_UB_UBUS) += msi/ ubus-y := ubus_driver.o sysfs.o ubus_controller.o msg.o ubus_config.o port.o cc.o eid.o cna.o route.o -ubus-y += enum.o resource.o ubus_entity.o reset.o cap.o interrupt.o decoder.o omm.o ioctl.o +ubus-y += enum.o resource.o ubus_entity.o reset.o cap.o interrupt.o decoder.o omm.o ioctl.o eu.o ubus-y += services/ras.o diff --git a/drivers/ub/ubus/eu.c b/drivers/ub/ubus/eu.c new file mode 100644 index 000000000000..25e798696c8c --- /dev/null +++ b/drivers/ub/ubus/eu.c @@ -0,0 +1,149 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (c) HiSilicon Technologies Co., Ltd. 2025. All rights reserved. + */ + +#define pr_fmt(fmt) "ubus eu: " fmt + +#include + +#include "ubus.h" +#include "ubus_controller.h" +#include "eu.h" + +/* + * The ubus driver provides only the eid-upi table memory. + * The table configuration policy is developed by the vendor. + * Implemented in a unified manner in the future. + */ + +static int ub_eu_table_init_common(struct ub_entity *uent) +{ + struct ub_bus_controller *ubc = uent->ubc; + struct ub_eu_table *eu; + u32 entries, cfg_ret; + int ret; + + ret = ub_cfg_read_dword(uent, UB_EU_TEN, &entries); + if (ret) { + ub_err(uent, "EU TABLE get fail, ret=%d\n", ret); + return ret; + } + if (entries == 0) { + ub_err(uent, "EID-UPI Table Entry Num is 0\n"); + return -EINVAL; + } + + ub_info(uent, "EU TABLE entries num is %#x\n", entries); + + eu = kzalloc(sizeof(*eu), GFP_KERNEL); + if (!eu) + return -ENOMEM; + + eu->entries = entries; + eu->size = entries * EU_ENTRY_SIZE; + eu->addr = dma_alloc_coherent(&ubc->dev, eu->size, &eu->dma_addr, + GFP_KERNEL); + if (!eu->addr) { + ret = -ENOMEM; + goto free; + } + + cfg_ret = (u32)ub_cfg_write_dword(uent, UB_EU_TBA_L, + lower_32_bits(eu->dma_addr)); + cfg_ret |= (u32)ub_cfg_write_dword(uent, UB_EU_TBA_H, + upper_32_bits(eu->dma_addr)); + if (cfg_ret) { + ret = (int)cfg_ret; + ub_err(uent, "EU TABLE set fail, ret=%d\n", ret); + goto dma_free; + } + + memset(eu->addr, 0, eu->size); + uent->eu_table = eu; + + return 0; + +dma_free: + dma_free_coherent(&ubc->dev, eu->size, eu->addr, eu->dma_addr); +free: + kfree(eu); + return ret; +} + +static void ub_eu_table_uninit_common(struct ub_entity *uent) +{ + struct ub_eu_table *eu = uent->eu_table; + struct ub_bus_controller *ubc = uent->ubc; + u32 ret; + + uent->eu_table = NULL; + + ret = (u32)ub_cfg_write_dword(uent, UB_EU_TBA_L, 0); + ret |= (u32)ub_cfg_write_dword(uent, UB_EU_TBA_H, 0); + if (ret) + ub_err(uent, "EU TABLE unset fail, ret=%d\n", (int)ret); + + dma_free_coherent(&ubc->dev, eu->size, eu->addr, eu->dma_addr); + kfree(eu); +} + +void ub_eu_table_init(struct ub_entity *uent) +{ + struct ub_bus_controller *ubc = uent->ubc; + int ret; + + if (!is_ibus_controller(uent)) + return; + + ret = ub_eu_table_init_common(uent); + if (ret) + return; + + if (ubc->ops && ubc->ops->eu_table_init) { + ret = ubc->ops->eu_table_init(ubc); + if (ret) { + ub_err(uent, "EU TABLE private init fail, ret=%d\n", + ret); + goto uninit_common; + } + } + + ub_cfg_write_byte(uent, UB_TH_EN, 1); + + return; + +uninit_common: + ub_eu_table_uninit_common(uent); +} + +void ub_eu_table_uninit(struct ub_entity *uent) +{ + struct ub_bus_controller *ubc = uent->ubc; + + if (!uent->eu_table) + return; + + ub_cfg_write_byte(uent, UB_TH_EN, 0); + + if (ubc->ops && ubc->ops->eu_table_uninit) + ubc->ops->eu_table_uninit(ubc); + + ub_eu_table_uninit_common(uent); +} + +int ub_cfg_eu_table(struct ub_bus_controller *ubc, bool flag, u32 eid, u16 upi) +{ + struct ub_bus_controller_ops *ops = ubc->ops; + int ret; + + if (!ops || !ops->eu_cfg) + return -ENODEV; + + ret = ops->eu_cfg(ubc, flag, eid, upi); + if (ret) + dev_err(&ubc->dev, "eu %s fail, eid[%#05x]<->upi[%#04x]\n", + flag ? "add" : "del", eid, upi); + + return ret; +} diff --git a/drivers/ub/ubus/eu.h b/drivers/ub/ubus/eu.h new file mode 100644 index 000000000000..cc527feaacd5 --- /dev/null +++ b/drivers/ub/ubus/eu.h @@ -0,0 +1,29 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Copyright (c) HiSilicon Technologies Co., Ltd. 2025. All rights reserved. + */ +#ifndef __EU_H__ +#define __EU_H__ + +struct ub_eu_table { /* eid-upi table */ + dma_addr_t dma_addr; + void *addr; + size_t size; + u32 entries; + void *private_data; /* For ubc vendor private */ +}; + +#pragma pack(2) +struct ub_eu_entry { + u32 eid[SZ_4]; + u16 upi; +}; +#pragma pack() + +#define EU_ENTRY_SIZE 18 + +void ub_eu_table_init(struct ub_entity *uent); +void ub_eu_table_uninit(struct ub_entity *uent); +int ub_cfg_eu_table(struct ub_bus_controller *ubc, bool flag, u32 eid, u16 upi); + +#endif /* __EU_H__ */ diff --git a/drivers/ub/ubus/ubus_controller.h b/drivers/ub/ubus/ubus_controller.h index 31a79840966d..0e956a3c431e 100644 --- a/drivers/ub/ubus/ubus_controller.h +++ b/drivers/ub/ubus/ubus_controller.h @@ -8,6 +8,9 @@ struct ub_bus_controller; struct ub_bus_controller_ops { + int (*eu_table_init)(struct ub_bus_controller *ubc); + void (*eu_table_uninit)(struct ub_bus_controller *ubc); + int (*eu_cfg)(struct ub_bus_controller *ubc, bool flag, u32 eid, u16 upi); int (*entity_enable)(struct ub_entity *uent, u8 enable); }; diff --git a/drivers/ub/ubus/ubus_entity.c b/drivers/ub/ubus/ubus_entity.c index d7be521e1772..a7a076670218 100644 --- a/drivers/ub/ubus/ubus_entity.c +++ b/drivers/ub/ubus/ubus_entity.c @@ -21,6 +21,7 @@ #include "ubus_driver.h" #include "ubus_inner.h" #include "cap.h" +#include "eu.h" #include "ubus_entity.h" /* @@ -309,10 +310,12 @@ EXPORT_SYMBOL_GPL(ub_setup_ent); static void ub_configure_ent(struct ub_entity *uent) { + ub_eu_table_init(uent); } static void ub_unconfigure_ent(struct ub_entity *uent) { + ub_eu_table_uninit(uent); } static int ub_ue_sort_by_ent_idx(void *priv, const struct list_head *a, diff --git a/include/ub/ubus/ubus.h b/include/ub/ubus/ubus.h index 09fe5395db41..1f70cfbbcc0f 100644 --- a/include/ub/ubus/ubus.h +++ b/include/ub/ubus/ubus.h @@ -229,6 +229,9 @@ struct ub_entity { /* UB saved config space */ u32 saved_config_space[24]; /* Config space saved at reset time */ + /* entity bus instance info */ + struct ub_eu_table *eu_table; + u32 support_feature; u16 upi; -- Gitee From 8c9557fdd9016ed0e8036173353ec03732df2ee6 Mon Sep 17 00:00:00 2001 From: Yuhao Xiang Date: Tue, 26 Aug 2025 18:25:55 +0800 Subject: [PATCH 42/45] ub:ubus: Support for page-range table address mapping and unmapping driver inclusion category: feature bugzilla: https://gitee.com/openeuler/kernel/issues/ID34DG CVE: NA ----------------------------------------------------------- Construct address mapping and populate the page table with information retrieved from the decoder hardware lookup. Clear the established mapping relationships. Signed-off-by: Yuhao Xiang --- drivers/ub/ubus/omm.c | 228 +++++++++++++++++++++++++++++++++++++++++- drivers/ub/ubus/omm.h | 4 + 2 files changed, 229 insertions(+), 3 deletions(-) diff --git a/drivers/ub/ubus/omm.c b/drivers/ub/ubus/omm.c index d4c84b585f93..f536722d29a5 100644 --- a/drivers/ub/ubus/omm.c +++ b/drivers/ub/ubus/omm.c @@ -5,6 +5,7 @@ #define pr_fmt(fmt) "ubus omm: " fmt +#include #include "decoder.h" #include "omm.h" @@ -94,28 +95,249 @@ struct range_table_entry { u64 reserve11 : 12; }; -#define DECODER_PAGE_TABLE_LOC 35 -#define DECODER_PAGE_TABLE_MASK GENMASK_ULL(43, 35) +#define UBA_ADDR_OFFSET 12 + +#define DECODER_PAGE_INDEX_LOC 20 +#define DECODER_PAGE_TABLE_LOC 35 +#define DECODER_SUB_PAGE_TABLE_LOC 32 +#define DECODER_PAGE_TABLE_MASK GENMASK_ULL(43, 35) +#define DECODER_SUB_PAGE_TABLE_MASK GENMASK_ULL(34, 32) +#define DECODER_PGTLB_PAGE_MASK GENMASK_ULL(31, 20) +#define DECODER_RGTLB_PAGE_MASK GENMASK_ULL(34, 20) +#define DECODER_RGTLB_ADDRESS_MASK GENMASK_ULL(34, 20) +#define DECODER_RGTLB_ADDRESS_OFFSET 20 +#define TOKEN_VALID_MASK GENMASK(0, 0) #define get_pgtlb_idx(decoder, pa) ((((pa) - (decoder)->mmio_base_addr) & \ DECODER_PAGE_TABLE_MASK) >> \ DECODER_PAGE_TABLE_LOC) +#define get_rgtlb_addr(decoder, pa) ((((pa) - (decoder)->mmio_base_addr) & \ + DECODER_RGTLB_ADDRESS_MASK) >> \ + DECODER_RGTLB_ADDRESS_OFFSET) + +#define get_page_idx(decoder, pa) ((((pa) - (decoder)->mmio_base_addr) & \ + DECODER_PGTLB_PAGE_MASK) >> \ + DECODER_PAGE_INDEX_LOC) + +#define get_pgtlb_sub_idx(decoder, pa) ((((pa) - (decoder)->mmio_base_addr) & \ + DECODER_SUB_PAGE_TABLE_MASK) >> \ + DECODER_SUB_PAGE_TABLE_LOC) + +static void fill_page_entry(struct page_entry *page, + struct decoder_map_info *info, u64 offset) +{ + page->entry_type = PAGE; + page->token_vld = ubc_feature & TOKEN_VALID_MASK; + page->ub_addr = (info->uba + offset) >> UBA_ADDR_OFFSET; + page->tpg_num = info->tpg_num; + page->order_type = info->order_type; + page->order_id = info->order_id; + page->dst_eid_low = info->eid_low; + page->dst_eid_high = info->eid_high; + page->token_id = info->token_id; + page->token_value = info->token_value; + page->upi = info->upi; + page->src_eid = info->src_eid; +} + static int handle_range_table(struct ub_decoder *decoder, u64 *offset, struct decoder_map_info *info, bool is_map) { return 0; } +static int pgtlb_alloc_page(struct ub_decoder *decoder, + struct page_table_desc *desc, + struct page_table_entry *pgtlb_entry) +{ + void *page_base; + + page_base = dmam_alloc_coherent(decoder->dev, PAGE_TABLE_PAGE_SIZE, + &desc->page_dma, GFP_KERNEL); + if (!page_base) + return -ENOMEM; + + desc->page_base = page_base; + desc->count = 0; + pgtlb_entry->entry_type = PAGE_TABLE; + pgtlb_entry->next_lv_addr = (desc->page_dma & + DECODER_PGTBL_PGPRT_MASK) >> + DECODER_DMA_PAGE_ADDR_OFFSET; + pgtlb_entry->pgtlb_attr = PGTLB_ATTR_DEFAULT; + return 0; +} + +static void pgtlb_free_page(struct ub_decoder *decoder, + struct page_table_desc *desc, + struct page_table_entry *pgtlb_entry) +{ + dmam_free_coherent(decoder->dev, PAGE_TABLE_PAGE_SIZE, desc->page_base, + desc->page_dma); + pgtlb_entry->next_lv_addr = decoder->invalid_page_dma; + pgtlb_entry->entry_type = PAGE_TABLE; + desc->page_base = NULL; + desc->page_dma = 0; +} + +static int pgtlb_map_to_page(struct ub_decoder *decoder, + struct page_table_desc *desc, + struct page_table_entry *pgtlb_entry, + struct decoder_map_info *info, u64 offset) +{ + struct page_entry *page; + u32 index; + int ret; + + if (!desc->page_base) { + ret = pgtlb_alloc_page(decoder, desc, pgtlb_entry); + if (ret) + return ret; + } + + index = get_page_idx(decoder, info->pa + offset); + + page = (struct page_entry *)desc->page_base + index; + + /* + * If it's the first time to apply for a page table, + * the error branch will not be taken. + */ + if (page->entry_type != INVALID_ENTRY) { + ub_err(decoder->uent, "decoder mapping page from page table error.\n"); + return -EINVAL; + } + + fill_page_entry(page, info, offset); + desc->count++; + return 0; +} + +static int pgtlb_unmap_to_page(struct ub_decoder *decoder, + struct page_table_desc *desc, + struct page_table_entry *pgtlb_entry, + struct decoder_map_info *info, u64 offset) +{ + struct page_entry *page; + u32 index; + + index = get_page_idx(decoder, info->pa + offset); + page = (struct page_entry *)desc->page_base + index; + + if (pgtlb_entry->next_lv_addr == decoder->invalid_page_dma || + page->entry_type != PAGE) { + ub_err(decoder->uent, "decoder unmap page from page table error.\n"); + return -EINVAL; + } + + page->entry_type = INVALID_ENTRY; + desc->count--; + + if (desc->count == 0) + pgtlb_free_page(decoder, desc, pgtlb_entry); + return 0; +} + static int handle_page_table(struct ub_decoder *decoder, u64 *offset, struct decoder_map_info *info, bool is_map) { - return 0; + struct page_table_entry *pgtlb_entry; + struct page_table_desc *desc; + struct page_table *pgtlb; + u32 table_idx; + u32 sub_index; + int ret; + + pgtlb = &decoder->pgtlb; + table_idx = get_pgtlb_idx(decoder, info->pa + *offset); + sub_index = get_pgtlb_sub_idx(decoder, info->pa + *offset); + desc = pgtlb->desc_base + table_idx * RGTLB_TO_PGTLB + sub_index; + pgtlb_entry = (struct page_table_entry *)pgtlb->pgtlb_base + + table_idx * RGTLB_TO_PGTLB + sub_index; + + if (is_map) + ret = pgtlb_map_to_page(decoder, desc, pgtlb_entry, info, + *offset); + else + ret = pgtlb_unmap_to_page(decoder, desc, pgtlb_entry, info, + *offset); + *offset += SZ_1M; + return ret; +} + +#define MEM_LMT_MAX 0x7FFF +#define RANGE_UBA_LOW_MASK GENMASK_ULL(34, 20) +#define RANGE_UBA_HIGH_MASK GENMASK_ULL(63, 35) +#define UBA_CARRY 0x800000000 +#define UBA_NOCARRY 0x0 + +static void fill_range_table(struct ub_decoder *decoder, + struct range_table_entry *rg_entry, + struct decoder_map_info *info, u64 *offset) +{ + u64 mem_base, mem_limit; + u64 uba_high, uba_low; + u64 size; + + mem_base = get_rgtlb_addr(decoder, info->pa + *offset); + size = (MEM_LMT_MAX - mem_base + 1ULL) << DECODER_RGTLB_ADDRESS_OFFSET; + + uba_low = (info->uba + *offset) & RANGE_UBA_LOW_MASK; + uba_high = (info->uba + *offset) & RANGE_UBA_HIGH_MASK; + + if (info->size - *offset >= size) { + mem_limit = MEM_LMT_MAX; + *offset += size; + } else { + mem_limit = get_rgtlb_addr(decoder, info->pa + info->size); + mem_limit--; + *offset += (mem_limit - mem_base + 1) << + DECODER_RGTLB_ADDRESS_OFFSET; + } + + rg_entry->entry_type = RANGE_TABLE; + rg_entry->next_lv_addr = decoder->invalid_page_dma; + rg_entry->page_table_attr = PGTLB_ATTR_DEFAULT; + rg_entry->token_vld = ubc_feature & TOKEN_VALID_MASK; + rg_entry->mem_base = mem_base; + rg_entry->mem_limit = mem_limit; + rg_entry->ub_addr = (uba_high | uba_low) >> UBA_ADDR_OFFSET; + rg_entry->tpg_num = info->tpg_num; + rg_entry->order_id = info->order_id; + rg_entry->order_type = info->order_type; + rg_entry->dst_eid_low = info->eid_low; + rg_entry->dst_eid_high = info->eid_high; + rg_entry->token_id = info->token_id; + rg_entry->token_value = info->token_value; + rg_entry->upi = info->upi; + rg_entry->src_eid = info->src_eid; } static int handle_pgrg_table(struct ub_decoder *decoder, u64 *offset, struct decoder_map_info *info, bool is_map) { + struct range_table_entry *rg_entry; + struct page_table_entry *pg_entry; + u32 table_idx, sub_index; + int i; + + if (!is_map) + return handle_page_table(decoder, offset, info, is_map); + + table_idx = get_pgtlb_idx(decoder, info->pa + *offset); + sub_index = get_pgtlb_sub_idx(decoder, info->pa + *offset); + pg_entry = (struct page_table_entry *)decoder->pgtlb.pgtlb_base + + table_idx * RGTLB_TO_PGTLB; + if (sub_index != 0 || info->size - *offset < decoder->rg_size) + return handle_page_table(decoder, offset, info, is_map); + + for (i = 0; i < RGTLB_TO_PGTLB; i++) + if ((pg_entry + i)->next_lv_addr != decoder->invalid_page_dma) + return handle_page_table(decoder, offset, info, is_map); + + ub_dbg(decoder->uent, "create range table\n"); + rg_entry = (struct range_table_entry *)pg_entry; + fill_range_table(decoder, rg_entry, info, offset); return 0; } diff --git a/drivers/ub/ubus/omm.h b/drivers/ub/ubus/omm.h index 4258a7609736..e90ce9cb1f7f 100644 --- a/drivers/ub/ubus/omm.h +++ b/drivers/ub/ubus/omm.h @@ -6,6 +6,10 @@ #ifndef __OMM_H__ #define __OMM_H__ +#include + +extern u8 ubc_feature; + struct decoder_map_info { phys_addr_t pa; phys_addr_t uba; -- Gitee From 2b0fb64516ca2f87189aed7b915253d80830f10c Mon Sep 17 00:00:00 2001 From: Yuhao Xiang Date: Tue, 26 Aug 2025 14:49:52 +0800 Subject: [PATCH 43/45] ub:ubus: Support for range table mapping and unmapping functions driver inclusion category: feature bugzilla: https://gitee.com/openeuler/kernel/issues/ID34DG CVE: NA ----------------------------------------------------------- Construct address mapping and populate the range table with information retrieved from the decoder hardware lookup. Clear the established mapping relationships. Signed-off-by: Yuhao Xiang --- drivers/ub/ubus/omm.c | 222 +++++++++++++++++++++++++++++++++++++++++- 1 file changed, 221 insertions(+), 1 deletion(-) diff --git a/drivers/ub/ubus/omm.c b/drivers/ub/ubus/omm.c index f536722d29a5..7cc304950209 100644 --- a/drivers/ub/ubus/omm.c +++ b/drivers/ub/ubus/omm.c @@ -116,6 +116,10 @@ struct range_table_entry { DECODER_RGTLB_ADDRESS_MASK) >> \ DECODER_RGTLB_ADDRESS_OFFSET) +#define get_rgtlb_page_idx(decoder, pa) ((((pa) - (decoder)->mmio_base_addr) & \ + DECODER_RGTLB_PAGE_MASK) >> \ + DECODER_PAGE_INDEX_LOC) + #define get_page_idx(decoder, pa) ((((pa) - (decoder)->mmio_base_addr) & \ DECODER_PGTLB_PAGE_MASK) >> \ DECODER_PAGE_INDEX_LOC) @@ -141,10 +145,226 @@ static void fill_page_entry(struct page_entry *page, page->src_eid = info->src_eid; } +static int rgtlb_alloc_page(struct ub_decoder *decoder, + struct page_table_desc *desc, + struct range_table_entry *entry) +{ + void *page_base; + + page_base = dmam_alloc_coherent(decoder->dev, RANGE_TABLE_PAGE_SIZE, + &desc->page_dma, GFP_KERNEL); + if (!page_base) + return -ENOMEM; + + desc->page_base = page_base; + desc->count = 0; + entry->next_lv_addr = (desc->page_dma & DECODER_PGTBL_PGPRT_MASK) >> + DECODER_DMA_PAGE_ADDR_OFFSET; + entry->page_table_attr = PGTLB_ATTR_DEFAULT; + return 0; +} + +static void rgtlb_free_page(struct ub_decoder *decoder, + struct page_table_desc *desc, + struct range_table_entry *rgtlb_entry) +{ + dmam_free_coherent(decoder->dev, RANGE_TABLE_PAGE_SIZE, desc->page_base, + desc->page_dma); + rgtlb_entry->next_lv_addr = decoder->invalid_page_dma; + desc->page_base = NULL; + desc->page_dma = 0; +} + +static int rgtlb_map(struct ub_decoder *decoder, u32 table_idx, + struct decoder_map_info *info, u64 *offset) +{ + struct range_table_entry *rgtlb_entry; + struct ub_entity *uent = decoder->uent; + struct page_table_desc *desc; + struct page_entry *page; + u32 index; + u64 addr; + + rgtlb_entry = (struct range_table_entry *)decoder->pgtlb.pgtlb_base + + table_idx; + desc = decoder->pgtlb.desc_base + table_idx * RGTLB_TO_PGTLB; + addr = get_rgtlb_addr(decoder, info->pa + *offset); + index = get_rgtlb_page_idx(decoder, info->pa + *offset); + if (addr >= rgtlb_entry->mem_base && addr <= rgtlb_entry->mem_limit) { + ub_err(uent, "decoder mapping addr already in range.\n"); + *offset += SZ_1M; + return -EINVAL; + } + + if (!desc->page_base) + if (rgtlb_alloc_page(decoder, desc, rgtlb_entry)) { + *offset += SZ_1M; + return -ENOMEM; + } + + /* + * If the page table is applied for the first time, + * the error branch is not used. + */ + page = (struct page_entry *)desc->page_base + index; + if (page->entry_type != INVALID_ENTRY) { + *offset += SZ_1M; + ub_err(uent, "page entry has been used\n"); + return -EINVAL; + } + + fill_page_entry(page, info, *offset); + desc->count++; + *offset += SZ_1M; + return 0; +} + +static int copy_rg_to_pg(struct ub_decoder *decoder, + struct page_table_desc *desc_base, + struct range_table_entry *rgtlb_entry, + struct page_entry *rg_base, int idx) +{ + struct page_table_entry *pgtlb_entry; + struct page_entry *rentry, *pentry; + struct page_table_desc *desc; + void *page_base; + int i; + + desc = desc_base + idx; + pgtlb_entry = (struct page_table_entry *)rgtlb_entry + idx; + page_base = dmam_alloc_coherent(decoder->dev, PAGE_TABLE_PAGE_SIZE, + &desc->page_dma, GFP_KERNEL); + if (!page_base) + return -ENOMEM; + + desc->page_base = page_base; + desc->count = 0; + pgtlb_entry->entry_type = PAGE_TABLE; + pgtlb_entry->next_lv_addr = (desc->page_dma & + DECODER_PGTBL_PGPRT_MASK) >> + DECODER_DMA_PAGE_ADDR_OFFSET; + pgtlb_entry->pgtlb_attr = PGTLB_ATTR_DEFAULT; + + pentry = (struct page_entry *)page_base; + rentry = (struct page_entry *)rg_base + idx * DECODER_PAGE_SIZE; + for (i = 0; i < DECODER_PAGE_SIZE; i++) { + if ((rentry + i)->entry_type == PAGE) { + desc->count++; + memcpy(pentry + i, rentry + i, DECODER_PAGE_ENTRY_SIZE); + } + } + return 0; +} + +static int rgtlb_unmap_to_range(struct ub_decoder *decoder, + struct page_table_desc *desc, + struct range_table_entry *rgtlb_entry) +{ + struct page_table_entry *pgtlb_entry; + struct page_entry *rg_base; + struct page_entry *entry; + dma_addr_t rg_dma; + int i, j, ret; + bool flag; + + for (i = 0; i < RGTLB_TO_PGTLB; i++) { + pgtlb_entry = (struct page_table_entry *)rgtlb_entry + i; + pgtlb_entry->entry_type = PAGE_TABLE; + pgtlb_entry->next_lv_addr = decoder->invalid_page_dma; + pgtlb_entry->pgtlb_attr = PGTLB_ATTR_DEFAULT; + } + + if (!desc->page_base) + return 0; + /* + * If l2 table exists, + * convert l2 table of range table to l2 table of the page table. + */ + rg_base = (struct page_entry *)desc->page_base; + rg_dma = desc->page_dma; + memset(desc, 0, sizeof(struct page_table_desc)); + for (i = 0; i < RGTLB_TO_PGTLB; i++) { + flag = false; + for (j = 0; j < DECODER_PAGE_SIZE; j++) { + entry = rg_base + i * DECODER_PAGE_SIZE + j; + if (entry->entry_type != INVALID_ENTRY) { + flag = true; + break; + } + } + if (flag) { + ret = copy_rg_to_pg(decoder, desc, rgtlb_entry, + rg_base, i); + if (ret) + return ret; + } + } + + dmam_free_coherent(decoder->dev, RANGE_TABLE_PAGE_SIZE, rg_base, + rg_dma); + return 0; +} + +static int rgtlb_unmap_to_page(struct ub_decoder *decoder, + struct page_table_desc *desc, + struct range_table_entry *rgtlb_entry, + u32 index) +{ + struct page_entry *page; + + page = (struct page_entry *)desc->page_base + index; + + if (page->entry_type != PAGE) { + ub_err(decoder->uent, "decoder unmap page from range table error.\n"); + return -EINVAL; + } + page->entry_type = INVALID_ENTRY; + desc->count--; + + if (desc->count == 0) + rgtlb_free_page(decoder, desc, rgtlb_entry); + return 0; +} + +static int rgtlb_unmap(struct ub_decoder *decoder, u32 table_idx, + struct decoder_map_info *info, u64 *offset) +{ + struct range_table_entry *rgtlb_entry; + struct page_table_desc *desc; + u32 sub_idx; + u64 begin; + u64 size; + + rgtlb_entry = (struct range_table_entry *)decoder->pgtlb.pgtlb_base + + table_idx; + desc = decoder->pgtlb.desc_base + table_idx * RGTLB_TO_PGTLB; + + begin = get_rgtlb_addr(decoder, info->pa + *offset); + size = (rgtlb_entry->mem_limit - rgtlb_entry->mem_base + 1ULL) * SZ_1M; + + if (begin == rgtlb_entry->mem_base) { + *offset += size; + if (info->size >= *offset) + return rgtlb_unmap_to_range(decoder, desc, rgtlb_entry); + ub_err(decoder->uent, "can't unmap part of range\n!"); + return -EINVAL; + } + sub_idx = get_rgtlb_page_idx(decoder, info->pa + *offset); + *offset += SZ_1M; + return rgtlb_unmap_to_page(decoder, desc, rgtlb_entry, sub_idx); +} + static int handle_range_table(struct ub_decoder *decoder, u64 *offset, struct decoder_map_info *info, bool is_map) { - return 0; + u32 table_idx; + + table_idx = get_pgtlb_idx(decoder, info->pa + *offset); + + if (is_map) + return rgtlb_map(decoder, table_idx, info, offset); + else + return rgtlb_unmap(decoder, table_idx, info, offset); } static int pgtlb_alloc_page(struct ub_decoder *decoder, -- Gitee From 984a9bd7d03d8d75fa011544755d55753f89202c Mon Sep 17 00:00:00 2001 From: Yuhao Xiang Date: Tue, 26 Aug 2025 20:34:00 +0800 Subject: [PATCH 44/45] ub:ubus: Support for sending decoder commands driver inclusion category: feature bugzilla: https://gitee.com/openeuler/kernel/issues/ID34DG CVE: NA ----------------------------------------------------------- Provide an interface to support writing commands to the command queue for sending to the UB decoder device. Signed-off-by: Yuhao Xiang --- drivers/ub/ubus/decoder.c | 308 +++++++++++++++++++++++++++++- drivers/ub/ubus/decoder.h | 49 ++++- drivers/ub/ubus/omm.c | 5 +- drivers/ub/ubus/ubus_controller.h | 2 + 4 files changed, 356 insertions(+), 8 deletions(-) diff --git a/drivers/ub/ubus/decoder.c b/drivers/ub/ubus/decoder.c index 366dcb24d130..4ca7a170a268 100644 --- a/drivers/ub/ubus/decoder.c +++ b/drivers/ub/ubus/decoder.c @@ -6,23 +6,57 @@ #define pr_fmt(fmt) "ubus decoder: " fmt #include +#include #include #include "ubus.h" +#include "ubus_controller.h" #include "omm.h" #include "decoder.h" -#define MMIO_SIZE_MASK GENMASK_ULL(18, 16) -#define MMIO_SIZE_OFFSET 16 +#define MMIO_SIZE_MASK GENMASK_ULL(18, 16) +#define CMDQ_SIZE_MASK GENMASK_ULL(15, 12) +#define MMIO_SIZE_OFFSET 16 +#define CMDQ_SIZE_OFFSET 12 + +#define CMDQ_SIZE_USE_MASK GENMASK(11, 8) +#define CMDQ_SIZE_USE_OFFSET 8 +#define CMDQ_ENABLE 0x1 +#define CMD_ENTRY_SIZE 16 #define DECODER_PAGE_TABLE_ENTRY_SIZE 8 +#define DECODER_QUEUE_TIMEOUT_US 1000000 /* 1s */ + static void ub_decoder_uninit_queue(struct ub_decoder *decoder) -{} +{ + iounmap(decoder->cmdq.qbase); +} static int ub_decoder_init_queue(struct ub_bus_controller *ubc, struct ub_decoder *decoder) { + struct ub_entity *uent = ubc->uent; + + if (ubc->ops->register_decoder_base_addr) { + ubc->ops->register_decoder_base_addr(ubc, &decoder->cmdq.base, + &decoder->evtq.base); + } else { + ub_err(uent, + "ub_bus_controller_ops does not provide register_decoder_base_addr func, exit\n"); + return -EINVAL; + } + + if (decoder->cmdq.qs == 0) { + ub_err(uent, "decoder cmdq qs is 0\n"); + return -EINVAL; + } + + decoder->cmdq.qbase = ioremap(decoder->cmdq.base, + (1 << decoder->cmdq.qs) * CMD_ENTRY_SIZE); + if (!decoder->cmdq.qbase) + return -ENOMEM; + return 0; } @@ -56,7 +90,28 @@ static u32 set_page_table_reg(struct ub_decoder *decoder) static u32 set_queue_reg(struct ub_decoder *decoder) { - return 0; + u32 val, ret; + + /* init decoder cmdq base addr and pi ci */ + ret = (u32)ub_cfg_write_dword(decoder->uent, DECODER_CMDQ_BASE_ADDR0, + lower_32_bits(decoder->cmdq.base)); + ret |= (u32)ub_cfg_write_dword(decoder->uent, DECODER_CMDQ_BASE_ADDR1, + upper_32_bits(decoder->cmdq.base)); + ret |= (u32)ub_cfg_write_dword(decoder->uent, DECODER_CMDQ_PROD, 0); + ret |= (u32)ub_cfg_write_dword(decoder->uent, DECODER_CMDQ_CONS, 0); + + /* set decoder cmdq conf */ + ret |= (u32)ub_cfg_read_dword(decoder->uent, DECODER_CMDQ_CFG, &val); + decoder->vals.cmdq_cfg_val = val; + val &= ~CMDQ_SIZE_USE_MASK; + val |= decoder->cmdq.qs << CMDQ_SIZE_USE_OFFSET; + val |= CMDQ_ENABLE; + ret |= (u32)ub_cfg_write_dword(decoder->uent, DECODER_CMDQ_CFG, val); + + if (ret) + ub_err(decoder->uent, "set decoder queue fail\n"); + + return ret; } static u32 set_decoder_enable(struct ub_decoder *decoder) @@ -180,6 +235,7 @@ static int ub_get_decoder_cap(struct ub_decoder *decoder) } decoder->mmio_size_sup = (val & MMIO_SIZE_MASK) >> MMIO_SIZE_OFFSET; + decoder->cmdq.qs = (val & CMDQ_SIZE_MASK) >> CMDQ_SIZE_OFFSET; return 0; } @@ -247,7 +303,19 @@ static void unset_mmio_base_reg(struct ub_decoder *decoder) } static void unset_queue_reg(struct ub_decoder *decoder) -{} +{ + struct ub_entity *uent = decoder->uent; + u32 ret; + + ret = (u32)ub_cfg_write_dword(uent, DECODER_CMDQ_CFG, + decoder->vals.cmdq_cfg_val); + + ret |= (u32)ub_cfg_write_dword(uent, DECODER_CMDQ_BASE_ADDR0, 0); + ret |= (u32)ub_cfg_write_dword(uent, DECODER_CMDQ_BASE_ADDR1, 0); + + if (ret) + ub_err(uent, "unset queue reg fail\n"); +} static void unset_page_table_reg(struct ub_decoder *decoder) { @@ -296,6 +364,236 @@ static void ub_remove_decoder(struct ub_bus_controller *ubc) ubc->decoder = NULL; } +struct sync_entry { + u64 op : 8; + u64 reserve0 : 4; + u64 cm : 2; + u64 ntf_sh : 2; + u64 ntf_attr : 4; + u64 reserve1 : 12; + u64 notify_data : 32; + u64 reserve2 : 2; + u64 notify_addr : 50; + u64 reserve3 : 12; +}; + +struct tlbi_all_entry { + u32 op : 8; + u32 reserve0 : 24; + u32 reserve1; + u32 reserve2; + u32 reserve3; +}; + +struct tlbi_partial_entry { + u32 op : 8; + u32 reserve0 : 24; + u32 tlbi_addr_base : 28; + u32 reserve1 : 4; + u32 tlbi_addr_limt : 28; + u32 reserve2 : 4; + u32 reserve3; +}; + +#define TLBI_ADDR_MASK GENMASK_ULL(43, 20) +#define TLBI_ADDR_OFFSET 20 +#define CMDQ_ENT_DWORDS 2 + +#define NTF_SH_NSH 0b00 +#define NTF_SH_OSH 0b10 +#define NTF_SH_ISH 0b11 +#define NTF_ATTR_IR_NC 0b00 +#define NTF_ATTR_IR_WBRA 0b01 +#define NTF_ATTR_IR_WT 0b10 +#define NTF_ATTR_IR_WB 0b11 +#define NTF_ATTR_OR_NC 0b0000 +#define NTF_ATTR_OR_WBRA 0b0100 +#define NTF_ATTR_OR_WT 0b1000 +#define NTF_ATTR_OR_WB 0b1100 + +#define Q_IDX(qs, p) ((p) & ((1 << (qs)) - 1)) +#define Q_WRP(qs, p) ((p) & (1 << (qs))) +#define Q_OVF(p) ((p) & Q_OVERFLOW_FLAG) + +enum NOTIFY_TYPE { + DISABLE_NOTIFY = 0, + ENABLE_NOTIFY = 1, +}; + +static bool queue_has_space(struct ub_decoder_queue *q, u32 n) +{ + u32 space, prod, cons; + + prod = Q_IDX(q->qs, q->prod.cmdq_wr_idx); + cons = Q_IDX(q->qs, q->cons.cmdq_rd_idx); + + if (Q_WRP(q->qs, q->prod.cmdq_wr_idx) == + Q_WRP(q->qs, q->cons.cmdq_rd_idx)) + space = (1 << q->qs) - (prod - cons); + else + space = cons - prod; + + return space >= n; +} + +static u32 queue_inc_prod_n(struct ub_decoder_queue *q, u32 n) +{ + u32 prod = (Q_WRP(q->qs, q->prod.cmdq_wr_idx) | + Q_IDX(q->qs, q->prod.cmdq_wr_idx)) + n; + return Q_WRP(q->qs, prod) | Q_IDX(q->qs, prod); +} + +#define CMD_0_OP GENMASK_ULL(7, 0) +#define CMD_0_ADDR_BASE GENMASK_ULL(59, 32) +#define CMD_1_ADDR_LIMT GENMASK_ULL(27, 0) + +static void decoder_cmdq_issue_cmd(struct ub_decoder *decoder, phys_addr_t addr, + u64 size, enum ub_cmd_op_type op) +{ + struct ub_decoder_queue *cmdq = &(decoder->cmdq); + struct tlbi_partial_entry entry = {}; + u64 cmd[CMDQ_ENT_DWORDS] = {}; + void *pi; + int i; + + entry.op = op; + entry.tlbi_addr_base = (addr & TLBI_ADDR_MASK) >> TLBI_ADDR_OFFSET; + entry.tlbi_addr_limt = ((addr + size - 1U) & TLBI_ADDR_MASK) >> + TLBI_ADDR_OFFSET; + + cmd[0] |= FIELD_PREP(CMD_0_OP, entry.op); + cmd[0] |= FIELD_PREP(CMD_0_ADDR_BASE, entry.tlbi_addr_base); + cmd[1] |= FIELD_PREP(CMD_1_ADDR_LIMT, entry.tlbi_addr_limt); + + pi = cmdq->qbase + Q_IDX(cmdq->qs, cmdq->prod.cmdq_wr_idx) * + sizeof(struct tlbi_partial_entry); + + for (i = 0; i < CMDQ_ENT_DWORDS; i++) + writeq(cmd[i], pi + i * sizeof(u64)); + + cmdq->prod.cmdq_wr_idx = queue_inc_prod_n(cmdq, 1); +} + +#define NTF_DMA_ADDR_OFFSERT 2 +#define SYNC_0_OP GENMASK_ULL(7, 0) +#define SYNC_0_CM GENMASK_ULL(13, 12) +#define SYNC_0_NTF_ISH GENMASK_ULL(15, 14) +#define SYNC_0_NTF_ATTR GENMASK_ULL(19, 16) +#define SYNC_0_NTF_DATA GENMASK_ULL(63, 32) +#define SYNC_1_NTF_ADDR GENMASK_ULL(51, 2) +#define SYNC_NTF_DATA 0xffffffff + +static void decoder_cmdq_issue_sync(struct ub_decoder *decoder) +{ + struct ub_decoder_queue *cmdq = &(decoder->cmdq); + u64 cmd[CMDQ_ENT_DWORDS] = {}; + struct sync_entry entry = {}; + phys_addr_t sync_dma; + void __iomem *pi; + int i; + + entry.op = SYNC; + entry.cm = ENABLE_NOTIFY; + sync_dma = cmdq->base + Q_IDX(cmdq->qs, cmdq->prod.cmdq_wr_idx) * + sizeof(struct sync_entry); + entry.ntf_sh = NTF_SH_NSH; + entry.ntf_attr = NTF_ATTR_IR_NC | NTF_ATTR_OR_NC; + entry.notify_data = SYNC_NTF_DATA; + entry.notify_addr = sync_dma >> NTF_DMA_ADDR_OFFSERT; + + cmd[0] |= FIELD_PREP(SYNC_0_OP, entry.op); + cmd[0] |= FIELD_PREP(SYNC_0_CM, entry.cm); + cmd[0] |= FIELD_PREP(SYNC_0_NTF_ISH, entry.ntf_sh); + cmd[0] |= FIELD_PREP(SYNC_0_NTF_ATTR, entry.ntf_attr); + cmd[0] |= FIELD_PREP(SYNC_0_NTF_DATA, entry.notify_data); + cmd[1] |= FIELD_PREP(SYNC_1_NTF_ADDR, entry.notify_addr); + + pi = cmdq->qbase + Q_IDX(cmdq->qs, cmdq->prod.cmdq_wr_idx) * + sizeof(struct sync_entry); + for (i = 0; i < CMDQ_ENT_DWORDS; i++) + writeq(cmd[i], pi + i * sizeof(u64)); + + decoder->notify = pi; + cmdq->prod.cmdq_wr_idx = queue_inc_prod_n(cmdq, 1); +} + +static void decoder_cmdq_update_prod(struct ub_decoder *decoder) +{ + struct ub_entity *uent = decoder->uent; + struct queue_idx q; + int ret; + + ret = ub_cfg_read_dword(uent, DECODER_CMDQ_PROD, &q.val); + if (ret) + ub_err(uent, "update pi, read decoder cmdq prod fail\n"); + + decoder->cmdq.prod.cmdq_err_resp = q.cmdq_err_resp; + ret = ub_cfg_write_dword(uent, DECODER_CMDQ_PROD, + decoder->cmdq.prod.val); + if (ret) + ub_err(uent, "update pi, write decoder cmdq prod fail\n"); +} + +static int wait_for_cmdq_free(struct ub_decoder *decoder, u32 n) +{ + ktime_t timeout = ktime_add_us(ktime_get(), DECODER_QUEUE_TIMEOUT_US); + struct ub_decoder_queue *cmdq = &(decoder->cmdq); + struct ub_entity *uent = decoder->uent; + int ret; + + while (true) { + ret = ub_cfg_read_dword(uent, DECODER_CMDQ_CONS, + &(cmdq->cons.val)); + if (ret) + return ret; + + if (queue_has_space(cmdq, n + 1)) + return 0; + + if (ktime_compare(ktime_get(), timeout) > 0) { + ub_err(uent, "decoder cmdq wait free entry timeout\n"); + return -ETIMEDOUT; + } + cpu_relax(); + } +} + +static int wait_for_cmdq_notify(struct ub_decoder *decoder) +{ + ktime_t timeout; + u32 val; + + timeout = ktime_add_us(ktime_get(), DECODER_QUEUE_TIMEOUT_US); + while (true) { + val = readl(decoder->notify); + if (val == SYNC_NTF_DATA) + return 0; + + if (ktime_compare(ktime_get(), timeout) > 0) { + ub_err(decoder->uent, "decoder cmdq wait notify timeout\n"); + return -ETIMEDOUT; + } + cpu_relax(); + } +} + +int ub_decoder_cmd_request(struct ub_decoder *decoder, phys_addr_t addr, + u64 size, enum ub_cmd_op_type op) +{ + int ret; + + ret = wait_for_cmdq_free(decoder, 1); + if (ret) + return ret; + + decoder_cmdq_issue_cmd(decoder, addr, size, op); + decoder_cmdq_issue_sync(decoder); + decoder_cmdq_update_prod(decoder); + + ret = wait_for_cmdq_notify(decoder); + return ret; +} + void ub_decoder_init(struct ub_entity *uent) { int ret; diff --git a/drivers/ub/ubus/decoder.h b/drivers/ub/ubus/decoder.h index 6b0fcee2681e..c2d623244bcc 100644 --- a/drivers/ub/ubus/decoder.h +++ b/drivers/ub/ubus/decoder.h @@ -8,6 +8,49 @@ #include +enum ub_cmd_op_type { + INVALID = 0x0, + SYNC = 0x1, + TLBI_ALL = 0x2, + TLBI_PARTIAL = 0x3, +}; + +struct queue_idx { + union { + u32 val; + struct { + u32 cmdq_wr_idx : 11; + u32 reserved1 : 5; + u32 cmdq_err_resp : 1; + u32 reserved2 : 15; + }; + struct { + u32 cmdq_rd_idx : 11; + u32 reserved3 : 5; + u32 cmdq_err : 1; + u32 cmdq_err_reason : 3; + u32 reserved4 : 12; + }; + struct { + u32 eventq_wr_idx : 11; + u32 reserved5 : 21; + }; + struct { + u32 eventq_rd_idx : 11; + u32 reserved6 : 20; + u32 eventq_ovfl_err_resp : 1; + }; + }; +}; + +struct ub_decoder_queue { + phys_addr_t base; + void __iomem *qbase; + u32 qs; + struct queue_idx prod; + struct queue_idx cons; +}; + struct reg_default_val { u32 cmdq_cfg_val; u32 evtq_cfg_val; @@ -31,8 +74,11 @@ struct ub_decoder { phys_addr_t mmio_base_addr; u32 mmio_size_sup; u64 rg_size; + struct ub_decoder_queue cmdq; + struct ub_decoder_queue evtq; struct page_table pgtlb; struct reg_default_val vals; + void __iomem *notify; int irq_num; struct page_table_desc invalid_desc; @@ -71,5 +117,6 @@ struct ub_decoder { void ub_decoder_init(struct ub_entity *uent); void ub_decoder_uninit(struct ub_entity *uent); - +int ub_decoder_cmd_request(struct ub_decoder *decoder, phys_addr_t addr, + u64 size, enum ub_cmd_op_type op); #endif /* __DECODER_H__ */ diff --git a/drivers/ub/ubus/omm.c b/drivers/ub/ubus/omm.c index 7cc304950209..b8b59a9da4f1 100644 --- a/drivers/ub/ubus/omm.c +++ b/drivers/ub/ubus/omm.c @@ -616,8 +616,9 @@ int ub_decoder_unmap(struct ub_decoder *decoder, phys_addr_t addr, u64 size) if (size < SZ_1M) size = SZ_1M; ret = handle_table(decoder, &info, false); - - return ret; + if (ret) + return ret; + return ub_decoder_cmd_request(decoder, addr, size, TLBI_PARTIAL); } int ub_decoder_map(struct ub_decoder *decoder, struct decoder_map_info *info) diff --git a/drivers/ub/ubus/ubus_controller.h b/drivers/ub/ubus/ubus_controller.h index 0e956a3c431e..8637da594ada 100644 --- a/drivers/ub/ubus/ubus_controller.h +++ b/drivers/ub/ubus/ubus_controller.h @@ -11,6 +11,8 @@ struct ub_bus_controller_ops { int (*eu_table_init)(struct ub_bus_controller *ubc); void (*eu_table_uninit)(struct ub_bus_controller *ubc); int (*eu_cfg)(struct ub_bus_controller *ubc, bool flag, u32 eid, u16 upi); + void (*register_decoder_base_addr)(struct ub_bus_controller *ubc, + u64 *cmd_queue, u64 *event_queue); int (*entity_enable)(struct ub_entity *uent, u8 enable); }; -- Gitee From 0c8b07a9ca1eddf1d8387bdbefc4f4a1b61f0922 Mon Sep 17 00:00:00 2001 From: Yuhao Xiang Date: Tue, 26 Aug 2025 20:38:22 +0800 Subject: [PATCH 45/45] ub:ubus: Supports decoder event processing driver inclusion category: feature bugzilla: https://gitee.com/openeuler/kernel/issues/ID34DG CVE: NA ----------------------------------------------------------- Support processing event notifications sent by the UB decoder device. Signed-off-by: Yuhao Xiang --- drivers/ub/ubus/decoder.c | 225 +++++++++++++++++++++++++++++++++++++- drivers/ub/ubus/decoder.h | 2 + 2 files changed, 225 insertions(+), 2 deletions(-) diff --git a/drivers/ub/ubus/decoder.c b/drivers/ub/ubus/decoder.c index 4ca7a170a268..7e4b7ef2faff 100644 --- a/drivers/ub/ubus/decoder.c +++ b/drivers/ub/ubus/decoder.c @@ -6,6 +6,8 @@ #define pr_fmt(fmt) "ubus decoder: " fmt #include +#include +#include #include #include @@ -16,14 +18,21 @@ #define MMIO_SIZE_MASK GENMASK_ULL(18, 16) #define CMDQ_SIZE_MASK GENMASK_ULL(15, 12) +#define EVTQ_SIZE_MASK GENMASK_ULL(7, 4) #define MMIO_SIZE_OFFSET 16 #define CMDQ_SIZE_OFFSET 12 +#define EVTQ_SIZE_OFFSET 4 #define CMDQ_SIZE_USE_MASK GENMASK(11, 8) #define CMDQ_SIZE_USE_OFFSET 8 #define CMDQ_ENABLE 0x1 #define CMD_ENTRY_SIZE 16 +#define EVTQ_SIZE_USE_MASK GENMASK(11, 8) +#define EVTQ_SIZE_USE_OFFSET 8 +#define EVTQ_ENABLE 0x1 +#define EVT_ENTRY_SIZE 16 + #define DECODER_PAGE_TABLE_ENTRY_SIZE 8 #define DECODER_QUEUE_TIMEOUT_US 1000000 /* 1s */ @@ -31,6 +40,7 @@ static void ub_decoder_uninit_queue(struct ub_decoder *decoder) { iounmap(decoder->cmdq.qbase); + iounmap(decoder->evtq.qbase); } static int ub_decoder_init_queue(struct ub_bus_controller *ubc, @@ -47,8 +57,8 @@ static int ub_decoder_init_queue(struct ub_bus_controller *ubc, return -EINVAL; } - if (decoder->cmdq.qs == 0) { - ub_err(uent, "decoder cmdq qs is 0\n"); + if (decoder->cmdq.qs == 0 || decoder->evtq.qs == 0) { + ub_err(uent, "decoder cmdq or evtq qs is 0\n"); return -EINVAL; } @@ -57,6 +67,12 @@ static int ub_decoder_init_queue(struct ub_bus_controller *ubc, if (!decoder->cmdq.qbase) return -ENOMEM; + decoder->evtq.qbase = ioremap(decoder->evtq.base, + (1 << decoder->evtq.qs) * EVT_ENTRY_SIZE); + if (!decoder->evtq.qbase) { + iounmap(decoder->cmdq.qbase); + return -ENOMEM; + } return 0; } @@ -108,6 +124,24 @@ static u32 set_queue_reg(struct ub_decoder *decoder) val |= CMDQ_ENABLE; ret |= (u32)ub_cfg_write_dword(decoder->uent, DECODER_CMDQ_CFG, val); + /* init decoder eventq base addr and pi ci */ + ret |= (u32)ub_cfg_write_dword(decoder->uent, DECODER_EVENTQ_BASE_ADDR0, + lower_32_bits(decoder->evtq.base)); + ret |= (u32)ub_cfg_write_dword(decoder->uent, DECODER_EVENTQ_BASE_ADDR1, + upper_32_bits(decoder->evtq.base)); + ret |= (u32)ub_cfg_write_dword(decoder->uent, DECODER_EVENTQ_PROD, 0); + ret |= (u32)ub_cfg_write_dword(decoder->uent, DECODER_EVENTQ_CONS, 0); + + /* set decoder eventq conf */ + ret |= (u32)ub_cfg_read_dword(decoder->uent, DECODER_EVENTQ_CFG, &val); + decoder->vals.evtq_cfg_val = val; + val &= ~EVTQ_SIZE_USE_MASK; + val |= decoder->evtq.qs << EVTQ_SIZE_USE_OFFSET; + val |= EVTQ_ENABLE; + ret |= (u32)ub_cfg_write_dword(decoder->uent, DECODER_EVENTQ_CFG, val); + + decoder->cmdq.prod.val = 0; + decoder->evtq.cons.val = 0; if (ret) ub_err(decoder->uent, "set decoder queue fail\n"); @@ -236,7 +270,11 @@ static int ub_get_decoder_cap(struct ub_decoder *decoder) decoder->mmio_size_sup = (val & MMIO_SIZE_MASK) >> MMIO_SIZE_OFFSET; decoder->cmdq.qs = (val & CMDQ_SIZE_MASK) >> CMDQ_SIZE_OFFSET; + decoder->evtq.qs = (val & EVTQ_SIZE_MASK) >> EVTQ_SIZE_OFFSET; + ub_dbg(uent, "cmdq_queue_size=%u, evtq_queue_size=%u, mmio_size=%s\n", + decoder->cmdq.qs, decoder->evtq.qs, + mmio_size_desc[decoder->mmio_size_sup]); return 0; } @@ -309,10 +347,14 @@ static void unset_queue_reg(struct ub_decoder *decoder) ret = (u32)ub_cfg_write_dword(uent, DECODER_CMDQ_CFG, decoder->vals.cmdq_cfg_val); + ret |= (u32)ub_cfg_write_dword(uent, DECODER_EVENTQ_CFG, + decoder->vals.evtq_cfg_val); ret |= (u32)ub_cfg_write_dword(uent, DECODER_CMDQ_BASE_ADDR0, 0); ret |= (u32)ub_cfg_write_dword(uent, DECODER_CMDQ_BASE_ADDR1, 0); + ret |= (u32)ub_cfg_write_dword(uent, DECODER_EVENTQ_BASE_ADDR0, 0); + ret |= (u32)ub_cfg_write_dword(uent, DECODER_EVENTQ_BASE_ADDR1, 0); if (ret) ub_err(uent, "unset queue reg fail\n"); } @@ -594,6 +636,185 @@ int ub_decoder_cmd_request(struct ub_decoder *decoder, phys_addr_t addr, return ret; } +static bool queue_empty(struct ub_decoder_queue *q) +{ + return (Q_IDX(q->qs, q->prod.eventq_wr_idx) == + Q_IDX(q->qs, q->cons.eventq_rd_idx)) && + (Q_WRP(q->qs, q->prod.eventq_wr_idx) == + Q_WRP(q->qs, q->cons.eventq_rd_idx)); +} + +static void queue_inc_cons(struct ub_decoder_queue *q) +{ + u32 cons = (Q_WRP(q->qs, q->cons.eventq_rd_idx) | + Q_IDX(q->qs, q->cons.eventq_rd_idx)) + 1; + q->cons.eventq_rd_idx = Q_WRP(q->qs, cons) | Q_IDX(q->qs, cons); +} + +enum event_op_type { + RESERVED = 0x00, + EVENT_ADDR_OUT_OF_RANGE = 0x01, + EVENT_ILLEGAL_CMD = 0x02, +}; + +#define EVTQ_0_ID GENMASK_ULL(7, 0) +#define EVTQ_0_ADDR GENMASK_ULL(59, 32) +#define EVTQ_0_CMD_OPCODE GENMASK_ULL(39, 32) +#define EVTQ_ENT_DWORDS 2 +#define MAX_REASON_NUM 3 + +static const char * const cmd_err_reason[MAX_REASON_NUM] = { + "no error", + "illegal command", + "abort error(read command with 2bit ecc)" +}; + +static void fix_err_cmd(struct ub_decoder *decoder) +{ + struct ub_decoder_queue *cmdq = &(decoder->cmdq); + struct ub_entity *uent = decoder->uent; + u64 cmd[CMDQ_ENT_DWORDS] = {}; + struct queue_idx prod, cons; + void *pi; + int i; + + if (ub_cfg_read_dword(uent, DECODER_CMDQ_CONS, &cons.val)) { + ub_err(uent, "decoder fix error cmd, read ci failed\n"); + return; + } + if (ub_cfg_read_dword(uent, DECODER_CMDQ_PROD, &prod.val)) { + ub_err(uent, "decoder fix error cmd, read pi failed\n"); + return; + } + + cmd[0] |= FIELD_PREP(CMD_0_OP, TLBI_ALL); + pi = cmdq->qbase + Q_IDX(cmdq->qs, cons.cmdq_rd_idx) * + sizeof(struct tlbi_partial_entry); + + for (i = 0; i < CMDQ_ENT_DWORDS; i++) + writeq(cmd[i], pi + i * sizeof(u64)); + + if (cons.cmdq_err_reason >= MAX_REASON_NUM) + ub_err(uent, "cmdq err reason is invalid, reason=%u\n", + cons.cmdq_err_reason); + else + ub_err(uent, "cmdq err reason is %s\n", cmd_err_reason[cons.cmdq_err_reason]); + + prod.cmdq_err_resp = cons.cmdq_err; + + if (ub_cfg_write_dword(uent, DECODER_CMDQ_PROD, prod.val)) + ub_err(uent, "decoder fix error cmd, write pi err resp failed\n"); +} + +static void handle_evt(struct ub_decoder *decoder, u64 *evt) +{ + struct ub_entity *uent = decoder->uent; + + switch (FIELD_GET(EVTQ_0_ID, evt[0])) { + case EVENT_ADDR_OUT_OF_RANGE: + ub_err(uent, "decoder event, input addr out of range, addr=%#.7x00000\n", + (u32)FIELD_GET(EVTQ_0_ADDR, evt[0])); + break; + case EVENT_ILLEGAL_CMD: + ub_err(uent, "decoder event, illegal cmd, cmd_opcode=%#x\n", + (u32)FIELD_GET(EVTQ_0_CMD_OPCODE, evt[0])); + fix_err_cmd(decoder); + break; + default: + ub_err(uent, "invalid event opcode, opcode=%#x\n", + (u32)FIELD_GET(EVTQ_0_ID, evt[0])); + } +} + +static void decoder_event_deal(struct ub_decoder *decoder) +{ + struct ub_decoder_queue *evtq = &decoder->evtq; + struct ub_entity *uent = decoder->uent; + u64 evt[EVTQ_ENT_DWORDS]; + void *ci; + int i; + + if (ub_cfg_read_dword(uent, DECODER_EVENTQ_PROD, &(evtq->prod.val))) { + ub_err(uent, "decoder handle event, read eventq pi fail\n"); + return; + } + + while (!queue_empty(evtq)) { + ci = evtq->qbase + Q_IDX(evtq->qs, evtq->cons.eventq_rd_idx) * + EVT_ENTRY_SIZE; + + for (i = 0; i < EVTQ_ENT_DWORDS; i++) + evt[i] = readq(ci + i * sizeof(u64)); + + handle_evt(decoder, evt); + queue_inc_cons(evtq); + + if (ub_cfg_write_dword(uent, DECODER_EVENTQ_CONS, + evtq->cons.val)) + ub_err(uent, "decoder handle event, write eventq ci fail\n"); + } +} + +static irqreturn_t decoder_event_deal_handle(int irq, void *data) +{ + struct ub_entity *uent = (struct ub_entity *)data; + struct ub_decoder *decoder = uent->ubc->decoder; + + if (!decoder) { + ub_err(uent, "decoder does not exist\n"); + return IRQ_HANDLED; + } + + decoder_event_deal(decoder); + return IRQ_HANDLED; +} + +void ub_init_decoder_usi(struct ub_entity *uent) +{ + int irq_num, ret; + u32 usi_idx; + + if (!uent->ubc->decoder) { + ub_err(uent, "decoder not exist, can't init usi\n"); + return; + } + + ret = ub_cfg_read_dword(uent, DECODER_USI_IDX, &usi_idx); + if (ret) { + ub_err(uent, "get decoder usi idx fail\n"); + return; + } + + irq_num = ub_irq_vector(uent, usi_idx); + if (irq_num < 0) { + ub_err(uent, "ub get irq vector fail, ret=%d\n", irq_num); + return; + } + + ret = request_irq((unsigned int)irq_num, decoder_event_deal_handle, + IRQF_SHARED, "decoder_event_handle", (void *)uent); + if (ret) + ub_err(uent, "decoder request_irq fail, ret=%d\n", ret); + else + uent->ubc->decoder->irq_num = irq_num; +} + +void ub_uninit_decoder_usi(struct ub_entity *uent) +{ + int irq_num; + + if (!uent->ubc->decoder) { + ub_err(uent, "decoder not exist, can't uninit usi\n"); + return; + } + + irq_num = uent->ubc->decoder->irq_num; + if (irq_num < 0) + return; + + free_irq((unsigned int)irq_num, (void *)uent); +} + void ub_decoder_init(struct ub_entity *uent) { int ret; diff --git a/drivers/ub/ubus/decoder.h b/drivers/ub/ubus/decoder.h index c2d623244bcc..47710ead71db 100644 --- a/drivers/ub/ubus/decoder.h +++ b/drivers/ub/ubus/decoder.h @@ -117,6 +117,8 @@ struct ub_decoder { void ub_decoder_init(struct ub_entity *uent); void ub_decoder_uninit(struct ub_entity *uent); +void ub_init_decoder_usi(struct ub_entity *uent); +void ub_uninit_decoder_usi(struct ub_entity *uent); int ub_decoder_cmd_request(struct ub_decoder *decoder, phys_addr_t addr, u64 size, enum ub_cmd_op_type op); #endif /* __DECODER_H__ */ -- Gitee