From c7b866dfee2cab2fde35c28fdeb6e9851d0b8a88 Mon Sep 17 00:00:00 2001 From: wanghaibin Date: Thu, 11 Jan 2024 20:43:22 +0800 Subject: [PATCH 01/12] irqchip/gic-v3-its: Introduce the reserved device ID pools ANBZ: #9398 commit 87f09e449b654411ffa6362174171c291d7e2b41 openEuler. virt inclusion category: feature bugzilla: https://gitee.com/openeuler/kernel/issues/I8UROS CVE: NA -------------------------------- Organize the system-wide unused device IDs to build several device ID pools. Use these pools to manage the reserved device IDs which can be used by virtual devices (without the actual HW device ID). Signed-off-by: wanghaibin Signed-off-by: Zenghui Yu Signed-off-by: Kunkun Jiang Signed-off-by: Dongxu Sun Signed-off-by: Guanghui Feng --- drivers/irqchip/irq-gic-v3-its.c | 122 +++++++++++++++++++++++++++++++ drivers/misc/Kconfig | 9 +++ 2 files changed, 131 insertions(+) diff --git a/drivers/irqchip/irq-gic-v3-its.c b/drivers/irqchip/irq-gic-v3-its.c index deed5fd828f0..3f64d8e6da4a 100644 --- a/drivers/irqchip/irq-gic-v3-its.c +++ b/drivers/irqchip/irq-gic-v3-its.c @@ -39,6 +39,104 @@ #include "irq-gic-common.h" +#ifdef CONFIG_VIRT_PLAT_DEV +#include + +/* a reserved bus id region */ +struct plat_rsv_buses { + u8 start; /* the first reserved bus id */ + u8 count; +}; + +/* + * Build a devid pool per reserved bus id region, where all + * device ids should be unused by physical PCI devices. + */ +struct rsv_devid_pool { + struct list_head entry; + + struct plat_rsv_buses buses; + u32 start; + u32 end; + + raw_spinlock_t devid_bm_lock; + unsigned long *devid_bm; +}; + +static LIST_HEAD(rsv_devid_pools); +static DEFINE_RAW_SPINLOCK(rsv_devid_pools_lock); + +/* Do we have usable rsv_devid_pool? Initialized to be true. */ +static bool rsv_devid_pool_cap = true; +static u8 rsv_buses_start, rsv_buses_count; + +static int __init rsv_buses_start_cfg(char *buf) +{ + return kstrtou8(buf, 0, &rsv_buses_start); +} +early_param("irqchip.gicv3_rsv_buses_start", rsv_buses_start_cfg); + +static int __init rsv_buses_count_cfg(char *buf) +{ + return kstrtou8(buf, 0, &rsv_buses_count); +} +early_param("irqchip.gicv3_rsv_buses_count", rsv_buses_count_cfg); + +static void get_rsv_buses_resource(struct plat_rsv_buses *buses) +{ + buses->start = rsv_buses_start; + buses->count = rsv_buses_count; + + /* + * FIXME: There is no architectural way to get the *correct* + * reserved bus id info. + * + * The first thought is to increase the GITS_TYPER.Devbits for + * the usage for virtualization, but this will break all + * command layouts with DeviceID as an argument (e.g., INT). + * + * The second way is to decrease the GITS_TYPER.Devids so that + * SW can pick the unused device IDs for use (these IDs should + * actually be supported at HW level, though not exposed). + * *Or* fetch the information with the help of firmware. They + * are essentially the same way. + */ +} + +static int probe_devid_pool_one(void) +{ + struct rsv_devid_pool *devid_pool; + + devid_pool = kzalloc(sizeof(*devid_pool), GFP_KERNEL); + if (!devid_pool) + return -ENOMEM; + + get_rsv_buses_resource(&devid_pool->buses); + raw_spin_lock_init(&devid_pool->devid_bm_lock); + + devid_pool->start = PCI_DEVID(devid_pool->buses.start, 0); + devid_pool->end = PCI_DEVID(devid_pool->buses.start + devid_pool->buses.count, 0); + + if (devid_pool->end == devid_pool->start) { + kfree(devid_pool); + return -EINVAL; + } + + devid_pool->devid_bm = bitmap_zalloc(devid_pool->end - devid_pool->start, + GFP_KERNEL); + if (!devid_pool->devid_bm) { + kfree(devid_pool); + return -ENOMEM; + } + + raw_spin_lock(&rsv_devid_pools_lock); + list_add(&devid_pool->entry, &rsv_devid_pools); + raw_spin_unlock(&rsv_devid_pools_lock); + + return 0; +} +#endif + #define ITS_FLAGS_CMDQ_NEEDS_FLUSHING (1ULL << 0) #define ITS_FLAGS_WORKAROUND_CAVIUM_22375 (1ULL << 1) #define ITS_FLAGS_WORKAROUND_CAVIUM_23144 (1ULL << 2) @@ -199,6 +297,22 @@ extern bool ft2000_iommu_hwfix; #define gic_data_rdist_rd_base() (gic_data_rdist()->rd_base) #define gic_data_rdist_vlpi_base() (gic_data_rdist_rd_base() + SZ_128K) +#ifdef CONFIG_VIRT_PLAT_DEV +/* + * Currently we only build *one* devid pool. + */ +static int build_devid_pools(void) +{ + struct its_node *its; + + its = list_first_entry(&its_nodes, struct its_node, entry); + if (readl_relaxed(its->base + GITS_IIDR) != 0x00051736) + return -EINVAL; + + return probe_devid_pool_one(); +} +#endif + /* * Skip ITSs that have no vLPIs mapped, unless we're on GICv4.1, as we * always have vSGIs mapped. @@ -5464,6 +5578,14 @@ int __init its_init(struct fwnode_handle *handle, struct rdists *rdists, rdists->has_vlpis = false; pr_err("ITS: Disabling GICv4 support\n"); } + +#ifdef CONFIG_VIRT_PLAT_DEV + if (build_devid_pools()) + rsv_devid_pool_cap = false; + + if (rsv_devid_pool_cap) + pr_info("ITS: reserved device id pools enabled\n"); +#endif } register_syscore_ops(&its_syscore_ops); diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig index 140716083ab8..0c9baed7f1e3 100644 --- a/drivers/misc/Kconfig +++ b/drivers/misc/Kconfig @@ -474,6 +474,15 @@ config HISI_HIKEY_USB switching between the dual-role USB-C port and the USB-A host ports using only one USB controller. +config VIRT_PLAT_DEV + bool "virt platform device driver" + depends on KVM && ARM64 && ARCH_HISI + default n + help + Enable this configuration option to probe the virtual platform device, + which created for the Qemu emulated device to implement virtual MSI + direct injection. + source "drivers/misc/c2port/Kconfig" source "drivers/misc/eeprom/Kconfig" source "drivers/misc/cb710/Kconfig" -- Gitee From 469cec46f9dbfb5f01411d530f8f2dd6236b720b Mon Sep 17 00:00:00 2001 From: wanghaibin Date: Thu, 11 Jan 2024 20:43:23 +0800 Subject: [PATCH 02/12] irqchip/gic-v3-its: Alloc/Free device id from pools for virtual devices ANBZ: #9398 commit 7b39fd06a39128ca3804691812f65dd307806734 openEuler. virt inclusion category: feature bugzilla: https://gitee.com/openeuler/kernel/issues/I8UROS CVE: NA -------------------------------- Alloc/Free device id from pools for virtual devices and plug the helpers into its_msi_prepare()/its_free_device(). Signed-off-by: wanghaibin Signed-off-by: Zenghui Yu Signed-off-by: Kunkun Jiang Signed-off-by: Dongxu Sun Signed-off-by: Guanghui Feng --- drivers/irqchip/irq-gic-v3-its.c | 95 ++++++++++++++++++++++++++++++++ 1 file changed, 95 insertions(+) diff --git a/drivers/irqchip/irq-gic-v3-its.c b/drivers/irqchip/irq-gic-v3-its.c index 3f64d8e6da4a..ceb1083b465a 100644 --- a/drivers/irqchip/irq-gic-v3-its.c +++ b/drivers/irqchip/irq-gic-v3-its.c @@ -262,6 +262,12 @@ struct its_device { u32 nr_ites; u32 device_id; bool shared; + +#ifdef CONFIG_VIRT_PLAT_DEV + /* For virtual devices which needed the devid managed */ + bool is_vdev; + struct rsv_devid_pool *devid_pool; +#endif }; static struct { @@ -292,6 +298,60 @@ static DEFINE_IDA(its_vpeid_ida); extern bool ft2000_iommu_hwfix; #endif +#ifdef CONFIG_VIRT_PLAT_DEV +static void free_devid_to_rsv_pools(struct its_device *its_dev) +{ + struct rsv_devid_pool *pool = its_dev->devid_pool; + u32 id, size; + + WARN_ON(!pool); + + id = its_dev->device_id - pool->start; + size = pool->end - pool->start; + WARN_ON(id >= size); + + raw_spin_lock(&pool->devid_bm_lock); + clear_bit(id, pool->devid_bm); + raw_spin_unlock(&pool->devid_bm_lock); + + pr_debug("ITS: free devid (%u) to rsv_devid_pools\n", its_dev->device_id); +} + +static int alloc_devid_from_rsv_pools(struct rsv_devid_pool **devid_pool, + u32 *dev_id) +{ + struct rsv_devid_pool *pool; + int err = -ENOSPC; + + raw_spin_lock(&rsv_devid_pools_lock); + list_for_each_entry(pool, &rsv_devid_pools, entry) { + u32 size, id; + + size = pool->end - pool->start; + + raw_spin_lock(&pool->devid_bm_lock); + id = find_first_zero_bit(pool->devid_bm, size); + if (id >= size) { + /* No usable device id in this pool, try next. */ + raw_spin_unlock(&pool->devid_bm_lock); + continue; + } + + *dev_id = pool->start + id; + set_bit(id, pool->devid_bm); + raw_spin_unlock(&pool->devid_bm_lock); + + *devid_pool = pool; + err = 0; + break; + } + raw_spin_unlock(&rsv_devid_pools_lock); + + pr_debug("ITS: alloc devid (%u) from rsv_devid_pools\n", *dev_id); + return err; +} +#endif + #define gic_data_rdist() (raw_cpu_ptr(gic_rdists->rdist)) #define gic_data_rdist_cpu(cpu) (per_cpu_ptr(gic_rdists->rdist, cpu)) #define gic_data_rdist_rd_base() (gic_data_rdist()->rd_base) @@ -3546,6 +3606,13 @@ static void its_free_device(struct its_device *its_dev) raw_spin_unlock_irqrestore(&its_dev->its->lock, flags); kfree(its_dev->event_map.col_map); kfree(its_dev->itt); + +#ifdef CONFIG_VIRT_PLAT_DEV + if (its_dev->is_vdev) { + WARN_ON(!rsv_devid_pool_cap); + free_devid_to_rsv_pools(its_dev); + } +#endif kfree(its_dev); } @@ -3582,6 +3649,23 @@ static int its_msi_prepare(struct irq_domain *domain, struct device *dev, */ dev_id = info->scratchpad[0].ul; +#ifdef CONFIG_VIRT_PLAT_DEV + int use_devid_pool = false; + struct rsv_devid_pool *pool = NULL; + + if (rsv_devid_pool_cap && !dev->of_node && !dev->fwnode && + info->scratchpad[0].ul == -1) + use_devid_pool = true; + + if (use_devid_pool) { + err = alloc_devid_from_rsv_pools(&pool, &dev_id); + if (err) { + pr_warn("ITS: No remaining device id\n"); + return err; + } + } +#endif + msi_info = msi_get_domain_info(domain); its = msi_info->data; @@ -3598,6 +3682,10 @@ static int its_msi_prepare(struct irq_domain *domain, struct device *dev, mutex_lock(&its->dev_alloc_lock); its_dev = its_find_device(its, dev_id); if (its_dev) { +#ifdef CONFIG_VIRT_PLAT_DEV + /* Impossible ...*/ + WARN_ON_ONCE(use_devid_pool); +#endif /* * We already have seen this ID, probably through * another alias (PCI bridge of some sort). No need to @@ -3615,6 +3703,13 @@ static int its_msi_prepare(struct irq_domain *domain, struct device *dev, } pr_debug("ITT %d entries, %d bits\n", nvec, ilog2(nvec)); + +#ifdef CONFIG_VIRT_PLAT_DEV + if (use_devid_pool) { + its_dev->is_vdev = true; + its_dev->devid_pool = pool; + } +#endif out: mutex_unlock(&its->dev_alloc_lock); info->scratchpad[0].ptr = its_dev; -- Gitee From 074242e0bd28f8240466796fc39bf480f9fd1224 Mon Sep 17 00:00:00 2001 From: wanghaibin Date: Thu, 11 Jan 2024 20:43:24 +0800 Subject: [PATCH 03/12] irqchip/gic-v3-its: Add virt platform devices MSI support ANBZ: #9398 commit 4cc685056a0056a0f8bdcfe901000dd4731efc6b openEuler. virt inclusion category: feature bugzilla: https://gitee.com/openeuler/kernel/issues/I8UROS CVE: NA -------------------------------- Implement the virtual platform device MSI with the GICv3 ITS. Compared with phycial platform device msi, msi_prepare implementation need consider the devid alloc. Signed-off-by: wanghaibin Signed-off-by: Zenghui Yu Signed-off-by: Kunkun Jiang Signed-off-by: Dongxu Sun Signed-off-by: Guanghui Feng --- drivers/irqchip/irq-gic-v3-its-platform-msi.c | 45 +++++++++++++++++-- drivers/irqchip/irq-gic-v3-its.c | 2 +- include/linux/msi.h | 4 ++ 3 files changed, 47 insertions(+), 4 deletions(-) diff --git a/drivers/irqchip/irq-gic-v3-its-platform-msi.c b/drivers/irqchip/irq-gic-v3-its-platform-msi.c index daa6d5053bc3..f6694b291ca6 100644 --- a/drivers/irqchip/irq-gic-v3-its-platform-msi.c +++ b/drivers/irqchip/irq-gic-v3-its-platform-msi.c @@ -10,6 +10,20 @@ #include #include +#ifdef CONFIG_VIRT_PLAT_DEV +static struct irq_domain *vp_irq_domain; +extern bool rsv_devid_pool_cap; + +struct irq_domain *vp_get_irq_domain(void) +{ + if (!vp_irq_domain) + pr_err("virtual platform irqdomain hasn't be initialized!\n"); + + return vp_irq_domain; +} +EXPORT_SYMBOL_GPL(vp_get_irq_domain); +#endif + static struct irq_chip its_pmsi_irq_chip = { .name = "ITS-pMSI", }; @@ -52,6 +66,19 @@ static int its_pmsi_prepare(struct irq_domain *domain, struct device *dev, msi_info = msi_get_domain_info(domain->parent); +#ifdef CONFIG_VIRT_PLAT_DEV + if (rsv_devid_pool_cap && !dev->of_node && !dev->fwnode) { + WARN_ON_ONCE(domain != vp_irq_domain); + /* + * virtual platform device doesn't have a DeviceID which + * will be allocated with core ITS's help. + */ + info->scratchpad[0].ul = -1; + + goto vdev_pmsi_prepare; + } +#endif + if (dev->of_node) ret = of_pmsi_get_dev_id(domain, dev, &dev_id); else @@ -62,6 +89,9 @@ static int its_pmsi_prepare(struct irq_domain *domain, struct device *dev, /* ITS specific DeviceID, as the core ITS ignores dev. */ info->scratchpad[0].ul = dev_id; +#ifdef CONFIG_VIRT_PLAT_DEV +vdev_pmsi_prepare: +#endif /* Allocate at least 32 MSIs, and always as a power of 2 */ nvec = max_t(int, 32, roundup_pow_of_two(nvec)); return msi_info->ops->msi_prepare(domain->parent, @@ -86,7 +116,7 @@ static const struct of_device_id its_device_id[] = { static int __init its_pmsi_init_one(struct fwnode_handle *fwnode, const char *name) { - struct irq_domain *parent; + struct irq_domain *pmsi_irqdomain, *parent; parent = irq_find_matching_fwnode(fwnode, DOMAIN_BUS_NEXUS); if (!parent || !msi_get_domain_info(parent)) { @@ -94,13 +124,22 @@ static int __init its_pmsi_init_one(struct fwnode_handle *fwnode, return -ENXIO; } - if (!platform_msi_create_irq_domain(fwnode, &its_pmsi_domain_info, - parent)) { + pmsi_irqdomain = platform_msi_create_irq_domain(fwnode, + &its_pmsi_domain_info, + parent); + if (!pmsi_irqdomain) { pr_err("%s: unable to create platform domain\n", name); return -ENXIO; } pr_info("Platform MSI: %s domain created\n", name); + +#ifdef CONFIG_VIRT_PLAT_DEV + /* Should we take other irqdomains into account? */ + if (!vp_irq_domain) + vp_irq_domain = pmsi_irqdomain; +#endif + return 0; } diff --git a/drivers/irqchip/irq-gic-v3-its.c b/drivers/irqchip/irq-gic-v3-its.c index ceb1083b465a..cac0974e5def 100644 --- a/drivers/irqchip/irq-gic-v3-its.c +++ b/drivers/irqchip/irq-gic-v3-its.c @@ -67,7 +67,7 @@ static LIST_HEAD(rsv_devid_pools); static DEFINE_RAW_SPINLOCK(rsv_devid_pools_lock); /* Do we have usable rsv_devid_pool? Initialized to be true. */ -static bool rsv_devid_pool_cap = true; +bool rsv_devid_pool_cap = true; static u8 rsv_buses_start, rsv_buses_count; static int __init rsv_buses_start_cfg(char *buf) diff --git a/include/linux/msi.h b/include/linux/msi.h index 8e01ac6a1f58..321291643466 100644 --- a/include/linux/msi.h +++ b/include/linux/msi.h @@ -550,6 +550,10 @@ int pci_msi_domain_check_cap(struct irq_domain *domain, u32 pci_msi_domain_get_msi_rid(struct irq_domain *domain, struct pci_dev *pdev); struct irq_domain *pci_msi_get_device_domain(struct pci_dev *pdev); bool pci_dev_has_special_msi_domain(struct pci_dev *pdev); + +#ifdef CONFIG_VIRT_PLAT_DEV +struct irq_domain *vp_get_irq_domain(void); +#endif #else static inline struct irq_domain *pci_msi_get_device_domain(struct pci_dev *pdev) { -- Gitee From 98324696f50ea39f1ab8594937bd6a91bfcd7d0e Mon Sep 17 00:00:00 2001 From: wanghaibin Date: Thu, 11 Jan 2024 20:43:25 +0800 Subject: [PATCH 04/12] virt_plat_dev: Register the virt platform device driver ANBZ: #9398 commit 8597aa42085141b113855f552ba9132990cad84a openEuler. virt inclusion category: feature bugzilla: https://gitee.com/openeuler/kernel/issues/I8UROS CVE: NA -------------------------------- This driver porbed the virt platform device by name, and it just take up a few MSIs through the vp_irqdomain. Signed-off-by: wanghaibin Signed-off-by: Zenghui Yu Signed-off-by: Kunkun Jiang Signed-off-by: Dongxu Sun Signed-off-by: Guanghui Feng --- drivers/misc/Makefile | 1 + drivers/misc/virt_plat_dev.c | 118 +++++++++++++++++++++++++++++++++++ 2 files changed, 119 insertions(+) create mode 100644 drivers/misc/virt_plat_dev.c diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile index 3615763234a6..64fb645bdc91 100644 --- a/drivers/misc/Makefile +++ b/drivers/misc/Makefile @@ -58,3 +58,4 @@ obj-$(CONFIG_HABANA_AI) += habanalabs/ obj-$(CONFIG_UACCE) += uacce/ obj-$(CONFIG_XILINX_SDFEC) += xilinx_sdfec.o obj-$(CONFIG_HISI_HIKEY_USB) += hisi_hikey_usb.o +obj-$(CONFIG_VIRT_PLAT_DEV) += virt_plat_dev.o diff --git a/drivers/misc/virt_plat_dev.c b/drivers/misc/virt_plat_dev.c new file mode 100644 index 000000000000..c0804474d7c1 --- /dev/null +++ b/drivers/misc/virt_plat_dev.c @@ -0,0 +1,118 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (C) 2019-2020 HUAWEI TECHNOLOGIES CO., LTD., All Rights Reserved. + * Author: Wanghaibin + */ +#include +#include +#include +#include +#include +#include +#include +#include + +#define VIRT_DEV_DEBUG 1 + +#ifdef VIRT_DEV_DEBUG +#define virtdev_info(fmt, ...) pr_info("virdev: " fmt, ## __VA_ARGS__) +#else +#define virtdev_info(fmt, ...) +#endif + +static irqreturn_t virt_irq_handle(int irq, void *data) +{ + return IRQ_HANDLED; +} + +static void virt_write_msi_msg(struct msi_desc *desc, struct msi_msg *msg) +{ +} + +static int virt_device_probe(struct platform_device *pdev) +{ + struct msi_desc *desc; + unsigned int *drvdata = dev_get_drvdata(&pdev->dev); + unsigned int nvec = *drvdata; + struct irq_domain *vp_irqdomain = vp_get_irq_domain(); + int ret; + + if (!vp_irqdomain) + return -ENXIO; + + virtdev_info("Allocate platform msi irqs nvecs: %d\n", nvec); + dev_set_msi_domain(&pdev->dev, vp_irqdomain); + + ret = platform_msi_domain_alloc_irqs(&pdev->dev, nvec, + virt_write_msi_msg); + if (ret) { + pr_err("Allocate platform msi irqs failed %d\n", ret); + goto error; + } + + virtdev_info("Allocate platform msi irqs succeed\n"); + for_each_msi_entry(desc, &pdev->dev) { + virtdev_info("Request irq %d\n", desc->irq); + ret = request_irq(desc->irq, virt_irq_handle, 0, + "virt_dev_host", pdev); + if (ret) { + pr_err("Request irq %d failed %d\n", desc->irq, ret); + goto error_free_irqs; + } + } + + virtdev_info("Init virtual platform device driver successfully.\n"); + return 0; + +error_free_irqs: + for_each_msi_entry(desc, &pdev->dev) + free_irq(desc->irq, pdev); + + platform_msi_domain_free_irqs(&pdev->dev); +error: + return ret; +} + +static int virt_device_remove(struct platform_device *pdev) +{ + struct msi_desc *desc; + + for_each_msi_entry(desc, &pdev->dev) + free_irq(desc->irq, pdev); + + platform_msi_domain_free_irqs(&pdev->dev); + + return 0; +} + +static struct platform_driver virtdev_driver = { + .driver = { + /* Using the device & driver name to match each other */ + .name = "virt_plat_dev", + }, + .probe = virt_device_probe, + .remove = virt_device_remove, +}; + +static int __init virtdev_init(void) +{ + int ret; + + ret = platform_driver_register(&virtdev_driver); + if (ret) { + pr_err("Register virtdev platform driver failed (%d)\n", ret); + return ret; + } + + virtdev_info("Register virtdev platform driver succeed.\n"); + return 0; +} +module_init(virtdev_init); + +static void __exit virtdev_exit(void) +{ + platform_driver_unregister(&virtdev_driver); +} +module_exit(virtdev_exit); + +MODULE_LICENSE("GPL"); -- Gitee From 052725bfe1d8b1d9574491ddbc8b6066a1528645 Mon Sep 17 00:00:00 2001 From: wanghaibin Date: Thu, 11 Jan 2024 20:43:26 +0800 Subject: [PATCH 05/12] KVM: arm64: Introduce shadow device ANBZ: #9398 commit 013f589495b79882a74fe3303e701effb4d26723 openEuler. virt inclusion category: feature bugzilla: https://gitee.com/openeuler/kernel/issues/I8UROS CVE: NA -------------------------------- The shadow device implement that establish relationships between virtual devices and back-end virtual platform devices. Signed-off-by: wanghaibin Signed-off-by: Zenghui Yu Signed-off-by: Kunkun Jiang Signed-off-by: Dongxu Sun Signed-off-by: Guanghui Feng --- arch/arm64/kvm/Makefile | 1 + arch/arm64/kvm/arm.c | 47 +++++ arch/arm64/kvm/vgic/shadow_dev.c | 327 +++++++++++++++++++++++++++++++ arch/arm64/kvm/vgic/vgic-init.c | 4 + include/kvm/arm_vgic.h | 30 +++ include/uapi/linux/kvm.h | 11 ++ 6 files changed, 420 insertions(+) create mode 100644 arch/arm64/kvm/vgic/shadow_dev.c diff --git a/arch/arm64/kvm/Makefile b/arch/arm64/kvm/Makefile index 40876bb3fbea..4bae1c00086a 100644 --- a/arch/arm64/kvm/Makefile +++ b/arch/arm64/kvm/Makefile @@ -26,6 +26,7 @@ kvm-y := $(KVM)/kvm_main.o $(KVM)/coalesced_mmio.o $(KVM)/eventfd.o \ vgic/vgic-its.o vgic/vgic-debug.o kvm-$(CONFIG_KVM_ARM_PMU) += pmu-emul.o +kvm-$(CONFIG_VIRT_PLAT_DEV) += vgic/shadow_dev.o ifndef CONFIG_KVM_ARM_HOST_VHE_ONLY obj-$(CONFIG_KVM) += hyp/ diff --git a/arch/arm64/kvm/arm.c b/arch/arm64/kvm/arm.c index 575f16c32c32..e4a4163f09e6 100644 --- a/arch/arm64/kvm/arm.c +++ b/arch/arm64/kvm/arm.c @@ -232,6 +232,11 @@ int kvm_vm_ioctl_check_extension(struct kvm *kvm, long ext) case KVM_CAP_STEAL_TIME: r = kvm_arm_pvtime_supported(); break; +#ifdef CONFIG_VIRT_PLAT_DEV + case KVM_CAP_ARM_VIRT_MSI_BYPASS: + r = sdev_enable; + break; +#endif default: r = kvm_arch_vm_ioctl_check_extension(kvm, ext); break; @@ -1328,6 +1333,37 @@ long kvm_arch_vm_ioctl(struct file *filp, return 0; } + +#ifdef CONFIG_VIRT_PLAT_DEV + case KVM_CREATE_SHADOW_DEV: { + struct kvm_master_dev_info *mdi; + u32 nvectors; + int ret; + + if (get_user(nvectors, (const u32 __user *)argp)) + return -EFAULT; + if (!nvectors) + return -EINVAL; + + mdi = memdup_user(argp, sizeof(*mdi) + nvectors * sizeof(mdi->msi[0])); + if (IS_ERR(mdi)) + return PTR_ERR(mdi); + + ret = kvm_shadow_dev_create(kvm, mdi); + kfree(mdi); + + return ret; + } + case KVM_DEL_SHADOW_DEV: { + u32 devid; + + if (get_user(devid, (const u32 __user *)argp)) + return -EFAULT; + + kvm_shadow_dev_delete(kvm, devid); + return 0; + } +#endif default: return -EINVAL; } @@ -1778,6 +1814,13 @@ void kvm_arch_irq_bypass_start(struct irq_bypass_consumer *cons) kvm_arm_resume_guest(irqfd->kvm); } +#ifdef CONFIG_VIRT_PLAT_DEV +void kvm_arch_pre_destroy_vm(struct kvm *kvm) +{ + kvm_shadow_dev_delete_all(kvm); +} +#endif + /** * Initialize Hyp-mode and memory mappings on all CPUs. */ @@ -1871,6 +1914,10 @@ static int arm_init(void) rc = kvm_init(NULL, sizeof(struct kvm_vcpu), 0, THIS_MODULE); if (!rc) kvm_info("init kvm-arm successfully\n"); + +#ifdef CONFIG_VIRT_PLAT_DEV + kvm_shadow_dev_init(); +#endif return rc; } diff --git a/arch/arm64/kvm/vgic/shadow_dev.c b/arch/arm64/kvm/vgic/shadow_dev.c new file mode 100644 index 000000000000..6774801e6024 --- /dev/null +++ b/arch/arm64/kvm/vgic/shadow_dev.c @@ -0,0 +1,327 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (C) 2019-2020 HUAWEI TECHNOLOGIES CO., LTD., All Rights Reserved. + * Author: Wanghaibin + */ + +#include +#include +#include +#include +#include +#include +#include + +static struct workqueue_struct *sdev_cleanup_wq; +static bool virt_msi_bypass; +bool sdev_enable; + +static void shadow_dev_destroy(struct work_struct *work); +static void sdev_virt_pdev_delete(struct platform_device *pdev); + +int shadow_dev_virq_bypass_inject(struct kvm *kvm, + struct kvm_kernel_irq_routing_entry *e) +{ + struct shadow_dev *sdev = e->cache.data; + u32 vec = e->msi.data; + u32 host_irq = sdev->host_irq[vec]; + int ret; + + ret = irq_set_irqchip_state(host_irq, IRQCHIP_STATE_PENDING, true); + WARN_RATELIMIT(ret, "IRQ %d", host_irq); + + return ret; +} + +/* Must be called with the dist->sdev_list_lock held */ +struct shadow_dev *kvm_shadow_dev_get(struct kvm *kvm, struct kvm_msi *msi) +{ + struct vgic_dist *dist = &kvm->arch.vgic; + struct shadow_dev *sdev; + + if (!sdev_enable) + return NULL; + + list_for_each_entry(sdev, &dist->sdev_list_head, entry) { + if (sdev->devid != msi->devid) + continue; + + if (sdev->nvecs <= msi->data || + !test_bit(msi->data, sdev->enable)) + break; + + return sdev; + } + + return NULL; +} + +static struct platform_device *sdev_virt_pdev_add(u32 nvec) +{ + struct platform_device *virtdev; + int ret = -ENOMEM; + + virtdev = platform_device_alloc("virt_plat_dev", PLATFORM_DEVID_AUTO); + if (!virtdev) { + kvm_err("Allocate virtual platform device failed\n"); + goto out; + } + + dev_set_drvdata(&virtdev->dev, &nvec); + + ret = platform_device_add(virtdev); + if (ret) { + kvm_err("Add virtual platform device failed (%d)\n", ret); + goto put_device; + } + + return virtdev; + +put_device: + platform_device_put(virtdev); +out: + return ERR_PTR(ret); +} + +static void sdev_set_irq_entry(struct shadow_dev *sdev, + struct kvm_kernel_irq_routing_entry *irq_entries) +{ + int i; + + for (i = 0; i < sdev->nvecs; i++) { + irq_entries[i].msi.address_lo = sdev->msi[i].address_lo; + irq_entries[i].msi.address_hi = sdev->msi[i].address_hi; + irq_entries[i].msi.data = sdev->msi[i].data; + irq_entries[i].msi.flags = sdev->msi[i].flags; + irq_entries[i].msi.devid = sdev->msi[i].devid; + } +} + +static int sdev_virq_bypass_active(struct kvm *kvm, struct shadow_dev *sdev) +{ + struct kvm_kernel_irq_routing_entry *irq_entries; + struct msi_desc *desc; + u32 vec = 0; + + sdev->host_irq = kcalloc(sdev->nvecs, sizeof(int), GFP_KERNEL); + sdev->enable = bitmap_zalloc(sdev->nvecs, GFP_KERNEL); + irq_entries = kcalloc(sdev->nvecs, + sizeof(struct kvm_kernel_irq_routing_entry), + GFP_KERNEL); + + if (!irq_entries || !sdev->enable || !sdev->host_irq) { + kfree(sdev->host_irq); + kfree(sdev->enable); + kfree(irq_entries); + return -ENOMEM; + } + + sdev_set_irq_entry(sdev, irq_entries); + + for_each_msi_entry(desc, &sdev->pdev->dev) { + if (!kvm_vgic_v4_set_forwarding(kvm, desc->irq, + &irq_entries[vec])) { + set_bit(vec, sdev->enable); + sdev->host_irq[vec] = desc->irq; + } else { + /* + * Can not use shadow device for direct injection, + * though not fatal... + */ + kvm_err("Shadow device set (%d) forwarding failed", + desc->irq); + } + vec++; + } + + kfree(irq_entries); + return 0; +} + +static void sdev_msi_entry_init(struct kvm_master_dev_info *mdi, + struct shadow_dev *sdev) +{ + int i; + + for (i = 0; i < sdev->nvecs; i++) { + sdev->msi[i].address_lo = mdi->msi[i].address_lo; + sdev->msi[i].address_hi = mdi->msi[i].address_hi; + sdev->msi[i].data = mdi->msi[i].data; + sdev->msi[i].flags = mdi->msi[i].flags; + sdev->msi[i].devid = mdi->msi[i].devid; + } +} + +int kvm_shadow_dev_create(struct kvm *kvm, struct kvm_master_dev_info *mdi) +{ + struct vgic_dist *dist = &kvm->arch.vgic; + struct shadow_dev *sdev; + struct kvm_msi *msi; + unsigned long flags; + int ret; + + if (WARN_ON(!sdev_enable)) + return -EINVAL; + + ret = -ENOMEM; + sdev = kzalloc(sizeof(struct shadow_dev), GFP_KERNEL); + if (!sdev) + return ret; + + sdev->nvecs = mdi->nvectors; + + msi = kcalloc(sdev->nvecs, sizeof(struct kvm_msi), GFP_KERNEL); + if (!msi) + goto free_sdev; + + sdev->msi = msi; + sdev_msi_entry_init(mdi, sdev); + sdev->devid = sdev->msi[0].devid; + + sdev->pdev = sdev_virt_pdev_add(sdev->nvecs); + if (IS_ERR(sdev->pdev)) { + ret = PTR_ERR(sdev->pdev); + goto free_sdev_msi; + } + + ret = sdev_virq_bypass_active(kvm, sdev); + if (ret) + goto delete_virtdev; + + sdev->kvm = kvm; + INIT_WORK(&sdev->destroy, shadow_dev_destroy); + + raw_spin_lock_irqsave(&dist->sdev_list_lock, flags); + list_add_tail(&sdev->entry, &dist->sdev_list_head); + raw_spin_unlock_irqrestore(&dist->sdev_list_lock, flags); + + kvm_info("Create shadow device: 0x%x\n", sdev->devid); + return ret; + +delete_virtdev: + sdev_virt_pdev_delete(sdev->pdev); +free_sdev_msi: + kfree(sdev->msi); +free_sdev: + kfree(sdev); + return ret; +} + +static void sdev_virt_pdev_delete(struct platform_device *pdev) +{ + platform_device_unregister(pdev); +} + +static void sdev_virq_bypass_deactive(struct kvm *kvm, struct shadow_dev *sdev) +{ + struct kvm_kernel_irq_routing_entry *irq_entries; + struct msi_desc *desc; + u32 vec = 0; + + irq_entries = kcalloc(sdev->nvecs, + sizeof(struct kvm_kernel_irq_routing_entry), + GFP_KERNEL); + if (!irq_entries) + return; + + sdev_set_irq_entry(sdev, irq_entries); + + for_each_msi_entry(desc, &sdev->pdev->dev) { + if (!kvm_vgic_v4_unset_forwarding(kvm, desc->irq, + &irq_entries[vec])) { + clear_bit(vec, sdev->enable); + sdev->host_irq[vec] = 0; + } else { + kvm_err("Shadow device unset (%d) forwarding failed", + desc->irq); + } + vec++; + } + + kfree(sdev->host_irq); + kfree(sdev->enable); + kfree(irq_entries); + + /* FIXME: no error handling */ +} + +static void shadow_dev_destroy(struct work_struct *work) +{ + struct shadow_dev *sdev = container_of(work, struct shadow_dev, destroy); + struct kvm *kvm = sdev->kvm; + + sdev_virq_bypass_deactive(kvm, sdev); + sdev_virt_pdev_delete(sdev->pdev); + + sdev->nvecs = 0; + kfree(sdev->msi); + kfree(sdev); +} + +void kvm_shadow_dev_delete(struct kvm *kvm, u32 devid) +{ + struct vgic_dist *dist = &kvm->arch.vgic; + struct shadow_dev *sdev, *tmp; + unsigned long flags; + + if (WARN_ON(!sdev_enable)) + return; + + raw_spin_lock_irqsave(&dist->sdev_list_lock, flags); + WARN_ON(list_empty(&dist->sdev_list_head)); /* shouldn't be invoked */ + + list_for_each_entry_safe(sdev, tmp, &dist->sdev_list_head, entry) { + if (sdev->devid != devid) + continue; + + list_del(&sdev->entry); + queue_work(sdev_cleanup_wq, &sdev->destroy); + break; + } + raw_spin_unlock_irqrestore(&dist->sdev_list_lock, flags); + + flush_workqueue(sdev_cleanup_wq); +} + +void kvm_shadow_dev_delete_all(struct kvm *kvm) +{ + struct vgic_dist *dist = &kvm->arch.vgic; + struct shadow_dev *sdev, *tmp; + unsigned long flags; + + if (!sdev_enable) + return; + + raw_spin_lock_irqsave(&dist->sdev_list_lock, flags); + + list_for_each_entry_safe(sdev, tmp, &dist->sdev_list_head, entry) { + list_del(&sdev->entry); + queue_work(sdev_cleanup_wq, &sdev->destroy); + } + + raw_spin_unlock_irqrestore(&dist->sdev_list_lock, flags); + + flush_workqueue(sdev_cleanup_wq); +} + +static int __init early_virt_msi_bypass(char *buf) +{ + return strtobool(buf, &virt_msi_bypass); +} +early_param("kvm-arm.virt_msi_bypass", early_virt_msi_bypass); + +void kvm_shadow_dev_init(void) +{ + /* + * FIXME: Ideally shadow device should only rely on a GICv4.0 + * capable ITS, but we should also take the reserved device ID + * pools into account. + */ + sdev_enable = kvm_vgic_global_state.has_gicv4 && virt_msi_bypass; + + sdev_cleanup_wq = alloc_workqueue("kvm-sdev-cleanup", 0, 0); + if (!sdev_cleanup_wq) + sdev_enable = false; + + kvm_info("Shadow device %sabled\n", sdev_enable ? "en" : "dis"); +} diff --git a/arch/arm64/kvm/vgic/vgic-init.c b/arch/arm64/kvm/vgic/vgic-init.c index f48d3ba00c2f..742a382cf910 100644 --- a/arch/arm64/kvm/vgic/vgic-init.c +++ b/arch/arm64/kvm/vgic/vgic-init.c @@ -56,6 +56,10 @@ void kvm_vgic_early_init(struct kvm *kvm) INIT_LIST_HEAD(&dist->lpi_list_head); INIT_LIST_HEAD(&dist->lpi_translation_cache); raw_spin_lock_init(&dist->lpi_list_lock); +#ifdef CONFIG_VIRT_PLAT_DEV + INIT_LIST_HEAD(&dist->sdev_list_head); + raw_spin_lock_init(&dist->sdev_list_lock); +#endif } /* CREATION */ diff --git a/include/kvm/arm_vgic.h b/include/kvm/arm_vgic.h index 8e33a33b68fa..1b0914ca7f98 100644 --- a/include/kvm/arm_vgic.h +++ b/include/kvm/arm_vgic.h @@ -33,6 +33,23 @@ #define irq_is_spi(irq) ((irq) >= VGIC_NR_PRIVATE_IRQS && \ (irq) <= VGIC_MAX_SPI) +#ifdef CONFIG_VIRT_PLAT_DEV +struct shadow_dev { + struct kvm *kvm; + struct list_head entry; + + u32 devid; /* guest visible device id */ + u32 nvecs; + unsigned long *enable; + int *host_irq; + struct kvm_msi *msi; + + struct platform_device *pdev; + + struct work_struct destroy; +}; +#endif + enum vgic_type { VGIC_V2, /* Good ol' GICv2 */ VGIC_V3, /* New fancy GICv3 */ @@ -267,6 +284,10 @@ struct vgic_dist { * else. */ struct its_vm its_vm; +#ifdef CONFIG_VIRT_PLAT_DEV + raw_spinlock_t sdev_list_lock; + struct list_head sdev_list_head; +#endif }; struct vgic_v2_cpu_if { @@ -406,4 +427,13 @@ int vgic_v4_load(struct kvm_vcpu *vcpu); void vgic_v4_commit(struct kvm_vcpu *vcpu); int vgic_v4_put(struct kvm_vcpu *vcpu, bool need_db); +#ifdef CONFIG_VIRT_PLAT_DEV +extern bool sdev_enable; + +void kvm_shadow_dev_init(void); +int kvm_shadow_dev_create(struct kvm *kvm, struct kvm_master_dev_info *mdi); +void kvm_shadow_dev_delete(struct kvm *kvm, u32 devid); +void kvm_shadow_dev_delete_all(struct kvm *kvm); +#endif + #endif /* __KVM_ARM_VGIC_H */ diff --git a/include/uapi/linux/kvm.h b/include/uapi/linux/kvm.h index d9a0443a7128..064fbdf56e28 100644 --- a/include/uapi/linux/kvm.h +++ b/include/uapi/linux/kvm.h @@ -903,6 +903,9 @@ struct kvm_ppc_resize_hpt { #ifdef __KVM_HAVE_PIT #define KVM_CAP_REINJECT_CONTROL 24 #endif + +#define KVM_CAP_ARM_VIRT_MSI_BYPASS 799 + #define KVM_CAP_IRQ_ROUTING 25 #define KVM_CAP_IRQ_INJECT_STATUS 26 #define KVM_CAP_ASSIGN_DEV_IRQ 29 @@ -1338,6 +1341,11 @@ struct kvm_vfio_spapr_tce { __s32 tablefd; }; +struct kvm_master_dev_info { + __u32 nvectors; + struct kvm_msi msi[]; +}; + /* * ioctls for VM fds */ @@ -1455,6 +1463,9 @@ struct kvm_s390_ucas_mapping { #define KVM_GET_DEVICE_ATTR _IOW(KVMIO, 0xe2, struct kvm_device_attr) #define KVM_HAS_DEVICE_ATTR _IOW(KVMIO, 0xe3, struct kvm_device_attr) +#define KVM_CREATE_SHADOW_DEV _IOW(KVMIO, 0xf0, struct kvm_master_dev_info) +#define KVM_DEL_SHADOW_DEV _IOW(KVMIO, 0xf1, __u32) + /* ioctls for control vm during system reset */ #define KVM_CONTROL_PRE_SYSTEM_RESET _IO(KVMIO, 0xe8) #define KVM_CONTROL_POST_SYSTEM_RESET _IO(KVMIO, 0xe9) -- Gitee From e0d5d97c46f6c1b4a49fd5630651ad669bb7c0fa Mon Sep 17 00:00:00 2001 From: wanghaibin Date: Thu, 11 Jan 2024 20:43:27 +0800 Subject: [PATCH 06/12] KVM: arm64: kire: irq routing entry cached the relevant cache data ANBZ: #9398 commit e25e2fd494003f0fe9cc8b5e0be3e74148c45a79 openEuler. virt inclusion category: feature bugzilla: https://gitee.com/openeuler/kernel/issues/I8UROS CVE: NA -------------------------------- For each kernel irq routing entry, cached the relevant virtual shadow device, for IRQ bypass inject in future. Signed-off-by: wanghaibin Signed-off-by: Zenghui Yu Signed-off-by: Kunkun Jiang Signed-off-by: Dongxu Sun Signed-off-by: Guanghui Feng --- arch/arm64/kvm/vgic/vgic-irqfd.c | 23 +++++++++++++++++++++++ include/kvm/arm_vgic.h | 1 + include/linux/kvm_host.h | 16 ++++++++++++++++ virt/kvm/eventfd.c | 11 +++++++++++ 4 files changed, 51 insertions(+) diff --git a/arch/arm64/kvm/vgic/vgic-irqfd.c b/arch/arm64/kvm/vgic/vgic-irqfd.c index 79f8899b234c..ff42403482db 100644 --- a/arch/arm64/kvm/vgic/vgic-irqfd.c +++ b/arch/arm64/kvm/vgic/vgic-irqfd.c @@ -9,6 +9,29 @@ #include #include "vgic.h" +#ifdef CONFIG_VIRT_PLAT_DEV +static void kvm_populate_msi(struct kvm_kernel_irq_routing_entry *e, + struct kvm_msi *msi); + +void kire_arch_cached_data_update(struct kvm *kvm, + struct kvm_kernel_irq_routing_entry *e) +{ + struct vgic_dist *dist = &kvm->arch.vgic; + struct kire_data *cache = &e->cache; + struct shadow_dev *sdev; + struct kvm_msi msi; + + kvm_populate_msi(e, &msi); + + raw_spin_lock(&dist->sdev_list_lock); + sdev = kvm_shadow_dev_get(kvm, &msi); + raw_spin_unlock(&dist->sdev_list_lock); + + cache->valid = !!sdev; + cache->data = sdev; +} +#endif + /** * vgic_irqfd_set_irq: inject the IRQ corresponding to the * irqchip routing entry diff --git a/include/kvm/arm_vgic.h b/include/kvm/arm_vgic.h index 1b0914ca7f98..2978bc125611 100644 --- a/include/kvm/arm_vgic.h +++ b/include/kvm/arm_vgic.h @@ -434,6 +434,7 @@ void kvm_shadow_dev_init(void); int kvm_shadow_dev_create(struct kvm *kvm, struct kvm_master_dev_info *mdi); void kvm_shadow_dev_delete(struct kvm *kvm, u32 devid); void kvm_shadow_dev_delete_all(struct kvm *kvm); +struct shadow_dev *kvm_shadow_dev_get(struct kvm *kvm, struct kvm_msi *msi); #endif #endif /* __KVM_ARM_VGIC_H */ diff --git a/include/linux/kvm_host.h b/include/linux/kvm_host.h index 5be279c9d4f4..d74f630373ec 100644 --- a/include/linux/kvm_host.h +++ b/include/linux/kvm_host.h @@ -383,6 +383,13 @@ struct kvm_hv_sint { u32 sint; }; +#ifdef CONFIG_VIRT_PLAT_DEV +struct kire_data { + bool valid; + void *data; +}; +#endif + struct kvm_kernel_irq_routing_entry { u32 gsi; u32 type; @@ -405,6 +412,10 @@ struct kvm_kernel_irq_routing_entry { struct kvm_hv_sint hv_sint; }; struct hlist_node link; + +#ifdef CONFIG_VIRT_PLAT_DEV + struct kire_data cache; +#endif }; #ifdef CONFIG_HAVE_KVM_IRQ_ROUTING @@ -1084,6 +1095,11 @@ int kvm_request_irq_source_id(struct kvm *kvm); void kvm_free_irq_source_id(struct kvm *kvm, int irq_source_id); bool kvm_arch_irqfd_allowed(struct kvm *kvm, struct kvm_irqfd *args); +#ifdef CONFIG_VIRT_PLAT_DEV +void kire_arch_cached_data_update(struct kvm *kvm, + struct kvm_kernel_irq_routing_entry *e); +#endif + /* * search_memslots() and __gfn_to_memslot() are here because they are * used in non-modular code in arch/powerpc/kvm/book3s_hv_rm_mmu.c. diff --git a/virt/kvm/eventfd.c b/virt/kvm/eventfd.c index 518cd8dc390e..c01415ee64b4 100644 --- a/virt/kvm/eventfd.c +++ b/virt/kvm/eventfd.c @@ -38,6 +38,14 @@ kvm_arch_irqfd_allowed(struct kvm *kvm, struct kvm_irqfd *args) return true; } +#ifdef CONFIG_VIRT_PLAT_DEV +void __weak +kire_arch_cached_data_update(struct kvm *kvm, + struct kvm_kernel_irq_routing_entry *e) +{ +} +#endif + static void irqfd_inject(struct work_struct *work) { @@ -256,6 +264,9 @@ static void irqfd_update(struct kvm *kvm, struct kvm_kernel_irqfd *irqfd) else irqfd->irq_entry.type = 0; +#ifdef CONFIG_VIRT_PLAT_DEV + kire_arch_cached_data_update(kvm, &irqfd->irq_entry); +#endif write_seqcount_end(&irqfd->irq_entry_sc); } -- Gitee From e20f0c00c649c9744bf45c5896ae2a074cd833b2 Mon Sep 17 00:00:00 2001 From: wanghaibin Date: Thu, 11 Jan 2024 20:43:28 +0800 Subject: [PATCH 07/12] KVM: arm64: sdev: Support virq bypass by INT/VSYNC command ANBZ: #9398 commit 3a95fc619fa462fca60b502ed87cad889bebd22b openEuler. virt inclusion category: feature bugzilla: https://gitee.com/openeuler/kernel/issues/I8UROS CVE: NA -------------------------------- We already have the set_irqchip_state() callback which will issue INT/VSYNC commands on the physical side when the handled IRQ is forwarded to vcpu. It was intended to be used to handle the guest INT command targeting a VLPI. Let's reuse this hack to set the virtio-pci function's VLPI pending, instead of directly writing message data into GITS_TRANSLATER, which should only be treated as the Message Address of PCI devices. Signed-off-by: wanghaibin Signed-off-by: Zenghui Yu Signed-off-by: Kunkun Jiang Signed-off-by: Dongxu Sun Signed-off-by: Guanghui Feng --- arch/arm64/kvm/vgic/vgic-irqfd.c | 21 +++++++++++++++++++++ include/kvm/arm_vgic.h | 3 +++ 2 files changed, 24 insertions(+) diff --git a/arch/arm64/kvm/vgic/vgic-irqfd.c b/arch/arm64/kvm/vgic/vgic-irqfd.c index ff42403482db..4580854aa800 100644 --- a/arch/arm64/kvm/vgic/vgic-irqfd.c +++ b/arch/arm64/kvm/vgic/vgic-irqfd.c @@ -121,6 +121,23 @@ int kvm_set_msi(struct kvm_kernel_irq_routing_entry *e, return vgic_its_inject_msi(kvm, &msi); } +#ifdef CONFIG_VIRT_PLAT_DEV +static int kvm_arch_set_irq_bypass(struct kvm_kernel_irq_routing_entry *e, + struct kvm *kvm) +{ + struct kire_data *cache = &e->cache; + + /* + * FIXME: is there any race against the irqfd_update(), + * where the cache data will be updated? + */ + if (!cache->valid) + return -EWOULDBLOCK; + + return shadow_dev_virq_bypass_inject(kvm, e); +} +#endif + /** * kvm_arch_set_irq_inatomic: fast-path for irqfd injection */ @@ -138,6 +155,10 @@ int kvm_arch_set_irq_inatomic(struct kvm_kernel_irq_routing_entry *e, if (!vgic_has_its(kvm)) break; +#ifdef CONFIG_VIRT_PLAT_DEV + if (!kvm_arch_set_irq_bypass(e, kvm)) + return 0; +#endif kvm_populate_msi(e, &msi); return vgic_its_inject_cached_translation(kvm, &msi); } diff --git a/include/kvm/arm_vgic.h b/include/kvm/arm_vgic.h index 2978bc125611..dd6c668f8c15 100644 --- a/include/kvm/arm_vgic.h +++ b/include/kvm/arm_vgic.h @@ -435,6 +435,9 @@ int kvm_shadow_dev_create(struct kvm *kvm, struct kvm_master_dev_info *mdi); void kvm_shadow_dev_delete(struct kvm *kvm, u32 devid); void kvm_shadow_dev_delete_all(struct kvm *kvm); struct shadow_dev *kvm_shadow_dev_get(struct kvm *kvm, struct kvm_msi *msi); + +int shadow_dev_virq_bypass_inject(struct kvm *kvm, + struct kvm_kernel_irq_routing_entry *e); #endif #endif /* __KVM_ARM_VGIC_H */ -- Gitee From 405d444f45b354df7d7e9b68c9c2ae96b6cbe7e9 Mon Sep 17 00:00:00 2001 From: Dongxu Sun Date: Thu, 11 Jan 2024 20:43:29 +0800 Subject: [PATCH 08/12] KVM: arm64: update arm64 openeuler_defconfig for CONFIG_VIRT_PLAT_DEV ANBZ: #9398 commit 0bd88058ec5229489e2647d35ad89a3998b1cfc8 openEuler. virt inclusion category: feature bugzilla: https://gitee.com/openeuler/kernel/issues/I8UROS ------------------------------- set CONFIG_VIRT_PLAT_DEV=y in arm64 openeuler_defconfig Signed-off-by: Dongxu Sun Signed-off-by: Guanghui Feng --- anolis/configs/L1-RECOMMEND/arm64/CONFIG_VIRT_PLAT_DEV | 1 + 1 file changed, 1 insertion(+) create mode 100644 anolis/configs/L1-RECOMMEND/arm64/CONFIG_VIRT_PLAT_DEV diff --git a/anolis/configs/L1-RECOMMEND/arm64/CONFIG_VIRT_PLAT_DEV b/anolis/configs/L1-RECOMMEND/arm64/CONFIG_VIRT_PLAT_DEV new file mode 100644 index 000000000000..fb8563a8c326 --- /dev/null +++ b/anolis/configs/L1-RECOMMEND/arm64/CONFIG_VIRT_PLAT_DEV @@ -0,0 +1 @@ +CONFIG_VIRT_PLAT_DEV=y -- Gitee From 888360ad2e90e086b6a5e85200842e033370ed0b Mon Sep 17 00:00:00 2001 From: Guanghui Feng Date: Mon, 24 Jun 2024 14:15:03 +0800 Subject: [PATCH 09/12] anolis: configs: update virt platform device direct lpi inject description ANBZ: #9398 Backport patches from openEuler to support virt platform device direct inject lpi irq & update configs document description. Signed-off-by: Guanghui Feng --- anolis/configs/metadata/changelog/CONFIG_VIRT_PLAT_DEV | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 anolis/configs/metadata/changelog/CONFIG_VIRT_PLAT_DEV diff --git a/anolis/configs/metadata/changelog/CONFIG_VIRT_PLAT_DEV b/anolis/configs/metadata/changelog/CONFIG_VIRT_PLAT_DEV new file mode 100644 index 000000000000..71f781a9b647 --- /dev/null +++ b/anolis/configs/metadata/changelog/CONFIG_VIRT_PLAT_DEV @@ -0,0 +1,3 @@ +Backport patches from openEuler to support virt platform device direct inject lpi irq(ANBZ: #9398). + +It's required by internal users(Aone: 56141911). -- Gitee From 7e8bde350407735e66c77346d155c1e7e20aa1ba Mon Sep 17 00:00:00 2001 From: Guanghui Feng Date: Thu, 27 Jun 2024 20:56:31 +0800 Subject: [PATCH 10/12] anolis: KVM: arm64: update arm64 virt platform device param ANBZ: #9398 Update virt platform dev param to support KVM builted into module. Signed-off-by: Guanghui Feng --- arch/arm64/kvm/vgic/shadow_dev.c | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/arch/arm64/kvm/vgic/shadow_dev.c b/arch/arm64/kvm/vgic/shadow_dev.c index 6774801e6024..b62f3002c5f1 100644 --- a/arch/arm64/kvm/vgic/shadow_dev.c +++ b/arch/arm64/kvm/vgic/shadow_dev.c @@ -304,11 +304,7 @@ void kvm_shadow_dev_delete_all(struct kvm *kvm) flush_workqueue(sdev_cleanup_wq); } -static int __init early_virt_msi_bypass(char *buf) -{ - return strtobool(buf, &virt_msi_bypass); -} -early_param("kvm-arm.virt_msi_bypass", early_virt_msi_bypass); +module_param(virt_msi_bypass, bool, 0444); void kvm_shadow_dev_init(void) { -- Gitee From 8c3d5c73b71f149a367ec9ba946eca368896cc0a Mon Sep 17 00:00:00 2001 From: Guanghui Feng Date: Mon, 1 Jul 2024 11:35:34 +0800 Subject: [PATCH 11/12] anolis: KVM: arm64: update arm64 virt platform device supported its ANBZ: #9398 The arm64 virt platform devcie use its vlpi direct inject. There is no need to identify precondition by its ids. Signed-off-by: Guanghui Feng --- drivers/irqchip/irq-gic-v3-its.c | 18 +----------------- 1 file changed, 1 insertion(+), 17 deletions(-) diff --git a/drivers/irqchip/irq-gic-v3-its.c b/drivers/irqchip/irq-gic-v3-its.c index cac0974e5def..205c469acf06 100644 --- a/drivers/irqchip/irq-gic-v3-its.c +++ b/drivers/irqchip/irq-gic-v3-its.c @@ -103,7 +103,7 @@ static void get_rsv_buses_resource(struct plat_rsv_buses *buses) */ } -static int probe_devid_pool_one(void) +static int build_devid_pools(void) { struct rsv_devid_pool *devid_pool; @@ -357,22 +357,6 @@ static int alloc_devid_from_rsv_pools(struct rsv_devid_pool **devid_pool, #define gic_data_rdist_rd_base() (gic_data_rdist()->rd_base) #define gic_data_rdist_vlpi_base() (gic_data_rdist_rd_base() + SZ_128K) -#ifdef CONFIG_VIRT_PLAT_DEV -/* - * Currently we only build *one* devid pool. - */ -static int build_devid_pools(void) -{ - struct its_node *its; - - its = list_first_entry(&its_nodes, struct its_node, entry); - if (readl_relaxed(its->base + GITS_IIDR) != 0x00051736) - return -EINVAL; - - return probe_devid_pool_one(); -} -#endif - /* * Skip ITSs that have no vLPIs mapped, unless we're on GICv4.1, as we * always have vSGIs mapped. -- Gitee From 040ef989d1bbe0e7d75391299f644fa967417a83 Mon Sep 17 00:00:00 2001 From: Guanrui Huang Date: Thu, 18 Apr 2024 14:10:52 +0800 Subject: [PATCH 12/12] irqchip/gic-v3-its: Prevent double free on error ANBZ: #9406 commit c26591afd33adce296c022e3480dea4282b7ef91 upstream. The error handling path in its_vpe_irq_domain_alloc() causes a double free when its_vpe_init() fails after successfully allocating at least one interrupt. This happens because its_vpe_irq_domain_free() frees the interrupts along with the area bitmap and the vprop_page and its_vpe_irq_domain_alloc() subsequently frees the area bitmap and the vprop_page again. Fix this by unconditionally invoking its_vpe_irq_domain_free() which handles all cases correctly and by removing the bitmap/vprop_page freeing from its_vpe_irq_domain_alloc(). [ tglx: Massaged change log ] Fixes: 7d75bbb4bc1a ("irqchip/gic-v3-its: Add VPE irq domain allocation/teardown") Signed-off-by: Guanrui Huang Signed-off-by: Thomas Gleixner Reviewed-by: Marc Zyngier Reviewed-by: Zenghui Yu Cc: stable@vger.kernel.org Link: https://lore.kernel.org/r/20240418061053.96803-2-guanrui.huang@linux.alibaba.com Signed-off-by: Guanghui Feng --- drivers/irqchip/irq-gic-v3-its.c | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/drivers/irqchip/irq-gic-v3-its.c b/drivers/irqchip/irq-gic-v3-its.c index 205c469acf06..93324b6698a9 100644 --- a/drivers/irqchip/irq-gic-v3-its.c +++ b/drivers/irqchip/irq-gic-v3-its.c @@ -4697,13 +4697,8 @@ static int its_vpe_irq_domain_alloc(struct irq_domain *domain, unsigned int virq set_bit(i, bitmap); } - if (err) { - if (i > 0) - its_vpe_irq_domain_free(domain, virq, i); - - its_lpi_free(bitmap, base, nr_ids); - its_free_prop_table(vprop_page); - } + if (err) + its_vpe_irq_domain_free(domain, virq, i); return err; } -- Gitee