[PATCH 00/23] KVM: arm64: rVIC/rVID PV interrupt controller
https://lore.kernel.org/all/20200903152610.1078827-1-maz@kernel.org/ Marc Zyngier (5): irqchip/rvic: Add support for untrusted interrupt allocation irqchip: Add Reduced Virtual Interrupt Distributor support irqchip/rvid: Add PCI MSI support KVM: arm64: Tighten msis_require_devid reporting KVM: arm64: Add debugfs files for the rVIC/rVID implementation wanghaibin (18): irqchip: Add Reduced Virtual Interrupt Controller driver KVM: arm64: Move GIC model out of the distributor KVM: arm64: vgic-v3: Move early init to kvm_vgic_create() KVM: arm64: Add irqchip callback structure to kvm_arch KVM: arm64: Move kvm_vgic_destroy to kvm_irqchip_flow KVM: arm64: Move kvm_vgic_vcpu_init() to irqchip_flow KVM: arm64: Move kvm_vgic_vcpu_[un]blocking() to irqchip_flow KVM: arm64: Move kvm_vgic_vcpu_load/put() to irqchip_flow KVM: arm64: Move kvm_vgic_vcpu_pending_irq() to irqchip_flow KVM: arm64: Move vgic resource mapping on first run to irqchip_flow KVM: arm64: Move kvm_vgic_vcpu_{sync, flush}_hwstate() to irqchip_flow KVM: arm64: nVHE: Only save/restore GICv3 state if modeling a GIC KVM: arm64: Move interrupt injection to irqchip_flow KVM: arm64: Move mapping of HW interrupts into irqchip_flow KVM: arm64: Move set_owner into irqchip_flow KVM: arm64: Turn vgic_initialized into irqchip_finalized KVM: arm64: Move irqfd routing to irqchip_flow KVM: arm64: Add a rVIC/rVID in-kernel implementation arch/arm64/include/asm/kvm_host.h | 12 +- arch/arm64/include/asm/kvm_irq.h | 142 +++ arch/arm64/include/uapi/asm/kvm.h | 10 + arch/arm64/kvm/Makefile | 2 +- arch/arm64/kvm/arch_timer.c | 55 +- arch/arm64/kvm/arm.c | 128 ++- arch/arm64/kvm/hyp/nvhe/switch.c | 10 +- arch/arm64/kvm/hypercalls.c | 13 + arch/arm64/kvm/pmu-emul.c | 10 +- arch/arm64/kvm/rvic-cpu.c | 1217 +++++++++++++++++++++++++ arch/arm64/kvm/vgic/vgic-debug.c | 7 +- arch/arm64/kvm/vgic/vgic-init.c | 122 ++- arch/arm64/kvm/vgic/vgic-irqfd.c | 72 +- arch/arm64/kvm/vgic/vgic-its.c | 2 +- arch/arm64/kvm/vgic/vgic-kvm-device.c | 18 +- arch/arm64/kvm/vgic/vgic-mmio-v3.c | 2 +- arch/arm64/kvm/vgic/vgic-mmio.c | 14 +- arch/arm64/kvm/vgic/vgic-v3.c | 24 +- arch/arm64/kvm/vgic/vgic.c | 55 +- arch/arm64/kvm/vgic/vgic.h | 41 +- arch/arm64/kvm/virtcca_cvm.c | 2 +- drivers/irqchip/Kconfig | 12 + drivers/irqchip/Makefile | 2 + drivers/irqchip/irq-rvic.c | 595 ++++++++++++ drivers/irqchip/irq-rvid.c | 440 +++++++++ include/kvm/arm_rvic.h | 41 + include/kvm/arm_vgic.h | 32 - include/linux/cpuhotplug.h | 1 + include/linux/irqchip/irq-rvic.h | 100 ++ include/uapi/linux/kvm.h | 3 +- 30 files changed, 2945 insertions(+), 239 deletions(-) create mode 100644 arch/arm64/include/asm/kvm_irq.h create mode 100644 arch/arm64/kvm/rvic-cpu.c create mode 100644 drivers/irqchip/irq-rvic.c create mode 100644 drivers/irqchip/irq-rvid.c create mode 100644 include/kvm/arm_rvic.h create mode 100644 include/linux/irqchip/irq-rvic.h -- 2.33.0
From: wanghaibin <wanghaibin.wang@huawei.com> The ARM rVIC is the simplest PV interrupt controller on this side of the universe. I mean it! Signed-off-by: wanghaibin <wanghaibin.wang@huawei.com> --- drivers/irqchip/Kconfig | 6 + drivers/irqchip/Makefile | 1 + drivers/irqchip/irq-rvic.c | 554 +++++++++++++++++++++++++++++++ include/linux/cpuhotplug.h | 1 + include/linux/irqchip/irq-rvic.h | 77 +++++ 5 files changed, 639 insertions(+) create mode 100644 drivers/irqchip/irq-rvic.c create mode 100644 include/linux/irqchip/irq-rvic.h diff --git a/drivers/irqchip/Kconfig b/drivers/irqchip/Kconfig index a15fbf9fa85f..95897923b0c7 100644 --- a/drivers/irqchip/Kconfig +++ b/drivers/irqchip/Kconfig @@ -123,6 +123,12 @@ config ARM_NVIC select IRQ_DOMAIN_HIERARCHY select GENERIC_IRQ_CHIP +config ARM_RVIC + bool + default ARM64 + select IRQ_DOMAIN_HIERARCHY + select GENERIC_IRQ_EFFECTIVE_AFF_MASK + config ARM_VIC bool select IRQ_DOMAIN diff --git a/drivers/irqchip/Makefile b/drivers/irqchip/Makefile index d8778ff3f260..906923c83903 100644 --- a/drivers/irqchip/Makefile +++ b/drivers/irqchip/Makefile @@ -46,6 +46,7 @@ obj-$(CONFIG_ARM_GIC_PHYTIUM_2500) += irq-gic-phytium-2500.o irq-gic-phytium-250 obj-$(CONFIG_PARTITION_PERCPU) += irq-partition-percpu.o obj-$(CONFIG_HISILICON_IRQ_MBIGEN) += irq-mbigen.o obj-$(CONFIG_ARM_NVIC) += irq-nvic.o +obj-$(CONFIG_ARM_RVIC) += irq-rvic.o obj-$(CONFIG_ARM_VIC) += irq-vic.o obj-$(CONFIG_ARMADA_370_XP_IRQ) += irq-armada-370-xp.o obj-$(CONFIG_ATMEL_AIC_IRQ) += irq-atmel-aic-common.o irq-atmel-aic.o diff --git a/drivers/irqchip/irq-rvic.c b/drivers/irqchip/irq-rvic.c new file mode 100644 index 000000000000..1c65ccabbf5a --- /dev/null +++ b/drivers/irqchip/irq-rvic.c @@ -0,0 +1,554 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * A Reduced Virtual Interrupt Controler driver + * + * Initial draft from Alex Shirshikov <alexander.shirshikov@arm.com> + * + * Copyright 2020 Google LLC. + * Author: Marc Zyngier <maz@kernel.org> + */ + + +#define pr_fmt(fmt) "rVIC: " fmt + +#include <linux/arm-smccc.h> +#include <linux/cpuhotplug.h> +#include <linux/interrupt.h> +#include <linux/irq.h> +#include <linux/irqchip.h> +#include <linux/irqdomain.h> + +#include <linux/irqchip/irq-rvic.h> + +#include <asm/exception.h> + +#define RVIC_WARN_INTID_TARGET(r, s, intid, mpid) \ + WARN((r), "Error " s " INTID%ld target %llx (%ld, %ld)\n", \ + (intid), (mpid), \ + RVIC_STATUS_REASON((r)), RVIC_STATUS_INDEX((r))); + +#define RVIC_WARN_INTID(r, s, intid) \ + WARN((r), "Error " s " INTID%ld (%ld, %ld)\n", \ + (intid), \ + RVIC_STATUS_REASON((r)), RVIC_STATUS_INDEX((r))); + +static DEFINE_PER_CPU(unsigned long *, trusted_masked); + +struct rvic_data { + struct fwnode_handle *fwnode; + struct irq_domain *domain; + unsigned int nr_trusted; + unsigned int nr_untrusted; +}; + +static struct rvic_data rvic; + +static inline int rvic_version(unsigned long *version) +{ + struct arm_smccc_res res; + + arm_smccc_1_1_invoke(SMC64_RVIC_VERSION, &res); + if (res.a0 == RVIC_STATUS_SUCCESS) + *version = res.a1; + return res.a0; +} + +static inline unsigned long rvic_info(unsigned long key, + unsigned long *value) +{ + struct arm_smccc_res res; + + arm_smccc_1_1_invoke(SMC64_RVIC_INFO, key, &res); + if (res.a0 == RVIC_STATUS_SUCCESS) + *value = res.a1; + return res.a0; +} + +static inline unsigned long rvic_enable(void) +{ + struct arm_smccc_res res; + + arm_smccc_1_1_invoke(SMC64_RVIC_ENABLE, &res); + return res.a0; +} + +static inline unsigned long rvic_disable(void) +{ + struct arm_smccc_res res; + + arm_smccc_1_1_invoke(SMC64_RVIC_DISABLE, &res); + return res.a0; +} + +static inline unsigned long rvic_set_masked(unsigned long target, + unsigned long intid) +{ + struct arm_smccc_res res; + + arm_smccc_1_1_invoke(SMC64_RVIC_SET_MASKED, target, intid, &res); + return res.a0; +} + +static inline unsigned long rvic_clear_masked(unsigned long target, + unsigned long intid) +{ + struct arm_smccc_res res; + + arm_smccc_1_1_invoke(SMC64_RVIC_CLEAR_MASKED, target, intid, &res); + return res.a0; +} + +static inline unsigned long rvic_is_pending(unsigned long target, + unsigned long intid, + bool *is_pending) +{ + struct arm_smccc_res res; + + arm_smccc_1_1_invoke(SMC64_RVIC_IS_PENDING, target, intid, &res); + if (res.a0 == RVIC_STATUS_SUCCESS) + *is_pending = res.a1; + return res.a0; +} + +static inline unsigned long rvic_signal(unsigned long target, + unsigned long intid) +{ + struct arm_smccc_res res; + + arm_smccc_1_1_invoke(SMC64_RVIC_SIGNAL, target, intid, &res); + return res.a0; +} + +static inline unsigned long rvic_clear_pending(unsigned long target, + unsigned long intid) +{ + struct arm_smccc_res res; + + arm_smccc_1_1_invoke(SMC64_RVIC_CLEAR_PENDING, target, intid, &res); + return res.a0; +} + +static inline unsigned long rvic_acknowledge(unsigned long *intid) +{ + struct arm_smccc_res res; + + arm_smccc_1_1_invoke(SMC64_RVIC_ACKNOWLEDGE, &res); + if (res.a0 == RVIC_STATUS_SUCCESS) + *intid = res.a1; + return res.a0; +} + +static inline unsigned long rvic_resample(unsigned long intid) +{ + struct arm_smccc_res res; + + arm_smccc_1_1_invoke(SMC64_RVIC_RESAMPLE, intid, &res); + return res.a0; +} + +static u64 rvic_irq_to_mpidr(struct irq_data *data, int *cpup) +{ + int cpu; + + if (data->hwirq < rvic.nr_trusted) { + *cpup = smp_processor_id(); + return read_cpuid_mpidr() & MPIDR_HWID_BITMASK; + } + + cpu = cpumask_first(data->common->effective_affinity); + *cpup = cpu; + return cpu_logical_map(cpu) & MPIDR_HWID_BITMASK; +} + +static void rvic_irq_mask(struct irq_data *data) +{ + unsigned long ret; + u64 mpidr; + int cpu; + + mpidr = rvic_irq_to_mpidr(data, &cpu); + pr_debug("%llu irq %d hwirq %ld masked\n", + mpidr, data->irq, data->hwirq); + ret = rvic_set_masked(mpidr, data->hwirq); + RVIC_WARN_INTID_TARGET(ret, "masking", data->hwirq, mpidr); + + if (data->hwirq < rvic.nr_trusted) + set_bit(data->hwirq, per_cpu(trusted_masked, cpu)); +} + +static void rvic_irq_unmask(struct irq_data *data) +{ + unsigned long ret; + u64 mpidr; + int cpu; + + mpidr = rvic_irq_to_mpidr(data, &cpu); + pr_debug("%llu irq %d hwirq %ld unmasked\n", + mpidr, data->irq, data->hwirq); + ret = rvic_clear_masked(mpidr, data->hwirq); + RVIC_WARN_INTID_TARGET(ret, "unmasking", data->hwirq, mpidr); + + if (data->hwirq < rvic.nr_trusted) + clear_bit(data->hwirq, per_cpu(trusted_masked, cpu)); +} + +static void rvic_irq_eoi(struct irq_data *data) +{ + bool masked; + + /* Resampling is only available on trusted interrupts for now */ + if (data->hwirq < rvic.nr_trusted && + (irqd_get_trigger_type(data) & IRQ_TYPE_LEVEL_MASK)) { + unsigned long ret; + + ret = rvic_resample(data->hwirq); + RVIC_WARN_INTID(ret, "resampling", data->hwirq); + } + + /* irqd_irq_masked doesn't work on percpu-devid interrupts. */ + if (data->hwirq < rvic.nr_trusted) + masked = test_bit(data->hwirq, *this_cpu_ptr(&trusted_masked)); + else + masked = irqd_irq_masked(data); + + if (!masked) + rvic_irq_unmask(data); +} + +static void rvic_ipi_send_mask(struct irq_data *data, + const struct cpumask *mask) +{ + int cpu; + + for_each_cpu(cpu, mask) { + u64 mpidr = cpu_logical_map(cpu) & MPIDR_HWID_BITMASK; + unsigned long ret; + + ret = rvic_signal(mpidr, data->hwirq); + RVIC_WARN_INTID_TARGET(ret, "signaling", data->hwirq, mpidr); + } +} + +static int rvic_irq_get_irqchip_state(struct irq_data *data, + enum irqchip_irq_state which, bool *val) +{ + unsigned long ret; + u64 mpidr; + int cpu; + + mpidr = rvic_irq_to_mpidr(data, &cpu); + + switch (which) { + case IRQCHIP_STATE_PENDING: + ret = rvic_is_pending(mpidr, data->hwirq, val); + RVIC_WARN_INTID_TARGET(ret, "getting pending state", + data->hwirq, mpidr); + return ret ? -EINVAL : 0; + + default: + return -EINVAL; + }; +} + +static int rvic_irq_set_irqchip_state(struct irq_data *data, + enum irqchip_irq_state which, bool val) +{ + unsigned long ret; + u64 mpidr; + int cpu; + + mpidr = rvic_irq_to_mpidr(data, &cpu); + + switch (which) { + case IRQCHIP_STATE_PENDING: + if (val) + ret = rvic_signal(mpidr, data->hwirq); + else + ret = rvic_clear_pending(mpidr, data->hwirq); + RVIC_WARN_INTID_TARGET(ret, "setting pending state", + data->hwirq, mpidr); + return ret ? -EINVAL : 0; + + case IRQCHIP_STATE_MASKED: + if (val) + ret = rvic_set_masked(mpidr, data->hwirq); + else + ret = rvic_clear_masked(mpidr, data->hwirq); + RVIC_WARN_INTID_TARGET(ret, "setting masked state", + data->hwirq, mpidr); + return ret ? -EINVAL : 0; + + default: + return -EINVAL; + } + +} + +static int rvic_irq_retrigger(struct irq_data *data) +{ + return !!rvic_irq_set_irqchip_state(data, IRQCHIP_STATE_PENDING, true); +} + +static int rvic_set_type(struct irq_data *data, unsigned int type) +{ + /* + * Nothing to do here, we're always edge under the hood. Just + * weed out untrusted interrupts, as they cannot be level yet. + */ + switch (type) { + case IRQ_TYPE_LEVEL_HIGH: + if (data->hwirq >= rvic.nr_trusted) + return -EINVAL; + + fallthrough; + case IRQ_TYPE_EDGE_RISING: + return 0; + default: + return -EINVAL; + } +} + +static struct irq_chip rvic_chip = { + .name = "rvic", + .irq_mask = rvic_irq_mask, + .irq_unmask = rvic_irq_unmask, + .irq_eoi = rvic_irq_eoi, + .ipi_send_mask = rvic_ipi_send_mask, + .irq_get_irqchip_state = rvic_irq_get_irqchip_state, + .irq_set_irqchip_state = rvic_irq_set_irqchip_state, + .irq_retrigger = rvic_irq_retrigger, + .irq_set_type = rvic_set_type, +}; + +static asmlinkage void __exception_irq_entry rvic_handle_irq(struct pt_regs *regs) +{ + unsigned long ret, intid; + int err; + + ret = rvic_acknowledge(&intid); + if (unlikely(ret == RVIC_STATUS_NO_INTERRUPTS)) { + pr_debug("CPU%d: Spurious interrupt\n", smp_processor_id()); + return; + } + + if ((unlikely(ret))) { + WARN(1, "rVIC: Error acknowledging interrupt (%ld, %ld)\n", + RVIC_STATUS_REASON(ret), + RVIC_STATUS_INDEX(ret)); + return; + } + + if (unlikely(intid >= (rvic.nr_trusted + rvic.nr_untrusted))) { + WARN(1, "Unexpected intid out of range (%lu)\n", intid); + return; + } + + pr_debug("CPU%d: IRQ%ld\n", smp_processor_id(), intid); + err = generic_handle_domain_irq(rvic.domain, intid); + WARN_ONCE(err, "Unexpected interrupt received %d\n", err); +} + +static int rvic_irq_domain_alloc(struct irq_domain *domain, unsigned int virq, + unsigned int nr_irqs, void *arg) +{ + struct irq_fwspec *fwspec = arg; + unsigned int type = IRQ_TYPE_NONE; + irq_hw_number_t hwirq; + int i, ret; + + ret = irq_domain_translate_twocell(domain, fwspec, &hwirq, &type); + if (ret) + return ret; + + for (i = 0; i < nr_irqs; i++) { + unsigned int intid = hwirq + i; + unsigned int irq = virq + i; + + if (intid < 16) { + irq_set_percpu_devid(irq); + irq_domain_set_info(domain, irq, intid, &rvic_chip, + domain->host_data, + handle_percpu_devid_irq, + NULL, NULL); + } else if (intid < rvic.nr_trusted) { + irq_set_percpu_devid(irq); + irq_domain_set_info(domain, irq, intid, &rvic_chip, + domain->host_data, + handle_percpu_devid_irq, + NULL, NULL); + } else { + return -EINVAL; + } + } + + return 0; +} + +static void rvic_irq_domain_free(struct irq_domain *domain, unsigned int virq, + unsigned int nr_irqs) +{ + int i; + + for (i = 0; i < nr_irqs; i++) { + struct irq_data *d = irq_domain_get_irq_data(domain, virq + i); + irq_set_handler(virq + i, NULL); + irq_domain_reset_irq_data(d); + } +} + +static const struct irq_domain_ops rvic_irq_domain_ops = { + .translate = irq_domain_translate_twocell, + .alloc = rvic_irq_domain_alloc, + .free = rvic_irq_domain_free, +}; + +static int rvic_cpu_starting(unsigned int cpu) +{ + u64 mpidr = read_cpuid_mpidr() & MPIDR_HWID_BITMASK; + unsigned long ret; + int i; + + rvic_disable(); + + for (i = 0; i < (rvic.nr_trusted + rvic.nr_untrusted); i++) { + rvic_set_masked(mpidr, i); + rvic_clear_pending(mpidr, i); + } + + ret = rvic_enable(); + if (ret != RVIC_STATUS_SUCCESS) { + pr_err("rVIC: error enabling instance (%ld, %ld)\n", + RVIC_STATUS_REASON(ret), + RVIC_STATUS_INDEX(ret)); + return -ENXIO; + } + + return 0; +} + +static int rvic_cpu_dying(unsigned int cpu) +{ + unsigned long ret; + + ret = rvic_disable(); + if (ret != RVIC_STATUS_SUCCESS) { + pr_err("rVIC: error disabling instance (%ld, %ld)\n", + RVIC_STATUS_REASON(ret), + RVIC_STATUS_INDEX(ret)); + return -ENXIO; + } + + return 0; +} + +static void __init rvic_smp_init(struct fwnode_handle *fwnode) +{ + struct irq_fwspec ipi_fwspec = { + .fwnode = fwnode, + .param_count = 2, + .param[0] = 0, + .param[1] = IRQ_TYPE_EDGE_RISING, + }; + int base_ipi; + + cpuhp_setup_state(CPUHP_AP_IRQ_RVIC_STARTING, "irqchip/rvic:starting", + rvic_cpu_starting, rvic_cpu_dying); + + base_ipi = __irq_domain_alloc_irqs(rvic.domain, -1, 16, + NUMA_NO_NODE, &ipi_fwspec, + false, NULL); + if (WARN_ON(base_ipi < 0)) + return; + + set_smp_ipi_range(base_ipi, 16); +} + +static int __init rvic_init(struct device_node *node, + struct device_node *parent) +{ + unsigned long ret, version, val; + int cpu; + + if (arm_smccc_get_version() < ARM_SMCCC_VERSION_1_1) { + pr_err("SMCCC 1.1 required, abording\n"); + return -EINVAL; + } + + rvic.fwnode = of_node_to_fwnode(node); + + ret = rvic_version(&version); + if (ret != RVIC_STATUS_SUCCESS) { + pr_err("error retrieving version (%ld, %ld)\n", + RVIC_STATUS_REASON(ret), + RVIC_STATUS_INDEX(ret)); + return -ENXIO; + } + + if (version < RVIC_VERSION(0, 3)) { + pr_err("version (%ld, %ld) too old, expected min. (%d, %d)\n", + RVIC_VERSION_MAJOR(version), + RVIC_VERSION_MINOR(version), + 0, 3); + return -ENXIO; + } + + ret = rvic_info(RVIC_INFO_KEY_NR_TRUSTED_INTERRUPTS, &val); + if (ret != RVIC_STATUS_SUCCESS) { + pr_err("error retrieving nr of trusted interrupts (%ld, %ld)\n", + RVIC_STATUS_REASON(ret), + RVIC_STATUS_INDEX(ret)); + return -ENXIO; + } + + rvic.nr_trusted = val; + + ret = rvic_info(RVIC_INFO_KEY_NR_UNTRUSTED_INTERRUPTS, &val); + if (ret != RVIC_STATUS_SUCCESS) { + pr_err("error retrieving nr of untrusted interrupts (%ld, %ld)\n", + RVIC_STATUS_REASON(ret), + RVIC_STATUS_INDEX(ret)); + return -ENXIO; + } + + rvic.nr_untrusted = val; + + pr_info("probed %u trusted interrupts, %u untrusted interrupts\n", + rvic.nr_trusted, rvic.nr_untrusted); + + rvic.domain = irq_domain_create_linear(rvic.fwnode, + rvic.nr_trusted + rvic.nr_untrusted, + &rvic_irq_domain_ops, &rvic); + if (!rvic.domain) { + pr_warn("Failed to allocate irq domain\n"); + return -ENOMEM; + } + + for_each_possible_cpu(cpu) { + unsigned long *map = bitmap_alloc(rvic.nr_trusted, GFP_KERNEL); + + if (!map) { + pr_warn("Failed to allocate trusted bitmap (CPU %d)\n", + cpu); + goto free_percpu; + } + + /* Default to masked */ + bitmap_fill(map, rvic.nr_trusted); + per_cpu(trusted_masked, cpu) = map; + } + + rvic_smp_init(rvic.fwnode); + set_handle_irq(rvic_handle_irq); + + return 0; + +free_percpu: + for_each_possible_cpu(cpu) + kfree(per_cpu(trusted_masked, cpu)); + + irq_domain_remove(rvic.domain); + + return -ENOMEM; +} + +IRQCHIP_DECLARE(rvic, "arm,rvic", rvic_init); diff --git a/include/linux/cpuhotplug.h b/include/linux/cpuhotplug.h index 89fd528e8ef4..069e60fad2f1 100644 --- a/include/linux/cpuhotplug.h +++ b/include/linux/cpuhotplug.h @@ -150,6 +150,7 @@ enum cpuhp_state { CPUHP_AP_IRQ_APPLE_AIC_STARTING, CPUHP_AP_IRQ_ARMADA_XP_STARTING, CPUHP_AP_IRQ_BCM2836_STARTING, + CPUHP_AP_IRQ_RVIC_STARTING, CPUHP_AP_IRQ_MIPS_GIC_STARTING, CPUHP_AP_IRQ_RISCV_STARTING, CPUHP_AP_IRQ_LOONGARCH_STARTING, diff --git a/include/linux/irqchip/irq-rvic.h b/include/linux/irqchip/irq-rvic.h new file mode 100644 index 000000000000..0176ca7d3c30 --- /dev/null +++ b/include/linux/irqchip/irq-rvic.h @@ -0,0 +1,77 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Definitions for rVIC/rVID PV interrupt controller architecture. + * + * Copyright 2020 Google LLC. + * Author: Marc Zyngier <maz@kernel.org> + * + * WARNING: All these constants are subject to change until the spec is final. + */ + +#ifndef __IRQCHIP_IRQ_RVIC_H__ +#define __IRQCHIP_IRQ_RVIC_H__ + +#include <linux/bitfield.h> + +/* Versioning */ +#define RVIx_VERSION_MAJOR_MASK GENMASK(31, 16) +#define RVIx_VERSION_MINOR_MASK GENMASK(15, 0) + +#define RVIx_VERSION(M, m) \ + (FIELD_PREP(RVIx_VERSION_MAJOR_MASK, (M)) | \ + FIELD_PREP(RVIx_VERSION_MINOR_MASK, (m))) + +#define RVIx_VERSION_MAJOR(v) FIELD_GET(RVIx_VERSION_MAJOR_MASK, (v)) +#define RVIx_VERSION_MINOR(v) FIELD_GET(RVIx_VERSION_MINOR_MASK, (v)) + +/* Error reporting */ +#define RVIx_STATUS_REASON_MASK GENMASK(7, 0) +#define RVIx_STATUS_INDEX_MASK GENMASK(15, 8) + +#define RVIx_STATUS_PACK(r,i) \ + (FIELD_PREP(RVIx_STATUS_REASON_MASK, (r)) | \ + FIELD_PREP(RVIx_STATUS_INDEX_MASK, (i))) + +#define RVIx_STATUS_REASON(c) FIELD_GET(RVIx_STATUS_REASON_MASK, (c)) +#define RVIx_STATUS_INDEX(c) FIELD_GET(RVIx_STATUS_INDEX_MASK, (c)) + +#define RVIx_STATUS_SUCCESS 0 +#define RVIx_STATUS_ERROR_PARAMETER 1 +#define RVIx_STATUS_INVALID_CPU 2 +#define RVIx_STATUS_DISABLED 3 +#define RVIx_STATUS_NO_INTERRUPTS 4 + +/* rVIC functions */ +#define SMC64_RVIC_BASE 0xc5000200 +#define SMC64_RVIC_FN(n) (SMC64_RVIC_BASE + (n)) + +#define SMC64_RVIC_VERSION SMC64_RVIC_FN(0) +#define SMC64_RVIC_INFO SMC64_RVIC_FN(1) +#define SMC64_RVIC_ENABLE SMC64_RVIC_FN(2) +#define SMC64_RVIC_DISABLE SMC64_RVIC_FN(3) +#define SMC64_RVIC_SET_MASKED SMC64_RVIC_FN(4) +#define SMC64_RVIC_CLEAR_MASKED SMC64_RVIC_FN(5) +#define SMC64_RVIC_IS_PENDING SMC64_RVIC_FN(6) +#define SMC64_RVIC_SIGNAL SMC64_RVIC_FN(7) +#define SMC64_RVIC_CLEAR_PENDING SMC64_RVIC_FN(8) +#define SMC64_RVIC_ACKNOWLEDGE SMC64_RVIC_FN(9) +#define SMC64_RVIC_RESAMPLE SMC64_RVIC_FN(10) + +#define RVIC_INFO_KEY_NR_TRUSTED_INTERRUPTS 0 +#define RVIC_INFO_KEY_NR_UNTRUSTED_INTERRUPTS 1 + +#define RVIC_VERSION(M, m) RVIx_VERSION((M), (m)) + +#define RVIC_VERSION_MAJOR(v) RVIx_VERSION_MAJOR((v)) +#define RVIC_VERSION_MINOR(v) RVIx_VERSION_MINOR((v)) + +#define RVIC_STATUS_REASON(c) RVIx_STATUS_REASON((c)) +#define RVIC_STATUS_INDEX(c) RVIx_STATUS_INDEX((c)) + +#define RVIC_STATUS_SUCCESS RVIx_STATUS_SUCCESS +#define RVIC_STATUS_ERROR_PARAMETER RVIx_STATUS_ERROR_PARAMETER +#define RVIC_STATUS_INVALID_CPU RVIx_STATUS_INVALID_CPU +#define RVIC_STATUS_DISABLED RVIx_STATUS_DISABLED +#define RVIC_STATUS_NO_INTERRUPTS RVIx_STATUS_NO_INTERRUPTS + +#endif /* __IRQCHIP_IRQ_RVIC_H__ */ -- 2.33.0
From: Marc Zyngier <maz@kernel.org> Signed-off-by: Marc Zyngier <maz@kernel.org> Signed-off-by: wanghaibin <wanghaibin.wang@huawei.com> --- drivers/irqchip/irq-rvic.c | 47 +++++++++++++++++++++++++++++++++++--- 1 file changed, 44 insertions(+), 3 deletions(-) diff --git a/drivers/irqchip/irq-rvic.c b/drivers/irqchip/irq-rvic.c index 1c65ccabbf5a..c89d958fac7a 100644 --- a/drivers/irqchip/irq-rvic.c +++ b/drivers/irqchip/irq-rvic.c @@ -37,6 +37,8 @@ static DEFINE_PER_CPU(unsigned long *, trusted_masked); struct rvic_data { struct fwnode_handle *fwnode; struct irq_domain *domain; + unsigned long *bitmap; + struct mutex lock; unsigned int nr_trusted; unsigned int nr_untrusted; }; @@ -356,9 +358,26 @@ static int rvic_irq_domain_alloc(struct irq_domain *domain, unsigned int virq, irq_hw_number_t hwirq; int i, ret; - ret = irq_domain_translate_twocell(domain, fwspec, &hwirq, &type); - if (ret) - return ret; + if (fwspec) { + ret = irq_domain_translate_twocell(domain, fwspec, + &hwirq, &type); + if (ret) + return ret; + } else { + /* rVID wants untrusted interrupts */ + mutex_lock(&rvic.lock); + hwirq = bitmap_find_next_zero_area(rvic.bitmap, + rvic.nr_untrusted, + 0, nr_irqs, 0); + if (hwirq < rvic.nr_untrusted) + bitmap_set(rvic.bitmap, hwirq, nr_irqs); + mutex_unlock(&rvic.lock); + + if (hwirq >= rvic.nr_untrusted) + return -ENOSPC; + + hwirq += rvic.nr_trusted; + } for (i = 0; i < nr_irqs; i++) { unsigned int intid = hwirq + i; @@ -376,6 +395,12 @@ static int rvic_irq_domain_alloc(struct irq_domain *domain, unsigned int virq, domain->host_data, handle_percpu_devid_irq, NULL, NULL); + } else if (intid < (rvic.nr_trusted + rvic.nr_untrusted)) { + irqd_set_single_target(irq_desc_get_irq_data(irq_to_desc(irq))); + irq_domain_set_info(domain, irq, intid, &rvic_chip, + domain->host_data, + handle_fasteoi_irq, + NULL, NULL); } else { return -EINVAL; } @@ -391,6 +416,11 @@ static void rvic_irq_domain_free(struct irq_domain *domain, unsigned int virq, for (i = 0; i < nr_irqs; i++) { struct irq_data *d = irq_domain_get_irq_data(domain, virq + i); + if (d->hwirq >= rvic.nr_trusted) { + mutex_lock(&rvic.lock); + __clear_bit(d->hwirq, rvic.bitmap); + mutex_unlock(&rvic.lock); + } irq_set_handler(virq + i, NULL); irq_domain_reset_irq_data(d); } @@ -523,6 +553,12 @@ static int __init rvic_init(struct device_node *node, return -ENOMEM; } + rvic.bitmap = bitmap_alloc(rvic.nr_untrusted, GFP_KERNEL | __GFP_ZERO); + if (!rvic.bitmap) { + pr_warn("Failed to allocate untrusted bitmap\n"); + goto free_domain; + } + for_each_possible_cpu(cpu) { unsigned long *map = bitmap_alloc(rvic.nr_trusted, GFP_KERNEL); @@ -537,6 +573,8 @@ static int __init rvic_init(struct device_node *node, per_cpu(trusted_masked, cpu) = map; } + mutex_init(&rvic.lock); + rvic_smp_init(rvic.fwnode); set_handle_irq(rvic_handle_irq); @@ -546,6 +584,9 @@ static int __init rvic_init(struct device_node *node, for_each_possible_cpu(cpu) kfree(per_cpu(trusted_masked, cpu)); + kfree(rvic.bitmap); + +free_domain: irq_domain_remove(rvic.domain); return -ENOMEM; -- 2.33.0
From: Marc Zyngier <maz@kernel.org> Signed-off-by: Marc Zyngier <maz@kernel.org> Signed-off-by: wanghaibin <wanghaibin.wang@huawei.com> --- drivers/irqchip/Kconfig | 6 + drivers/irqchip/Makefile | 1 + drivers/irqchip/irq-rvid.c | 259 +++++++++++++++++++++++++++++++ include/linux/irqchip/irq-rvic.h | 19 +++ 4 files changed, 285 insertions(+) create mode 100644 drivers/irqchip/irq-rvid.c diff --git a/drivers/irqchip/Kconfig b/drivers/irqchip/Kconfig index 95897923b0c7..ace9f1c55fe4 100644 --- a/drivers/irqchip/Kconfig +++ b/drivers/irqchip/Kconfig @@ -129,6 +129,12 @@ config ARM_RVIC select IRQ_DOMAIN_HIERARCHY select GENERIC_IRQ_EFFECTIVE_AFF_MASK +config ARM_RVID + bool + default ARM64 + select IRQ_DOMAIN_HIERARCHY + select GENERIC_IRQ_EFFECTIVE_AFF_MASK + config ARM_VIC bool select IRQ_DOMAIN diff --git a/drivers/irqchip/Makefile b/drivers/irqchip/Makefile index 906923c83903..3669628d298d 100644 --- a/drivers/irqchip/Makefile +++ b/drivers/irqchip/Makefile @@ -47,6 +47,7 @@ obj-$(CONFIG_PARTITION_PERCPU) += irq-partition-percpu.o obj-$(CONFIG_HISILICON_IRQ_MBIGEN) += irq-mbigen.o obj-$(CONFIG_ARM_NVIC) += irq-nvic.o obj-$(CONFIG_ARM_RVIC) += irq-rvic.o +obj-$(CONFIG_ARM_RVID) += irq-rvid.o obj-$(CONFIG_ARM_VIC) += irq-vic.o obj-$(CONFIG_ARMADA_370_XP_IRQ) += irq-armada-370-xp.o obj-$(CONFIG_ATMEL_AIC_IRQ) += irq-atmel-aic-common.o irq-atmel-aic.o diff --git a/drivers/irqchip/irq-rvid.c b/drivers/irqchip/irq-rvid.c new file mode 100644 index 000000000000..953f654e58d4 --- /dev/null +++ b/drivers/irqchip/irq-rvid.c @@ -0,0 +1,259 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright 2020 Google LLC. + * Author: Marc Zyngier <maz@kernel.org> + */ + +#define pr_fmt(fmt) "rVID: " fmt + +#include <linux/arm-smccc.h> +#include <linux/cpuhotplug.h> +#include <linux/interrupt.h> +#include <linux/irq.h> +#include <linux/irqchip.h> +#include <linux/irqdomain.h> + +#include <linux/irqchip/irq-rvic.h> + +struct rvid_data { + struct fwnode_handle *fwnode; + struct irq_domain *domain; +}; + +static struct rvid_data rvid; + +static inline int rvid_version(unsigned long *version) +{ + struct arm_smccc_res res; + + arm_smccc_1_1_invoke(SMC64_RVID_VERSION, &res); + if (res.a0 == RVID_STATUS_SUCCESS) + *version = res.a1; + return res.a0; +} + +static inline int rvid_map(unsigned long input, + unsigned long target, unsigned long intid) +{ + struct arm_smccc_res res; + + arm_smccc_1_1_invoke(SMC64_RVID_MAP, input, target, intid, &res); + return res.a0; +} + +static inline int rvid_unmap(unsigned long input) +{ + struct arm_smccc_res res; + + arm_smccc_1_1_invoke(SMC64_RVID_UNMAP, input, &res); + return res.a0; +} + +static int rvid_irq_set_affinity(struct irq_data *data, + const struct cpumask *mask_val, + bool force) +{ + unsigned int old_cpu, cpu; + bool masked, pending; + int err = 0, ret; + u64 mpidr; + + if (force) + cpu = cpumask_first(mask_val); + else + cpu = cpumask_any_and(mask_val, cpu_online_mask); + + if (cpu >= nr_cpu_ids) + return -EINVAL; + + mpidr = cpu_logical_map(cpu) & MPIDR_HWID_BITMASK; + old_cpu = cpumask_first(data->common->effective_affinity); + if (cpu == old_cpu) + return 0; + + /* Mask on source */ + masked = irqd_irq_masked(data); + if (!masked) + irq_chip_mask_parent(data); + + /* Switch to target */ + irq_data_update_effective_affinity(data, cpumask_of(cpu)); + + /* Mask on target */ + irq_chip_mask_parent(data); + + /* Map the input signal to the new target */ + ret = rvid_map(data->hwirq, mpidr, data->parent_data->hwirq); + if (ret != RVID_STATUS_SUCCESS) { + err = -ENXIO; + goto unmask; + } + + /* Back to the source */ + irq_data_update_effective_affinity(data, cpumask_of(old_cpu)); + + /* Sample pending state and clear it if necessary */ + err = irq_chip_get_parent_state(data, IRQCHIP_STATE_PENDING, &pending); + if (err) + goto unmask; + if (pending) + irq_chip_set_parent_state(data, IRQCHIP_STATE_PENDING, false); + + /* + * To the target again (for good this time), propagating the + * pending bit if required. + */ + irq_data_update_effective_affinity(data, cpumask_of(cpu)); + if (pending) + irq_chip_set_parent_state(data, IRQCHIP_STATE_PENDING, true); +unmask: + /* Propagate the masking state */ + if (!masked) + irq_chip_unmask_parent(data); + + return err; +} + +static struct irq_chip rvid_chip = { + .name = "rvid", + .irq_mask = irq_chip_mask_parent, + .irq_unmask = irq_chip_unmask_parent, + .irq_eoi = irq_chip_eoi_parent, + .irq_get_irqchip_state = irq_chip_get_parent_state, + .irq_set_irqchip_state = irq_chip_set_parent_state, + .irq_retrigger = irq_chip_retrigger_hierarchy, + .irq_set_type = irq_chip_set_type_parent, + .irq_set_affinity = rvid_irq_set_affinity, +}; + +static int rvid_irq_domain_alloc(struct irq_domain *domain, unsigned int virq, + unsigned int nr_irqs, void *arg) +{ + struct irq_fwspec *fwspec = arg; + unsigned int type = IRQ_TYPE_NONE; + irq_hw_number_t hwirq; + int i, ret; + + ret = irq_domain_translate_onecell(domain, fwspec, &hwirq, &type); + if (ret) + return ret; + + for (i = 0; i < nr_irqs; i++) { + unsigned int intid = hwirq + i; + unsigned int irq = virq + i; + + /* Get the rVIC to allocate any untrusted intid */ + ret = irq_domain_alloc_irqs_parent(domain, irq, 1, NULL); + if (WARN_ON(ret)) + return ret; + + irq_domain_set_hwirq_and_chip(domain, irq, intid, + &rvid_chip, &rvid); + irqd_set_affinity_on_activate(irq_get_irq_data(irq)); + } + + return 0; +} + +static void rvid_irq_domain_free(struct irq_domain *domain, unsigned int virq, + unsigned int nr_irqs) +{ + int i; + + irq_domain_free_irqs_parent(domain, virq, nr_irqs); + + for (i = 0; i < nr_irqs; i++) { + struct irq_data *d; + + d = irq_domain_get_irq_data(domain, virq + i); + irq_set_handler(virq + i, NULL); + irq_domain_reset_irq_data(d); + } +} + +static int rvid_irq_domain_activate(struct irq_domain *domain, + struct irq_data *data, bool reserve) +{ + unsigned long ret; + int cpu, err = 0; + u64 mpidr; + + cpu = get_cpu(); + mpidr = cpu_logical_map(cpu) & MPIDR_HWID_BITMASK; + + /* Map the input signal */ + ret = rvid_map(data->hwirq, mpidr, data->parent_data->hwirq); + if (ret != RVID_STATUS_SUCCESS) { + err = -ENXIO; + goto out; + } + + irq_data_update_effective_affinity(data, cpumask_of(cpu)); + +out: + put_cpu(); + return err; +} + +static void rvid_irq_domain_deactivate(struct irq_domain *domain, + struct irq_data *data) +{ + rvid_unmap(data->hwirq); +} + +static const struct irq_domain_ops rvid_irq_domain_ops = { + .translate = irq_domain_translate_onecell, + .alloc = rvid_irq_domain_alloc, + .free = rvid_irq_domain_free, + .activate = rvid_irq_domain_activate, + .deactivate = rvid_irq_domain_deactivate, +}; + +static int __init rvid_init(struct device_node *node, + struct device_node *parent) +{ + struct irq_domain *parent_domain; + unsigned long ret, version; + + if (arm_smccc_get_version() < ARM_SMCCC_VERSION_1_1) { + pr_err("SMCCC 1.1 required, aborting\n"); + return -EINVAL; + } + + if (!parent) + return -ENXIO; + + parent_domain = irq_find_host(parent); + if (!parent_domain) + return -ENXIO; + + rvid.fwnode = of_node_to_fwnode(node); + + ret = rvid_version(&version); + if (ret != RVID_STATUS_SUCCESS) { + pr_err("error retrieving version (%ld, %ld)\n", + RVID_STATUS_REASON(ret), RVID_STATUS_INDEX(ret)); + return -ENXIO; + } + + if (version < RVID_VERSION(0, 3)) { + pr_err("version (%ld, %ld) too old, expected min. (%d, %d)\n", + RVID_VERSION_MAJOR(version), RVID_VERSION_MINOR(version), + 0, 3); + return -ENXIO; + } + + pr_info("distributing interrupts to %pOF\n", parent); + + rvid.domain = irq_domain_create_hierarchy(parent_domain, 0, 0, + rvid.fwnode, + &rvid_irq_domain_ops, &rvid); + if (!rvid.domain) { + pr_warn("Failed to allocate irq domain\n"); + return -ENOMEM; + } + + return 0; +} + +IRQCHIP_DECLARE(rvic, "arm,rvid", rvid_init); diff --git a/include/linux/irqchip/irq-rvic.h b/include/linux/irqchip/irq-rvic.h index 0176ca7d3c30..4545c1e89741 100644 --- a/include/linux/irqchip/irq-rvic.h +++ b/include/linux/irqchip/irq-rvic.h @@ -74,4 +74,23 @@ #define RVIC_STATUS_DISABLED RVIx_STATUS_DISABLED #define RVIC_STATUS_NO_INTERRUPTS RVIx_STATUS_NO_INTERRUPTS +/* rVID functions */ +#define SMC64_RVID_BASE 0xc5000100 +#define SMC64_RVID_FN(n) (SMC64_RVID_BASE + (n)) + +#define SMC64_RVID_VERSION SMC64_RVID_FN(0) +#define SMC64_RVID_MAP SMC64_RVID_FN(1) +#define SMC64_RVID_UNMAP SMC64_RVID_FN(2) + +#define RVID_VERSION(M, m) RVIx_VERSION((M), (m)) + +#define RVID_VERSION_MAJOR(v) RVIx_VERSION_MAJOR((v)) +#define RVID_VERSION_MINOR(v) RVIx_VERSION_MINOR((v)) + +#define RVID_STATUS_REASON(c) RVIx_STATUS_REASON((c)) +#define RVID_STATUS_INDEX(c) RVIx_STATUS_INDEX((c)) + +#define RVID_STATUS_SUCCESS RVIx_STATUS_SUCCESS +#define RVID_STATUS_ERROR_PARAMETER RVIx_STATUS_ERROR_PARAMETER + #endif /* __IRQCHIP_IRQ_RVIC_H__ */ -- 2.33.0
From: Marc Zyngier <maz@kernel.org> Signed-off-by: Marc Zyngier <maz@kernel.org> Signed-off-by: wanghaibin <wanghaibin.wang@huawei.com> --- drivers/irqchip/irq-rvid.c | 181 +++++++++++++++++++++++++++++++++++++ 1 file changed, 181 insertions(+) diff --git a/drivers/irqchip/irq-rvid.c b/drivers/irqchip/irq-rvid.c index 953f654e58d4..df4fab85bb17 100644 --- a/drivers/irqchip/irq-rvid.c +++ b/drivers/irqchip/irq-rvid.c @@ -12,12 +12,19 @@ #include <linux/irq.h> #include <linux/irqchip.h> #include <linux/irqdomain.h> +#include <linux/msi.h> #include <linux/irqchip/irq-rvic.h> struct rvid_data { struct fwnode_handle *fwnode; struct irq_domain *domain; + struct irq_domain *msi_domain; + struct irq_domain *pci_domain; + unsigned long *msi_map; + struct mutex msi_lock; + u32 msi_base; + u32 msi_nr; }; static struct rvid_data rvid; @@ -209,6 +216,176 @@ static const struct irq_domain_ops rvid_irq_domain_ops = { .deactivate = rvid_irq_domain_deactivate, }; +#ifdef CONFIG_PCI_MSI +/* + * The MSI irqchip is completely transparent. The only purpose of the + * corresponding irq domain is to provide the MSI allocator, and feed + * the allocated inputs to the main rVID irq domain for mapping at the + * rVIC level. + */ +static struct irq_chip rvid_msi_chip = { + .name = "rvid-MSI", + .irq_mask = irq_chip_mask_parent, + .irq_unmask = irq_chip_unmask_parent, + .irq_eoi = irq_chip_eoi_parent, + .irq_get_irqchip_state = irq_chip_get_parent_state, + .irq_set_irqchip_state = irq_chip_set_parent_state, + .irq_retrigger = irq_chip_retrigger_hierarchy, + .irq_set_type = irq_chip_set_type_parent, + .irq_set_affinity = irq_chip_set_affinity_parent, +}; + +static int rvid_msi_domain_alloc(struct irq_domain *domain, unsigned int virq, + unsigned int nr_irqs, void *arg) +{ + int ret, hwirq, i; + + mutex_lock(&rvid.msi_lock); + hwirq = bitmap_find_free_region(rvid.msi_map, rvid.msi_nr, + get_count_order(nr_irqs)); + mutex_unlock(&rvid.msi_lock); + + if (hwirq < 0) + return -ENOSPC; + + for (i = 0; i < nr_irqs; i++) { + /* Use the rVID domain to map the input to something */ + struct irq_fwspec fwspec = (struct irq_fwspec) { + .fwnode = domain->parent->fwnode, + .param_count = 1, + .param[0] = rvid.msi_base + hwirq + i, + }; + + ret = irq_domain_alloc_irqs_parent(domain, virq + i, 1, &fwspec); + if (WARN_ON(ret)) + goto out; + + irq_domain_set_hwirq_and_chip(domain, virq + i, hwirq + i, + &rvid_msi_chip, &rvid); + } + + return 0; + +out: + mutex_lock(&rvid.msi_lock); + bitmap_release_region(rvid.msi_map, hwirq, get_count_order(nr_irqs)); + mutex_unlock(&rvid.msi_lock); + + return ret; +} + +static void rvid_msi_domain_free(struct irq_domain *domain, unsigned int virq, + unsigned int nr_irqs) +{ + struct irq_data *d = irq_domain_get_irq_data(domain, virq); + irq_hw_number_t hwirq = d->hwirq; + + /* This is a bit cheeky, but hey, recursion never hurt anyone... */ + rvid_irq_domain_free(domain, virq, nr_irqs); + + mutex_lock(&rvid.msi_lock); + bitmap_release_region(rvid.msi_map, hwirq, get_count_order(nr_irqs)); + mutex_unlock(&rvid.msi_lock); +} + +static struct irq_domain_ops rvid_msi_domain_ops = { + .alloc = rvid_msi_domain_alloc, + .free = rvid_msi_domain_free, +}; + +/* + * The PCI irq chip only provides the minimal stuff, as most of the + * other methods will be provided as defaults. + */ +static void rvid_compose_msi_msg(struct irq_data *data, struct msi_msg *msg) +{ + /* Random address, the rVID doesn't really have a doorbell */ + msg->address_hi = 0; + msg->address_lo = 0xba5e0000; + + /* + * We are called from the PCI domain, and what we program in + * the device is the rVID input pin, which is located two + * levels down in the interrupt chain (PCI -> MSI -> rVID). + */ + msg->data = data->parent_data->parent_data->hwirq; +} + +static void rvid_pci_mask(struct irq_data *d) +{ + pci_msi_mask_irq(d); + irq_chip_mask_parent(d); +} + +static void rvid_pci_unmask(struct irq_data *d) +{ + pci_msi_unmask_irq(d); + irq_chip_unmask_parent(d); +} + +static struct irq_chip rvid_pci_chip = { + .name = "PCI-MSI", + .irq_mask = rvid_pci_mask, + .irq_unmask = rvid_pci_unmask, + .irq_eoi = irq_chip_eoi_parent, + .irq_compose_msi_msg = rvid_compose_msi_msg, +}; + +static struct msi_domain_info rvid_pci_domain_info = { + .flags = (MSI_FLAG_USE_DEF_DOM_OPS | MSI_FLAG_USE_DEF_CHIP_OPS | + MSI_FLAG_PCI_MSIX | MSI_FLAG_MULTI_PCI_MSI), + .chip = &rvid_pci_chip, +}; + +static void __init rvid_msi_setup(struct device_node *np) +{ + if (!of_property_read_bool(np, "msi-controller")) + return; + + if (of_property_read_u32_index(np, "msi-range", 0, &rvid.msi_base) || + of_property_read_u32_index(np, "msi-range", 1, &rvid.msi_nr)) { + pr_err("Invalid or missing msi-range\n"); + return; + } + + mutex_init(&rvid.msi_lock); + + rvid.msi_map = bitmap_alloc(rvid.msi_nr, GFP_KERNEL | __GFP_ZERO); + if (!rvid.msi_map) + return; + + rvid.msi_domain = irq_domain_create_hierarchy(rvid.domain, 0, 0, + rvid.fwnode, + &rvid_msi_domain_ops, + &rvid); + if (!rvid.msi_domain) { + pr_err("Failed to allocate MSI domain\n"); + goto out; + } + + irq_domain_update_bus_token(rvid.msi_domain, DOMAIN_BUS_NEXUS); + + rvid.pci_domain = pci_msi_create_irq_domain(rvid.domain->fwnode, + &rvid_pci_domain_info, + rvid.msi_domain); + if (!rvid.pci_domain) { + pr_err("Failed to allocate PCI domain\n"); + goto out; + } + + pr_info("MSIs available as inputs [%d:%d]\n", + rvid.msi_base, rvid.msi_base + rvid.msi_nr - 1); + return; + +out: + if (rvid.msi_domain) + irq_domain_remove(rvid.msi_domain); + kfree(rvid.msi_map); +} +#else +static inline void rvid_msi_setup(struct device_node *np) {} +#endif + static int __init rvid_init(struct device_node *node, struct device_node *parent) { @@ -253,6 +430,10 @@ static int __init rvid_init(struct device_node *node, return -ENOMEM; } + irq_domain_update_bus_token(rvid.domain, DOMAIN_BUS_WIRED); + + rvid_msi_setup(node); + return 0; } -- 2.33.0
From: wanghaibin <wanghaibin.wang@huawei.com> In order to allow more than just GIC implementations in the future, let's move the GIC model outside of the distributor. This also allows us to back irqchip_in_kernel() with its own irqchip type (IRQCHIP_USER), removing another field from the distributor. New helpers are provided as a convenience. Signed-off-by: Marc Zyngier <maz@kernel.org> Signed-off-by: wanghaibin <wanghaibin.wang@huawei.com> --- arch/arm64/include/asm/kvm_host.h | 3 +++ arch/arm64/include/asm/kvm_irq.h | 20 ++++++++++++++++++++ arch/arm64/kvm/vgic/vgic-debug.c | 5 +++-- arch/arm64/kvm/vgic/vgic-init.c | 27 +++++++++++++-------------- arch/arm64/kvm/vgic/vgic-kvm-device.c | 16 ++++++++++++---- arch/arm64/kvm/vgic/vgic-mmio-v3.c | 2 +- arch/arm64/kvm/vgic/vgic-mmio.c | 14 ++++++-------- arch/arm64/kvm/vgic/vgic-v3.c | 20 ++++++++------------ arch/arm64/kvm/vgic/vgic.h | 2 +- include/kvm/arm_vgic.h | 5 ----- 10 files changed, 67 insertions(+), 47 deletions(-) create mode 100644 arch/arm64/include/asm/kvm_irq.h diff --git a/arch/arm64/include/asm/kvm_host.h b/arch/arm64/include/asm/kvm_host.h index 69a08a4f3d85..badc60577f91 100644 --- a/arch/arm64/include/asm/kvm_host.h +++ b/arch/arm64/include/asm/kvm_host.h @@ -31,6 +31,8 @@ #include <asm/kvm_rme.h> #include <asm/kvm_tmm.h> +#include <asm/kvm_irq.h> + #define __KVM_HAVE_ARCH_INTC_INITIALIZED #define KVM_HALT_POLL_NS_DEFAULT 500000 @@ -216,6 +218,7 @@ struct kvm_arch { u8 pfr1_nmi; /* Interrupt controller */ + enum kvm_irqchip_type irqchip_type; struct vgic_dist vgic; /* Timers */ diff --git a/arch/arm64/include/asm/kvm_irq.h b/arch/arm64/include/asm/kvm_irq.h new file mode 100644 index 000000000000..46bffb6026f8 --- /dev/null +++ b/arch/arm64/include/asm/kvm_irq.h @@ -0,0 +1,20 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright (C) 2020 - Google LLC + * Author: Marc Zyngier <maz@kernel.org> + */ + +#ifndef __ARM64_KVM_IRQ_H__ +#define __ARM64_KVM_IRQ_H__ + +enum kvm_irqchip_type { + IRQCHIP_USER, /* Implemented in userspace */ + IRQCHIP_GICv2, /* v2 on v2, or v2 on v3 */ + IRQCHIP_GICv3, /* v3 on v3 */ +}; + +#define irqchip_in_kernel(k) ((k)->arch.irqchip_type != IRQCHIP_USER) +#define irqchip_is_gic_v2(k) ((k)->arch.irqchip_type == IRQCHIP_GICv2) +#define irqchip_is_gic_v3(k) ((k)->arch.irqchip_type == IRQCHIP_GICv3) + +#endif diff --git a/arch/arm64/kvm/vgic/vgic-debug.c b/arch/arm64/kvm/vgic/vgic-debug.c index 340f960a1154..660499e5c797 100644 --- a/arch/arm64/kvm/vgic/vgic-debug.c +++ b/arch/arm64/kvm/vgic/vgic-debug.c @@ -61,7 +61,7 @@ static void iter_init(struct kvm *kvm, struct vgic_state_iter *iter, iter->nr_cpus = nr_cpus; iter->nr_spis = kvm->arch.vgic.nr_spis; - if (kvm->arch.vgic.vgic_model == KVM_DEV_TYPE_ARM_VGIC_V3) { + if (irqchip_is_gic_v3(kvm)) { iter->nr_lpis = vgic_copy_lpi_list(kvm, NULL, &iter->lpi_array); if (iter->nr_lpis < 0) iter->nr_lpis = 0; @@ -142,7 +142,8 @@ static void vgic_debug_stop(struct seq_file *s, void *v) static void print_dist_state(struct seq_file *s, struct vgic_dist *dist) { - bool v3 = dist->vgic_model == KVM_DEV_TYPE_ARM_VGIC_V3; + struct kvm *kvm = container_of(dist, struct kvm, arch.vgic); + bool v3 = irqchip_is_gic_v3(kvm); seq_printf(s, "Distributor\n"); seq_printf(s, "===========\n"); diff --git a/arch/arm64/kvm/vgic/vgic-init.c b/arch/arm64/kvm/vgic/vgic-init.c index 14c4ce075232..3e4be3c80290 100644 --- a/arch/arm64/kvm/vgic/vgic-init.c +++ b/arch/arm64/kvm/vgic/vgic-init.c @@ -136,8 +136,8 @@ int kvm_vgic_create(struct kvm *kvm, u32 type) goto out_unlock; } - kvm->arch.vgic.in_kernel = true; - kvm->arch.vgic.vgic_model = type; + kvm->arch.irqchip_type = (type == KVM_DEV_TYPE_ARM_VGIC_V2 ? + IRQCHIP_GICv2 : IRQCHIP_GICv3); kvm->arch.vgic.vgic_dist_base = VGIC_ADDR_UNDEF; @@ -186,12 +186,12 @@ static int kvm_vgic_dist_init(struct kvm *kvm, unsigned int nr_spis) irq->vcpu = NULL; irq->target_vcpu = vcpu0; kref_init(&irq->refcount); - switch (dist->vgic_model) { - case KVM_DEV_TYPE_ARM_VGIC_V2: + switch (kvm->arch.irqchip_type) { + case IRQCHIP_GICv2: irq->targets = 0; irq->group = 0; break; - case KVM_DEV_TYPE_ARM_VGIC_V3: + case IRQCHIP_GICv3: irq->mpidr = 0; irq->group = 1; break; @@ -216,7 +216,6 @@ static int kvm_vgic_dist_init(struct kvm *kvm, unsigned int nr_spis) int kvm_vgic_vcpu_init(struct kvm_vcpu *vcpu) { struct vgic_cpu *vgic_cpu = &vcpu->arch.vgic_cpu; - struct vgic_dist *dist = &vcpu->kvm->arch.vgic; int ret = 0; int i; @@ -261,7 +260,7 @@ int kvm_vgic_vcpu_init(struct kvm_vcpu *vcpu) * If we are creating a VCPU with a GICv3 we must also register the * KVM io device for the redistributor that belongs to this VCPU. */ - if (dist->vgic_model == KVM_DEV_TYPE_ARM_VGIC_V3) { + if (irqchip_is_gic_v3(vcpu->kvm)) { mutex_lock(&vcpu->kvm->slots_lock); ret = vgic_register_redist_iodev(vcpu); mutex_unlock(&vcpu->kvm->slots_lock); @@ -316,12 +315,12 @@ int vgic_init(struct kvm *kvm) for (i = 0; i < VGIC_NR_PRIVATE_IRQS; i++) { struct vgic_irq *irq = &vgic_cpu->private_irqs[i]; - switch (dist->vgic_model) { - case KVM_DEV_TYPE_ARM_VGIC_V3: + switch (kvm->arch.irqchip_type) { + case IRQCHIP_GICv3: irq->group = 1; irq->mpidr = kvm_vcpu_get_mpidr_aff(vcpu); break; - case KVM_DEV_TYPE_ARM_VGIC_V2: + case IRQCHIP_GICv2: irq->group = 0; irq->targets = 1U << idx; break; @@ -388,7 +387,7 @@ static void kvm_vgic_dist_destroy(struct kvm *kvm) dist->nr_spis = 0; dist->vgic_dist_base = VGIC_ADDR_UNDEF; - if (dist->vgic_model == KVM_DEV_TYPE_ARM_VGIC_V3) { + if (irqchip_is_gic_v3(kvm)) { list_for_each_entry_safe(rdreg, next, &dist->rd_regions, list) vgic_v3_free_redist_region(kvm, rdreg); INIT_LIST_HEAD(&dist->rd_regions); @@ -414,7 +413,7 @@ static void __kvm_vgic_vcpu_destroy(struct kvm_vcpu *vcpu) vgic_flush_pending_lpis(vcpu); INIT_LIST_HEAD(&vgic_cpu->ap_list_head); - if (vcpu->kvm->arch.vgic.vgic_model == KVM_DEV_TYPE_ARM_VGIC_V3) { + if (irqchip_is_gic_v3(vcpu->kvm)) { vgic_unregister_redist_iodev(vcpu); vgic_cpu->rd_iodev.base_addr = VGIC_ADDR_UNDEF; } @@ -466,7 +465,7 @@ int vgic_lazy_init(struct kvm *kvm) * be explicitly initialized once setup with the respective * KVM device call. */ - if (kvm->arch.vgic.vgic_model != KVM_DEV_TYPE_ARM_VGIC_V2) + if (!irqchip_is_gic_v2(kvm)) return -EBUSY; mutex_lock(&kvm->arch.config_lock); @@ -506,7 +505,7 @@ int kvm_vgic_map_resources(struct kvm *kvm) if (!irqchip_in_kernel(kvm)) goto out; - if (dist->vgic_model == KVM_DEV_TYPE_ARM_VGIC_V2) { + if (irqchip_is_gic_v2(kvm)) { ret = vgic_v2_map_resources(kvm); type = VGIC_V2; } else { diff --git a/arch/arm64/kvm/vgic/vgic-kvm-device.c b/arch/arm64/kvm/vgic/vgic-kvm-device.c index 2f9e8c611f64..d035d9eedb6d 100644 --- a/arch/arm64/kvm/vgic/vgic-kvm-device.c +++ b/arch/arm64/kvm/vgic/vgic-kvm-device.c @@ -35,10 +35,18 @@ int vgic_check_iorange(struct kvm *kvm, phys_addr_t ioaddr, static int vgic_check_type(struct kvm *kvm, int type_needed) { - if (kvm->arch.vgic.vgic_model != type_needed) - return -ENODEV; - else - return 0; + switch (type_needed) { + case KVM_DEV_TYPE_ARM_VGIC_V2: + if (irqchip_is_gic_v2(kvm)) + return 0; + break; + case KVM_DEV_TYPE_ARM_VGIC_V3: + if (irqchip_is_gic_v3(kvm)) + return 0; + break; + } + + return -ENODEV; } int kvm_set_legacy_vgic_v2_addr(struct kvm *kvm, struct kvm_arm_device_addr *dev_addr) diff --git a/arch/arm64/kvm/vgic/vgic-mmio-v3.c b/arch/arm64/kvm/vgic/vgic-mmio-v3.c index 623160d51e54..ea14c0c9d33d 100644 --- a/arch/arm64/kvm/vgic/vgic-mmio-v3.c +++ b/arch/arm64/kvm/vgic/vgic-mmio-v3.c @@ -42,7 +42,7 @@ bool vgic_has_its(struct kvm *kvm) { struct vgic_dist *dist = &kvm->arch.vgic; - if (dist->vgic_model != KVM_DEV_TYPE_ARM_VGIC_V3) + if (!irqchip_is_gic_v3(kvm)) return false; return dist->has_its; diff --git a/arch/arm64/kvm/vgic/vgic-mmio.c b/arch/arm64/kvm/vgic/vgic-mmio.c index 1b4a959601af..44a1a77ddd58 100644 --- a/arch/arm64/kvm/vgic/vgic-mmio.c +++ b/arch/arm64/kvm/vgic/vgic-mmio.c @@ -279,8 +279,8 @@ static unsigned long __read_pending(struct kvm_vcpu *vcpu, } else if (!is_user && vgic_irq_is_mapped_level(irq)) { val = vgic_get_phys_line_level(irq); } else { - switch (vcpu->kvm->arch.vgic.vgic_model) { - case KVM_DEV_TYPE_ARM_VGIC_V3: + switch (vcpu->kvm->arch.irqchip_type) { + case IRQCHIP_GICv3: if (is_user) { val = irq->pending_latch; break; @@ -315,8 +315,7 @@ unsigned long vgic_uaccess_read_pending(struct kvm_vcpu *vcpu, static bool is_vgic_v2_sgi(struct kvm_vcpu *vcpu, struct vgic_irq *irq) { - return (vgic_irq_is_sgi(irq->intid) && - vcpu->kvm->arch.vgic.vgic_model == KVM_DEV_TYPE_ARM_VGIC_V2); + return (vgic_irq_is_sgi(irq->intid) && irqchip_is_gic_v2(vcpu->kvm)); } void vgic_mmio_write_spending(struct kvm_vcpu *vcpu, @@ -503,7 +502,7 @@ int vgic_uaccess_write_cpending(struct kvm_vcpu *vcpu, */ static void vgic_access_active_prepare(struct kvm_vcpu *vcpu, u32 intid) { - if ((vcpu->kvm->arch.vgic.vgic_model == KVM_DEV_TYPE_ARM_VGIC_V3 && + if ((irqchip_is_gic_v3(vcpu->kvm) && vcpu != kvm_get_running_vcpu()) || intid >= VGIC_NR_PRIVATE_IRQS) kvm_arm_halt_guest(vcpu->kvm); @@ -512,7 +511,7 @@ static void vgic_access_active_prepare(struct kvm_vcpu *vcpu, u32 intid) /* See vgic_access_active_prepare */ static void vgic_access_active_finish(struct kvm_vcpu *vcpu, u32 intid) { - if ((vcpu->kvm->arch.vgic.vgic_model == KVM_DEV_TYPE_ARM_VGIC_V3 && + if ((irqchip_is_gic_v3(vcpu->kvm) && vcpu != kvm_get_running_vcpu()) || intid >= VGIC_NR_PRIVATE_IRQS) kvm_arm_resume_guest(vcpu->kvm); @@ -610,7 +609,6 @@ static void vgic_mmio_change_active(struct kvm_vcpu *vcpu, struct vgic_irq *irq, irq->vtimer_info->set_active_stat(vcpu, irq->intid, active); #endif } else { - u32 model = vcpu->kvm->arch.vgic.vgic_model; u8 active_source; irq->active = active; @@ -628,7 +626,7 @@ static void vgic_mmio_change_active(struct kvm_vcpu *vcpu, struct vgic_irq *irq, */ active_source = (requester_vcpu) ? requester_vcpu->vcpu_id : 0; - if (model == KVM_DEV_TYPE_ARM_VGIC_V2 && + if (irqchip_is_gic_v2(vcpu->kvm) && active && vgic_irq_is_sgi(irq->intid)) irq->active_source = active_source; } diff --git a/arch/arm64/kvm/vgic/vgic-v3.c b/arch/arm64/kvm/vgic/vgic-v3.c index 378cb6d370b1..f0fe7737a8ed 100644 --- a/arch/arm64/kvm/vgic/vgic-v3.c +++ b/arch/arm64/kvm/vgic/vgic-v3.c @@ -39,7 +39,7 @@ void vgic_v3_fold_lr_state(struct kvm_vcpu *vcpu) { struct vgic_cpu *vgic_cpu = &vcpu->arch.vgic_cpu; struct vgic_v3_cpu_if *cpuif = &vgic_cpu->vgic_v3; - u32 model = vcpu->kvm->arch.vgic.vgic_model; + bool is_v3 = irqchip_is_gic_v3(vcpu->kvm); int lr; DEBUG_SPINLOCK_BUG_ON(!irqs_disabled()); @@ -56,7 +56,7 @@ void vgic_v3_fold_lr_state(struct kvm_vcpu *vcpu) cpuid = val & GICH_LR_PHYSID_CPUID; cpuid >>= GICH_LR_PHYSID_CPUID_SHIFT; - if (model == KVM_DEV_TYPE_ARM_VGIC_V3) { + if (is_v3) { intid = val & ICH_LR_VIRTUAL_ID_MASK; } else { intid = val & GICH_LR_VIRTUALID; @@ -109,12 +109,11 @@ void vgic_v3_fold_lr_state(struct kvm_vcpu *vcpu) /* Requires the irq to be locked already */ void vgic_v3_populate_lr(struct kvm_vcpu *vcpu, struct vgic_irq *irq, int lr) { - u32 model = vcpu->kvm->arch.vgic.vgic_model; + bool is_v2 = irqchip_is_gic_v2(vcpu->kvm); u64 val = irq->intid; bool allow_pending = true, is_v2_sgi; - is_v2_sgi = (vgic_irq_is_sgi(irq->intid) && - model == KVM_DEV_TYPE_ARM_VGIC_V2); + is_v2_sgi = (vgic_irq_is_sgi(irq->intid) && is_v2); if (irq->active) { val |= ICH_LR_ACTIVE_BIT; @@ -155,8 +154,7 @@ void vgic_v3_populate_lr(struct kvm_vcpu *vcpu, struct vgic_irq *irq, int lr) if (irq->config == VGIC_CONFIG_EDGE) irq->pending_latch = false; - if (vgic_irq_is_sgi(irq->intid) && - model == KVM_DEV_TYPE_ARM_VGIC_V2) { + if (vgic_irq_is_sgi(irq->intid) && is_v2) { u32 src = ffs(irq->source); if (WARN_RATELIMIT(!src, "No SGI source for INTID %d\n", @@ -201,10 +199,9 @@ void vgic_v3_clear_lr(struct kvm_vcpu *vcpu, int lr) void vgic_v3_set_vmcr(struct kvm_vcpu *vcpu, struct vgic_vmcr *vmcrp) { struct vgic_v3_cpu_if *cpu_if = &vcpu->arch.vgic_cpu.vgic_v3; - u32 model = vcpu->kvm->arch.vgic.vgic_model; u32 vmcr; - if (model == KVM_DEV_TYPE_ARM_VGIC_V2) { + if (irqchip_is_gic_v2(vcpu->kvm)) { vmcr = (vmcrp->ackctl << ICH_VMCR_ACK_CTL_SHIFT) & ICH_VMCR_ACK_CTL_MASK; vmcr |= (vmcrp->fiqen << ICH_VMCR_FIQ_EN_SHIFT) & @@ -231,12 +228,11 @@ void vgic_v3_set_vmcr(struct kvm_vcpu *vcpu, struct vgic_vmcr *vmcrp) void vgic_v3_get_vmcr(struct kvm_vcpu *vcpu, struct vgic_vmcr *vmcrp) { struct vgic_v3_cpu_if *cpu_if = &vcpu->arch.vgic_cpu.vgic_v3; - u32 model = vcpu->kvm->arch.vgic.vgic_model; u32 vmcr; vmcr = cpu_if->vgic_vmcr; - if (model == KVM_DEV_TYPE_ARM_VGIC_V2) { + if (irqchip_is_gic_v2(vcpu->kvm)) { vmcrp->ackctl = (vmcr & ICH_VMCR_ACK_CTL_MASK) >> ICH_VMCR_ACK_CTL_SHIFT; vmcrp->fiqen = (vmcr & ICH_VMCR_FIQ_EN_MASK) >> @@ -281,7 +277,7 @@ void vgic_v3_enable(struct kvm_vcpu *vcpu) * Also, we don't support any form of IRQ/FIQ bypass. * This goes with the spec allowing the value to be RAO/WI. */ - if (vcpu->kvm->arch.vgic.vgic_model == KVM_DEV_TYPE_ARM_VGIC_V3) { + if (irqchip_is_gic_v3(vcpu->kvm)) { vgic_v3->vgic_sre = (ICC_SRE_EL1_DIB | ICC_SRE_EL1_DFB | ICC_SRE_EL1_SRE); diff --git a/arch/arm64/kvm/vgic/vgic.h b/arch/arm64/kvm/vgic/vgic.h index f78c026c20f0..b7405cf95624 100644 --- a/arch/arm64/kvm/vgic/vgic.h +++ b/arch/arm64/kvm/vgic/vgic.h @@ -363,7 +363,7 @@ static inline bool kvm_has_gicv3(struct kvm *kvm) { return (static_branch_unlikely(&kvm_vgic_global_state.gicv3_cpuif) && irqchip_in_kernel(kvm) && - kvm->arch.vgic.vgic_model == KVM_DEV_TYPE_ARM_VGIC_V3); + irqchip_is_gic_v3(kvm)); } #endif diff --git a/include/kvm/arm_vgic.h b/include/kvm/arm_vgic.h index 0b734d6f3d21..640507053c69 100644 --- a/include/kvm/arm_vgic.h +++ b/include/kvm/arm_vgic.h @@ -281,13 +281,9 @@ struct vgic_redist_region { }; struct vgic_dist { - bool in_kernel; bool ready; bool initialized; - /* vGIC model the kernel emulates for the guest (GICv2 or GICv3) */ - u32 vgic_model; - /* Implementation revision as reported in the GICD_IIDR */ u32 implementation_rev; #define KVM_VGIC_IMP_REV_2 2 /* GICv2 restorable groups */ @@ -469,7 +465,6 @@ void kvm_vgic_load(struct kvm_vcpu *vcpu); void kvm_vgic_put(struct kvm_vcpu *vcpu); void kvm_vgic_vmcr_sync(struct kvm_vcpu *vcpu); -#define irqchip_in_kernel(k) (!!((k)->arch.vgic.in_kernel)) #define vgic_initialized(k) ((k)->arch.vgic.initialized) #define vgic_ready(k) ((k)->arch.vgic.ready) #define vgic_valid_spi(k, i) (((i) >= VGIC_NR_PRIVATE_IRQS) && \ -- 2.33.0
From: wanghaibin <wanghaibin.wang@huawei.com> The current early init for the GIC is pretty silly. The data it initializes only matters for GICv3, which is guaranteed to be created via a kvm_create_device call. Given that, it is pointless to initialize the data early, before userspace can get a file descriptor and mess with it. Move everything to kvm_vgic_create(). Signed-off-by: Marc Zyngier <maz@kernel.org> Signed-off-by: wanghaibin <wanghaibin.wang@huawei.com> --- arch/arm64/kvm/arm.c | 2 -- arch/arm64/kvm/vgic/vgic-init.c | 4 +++- include/kvm/arm_vgic.h | 1 - 3 files changed, 3 insertions(+), 4 deletions(-) diff --git a/arch/arm64/kvm/arm.c b/arch/arm64/kvm/arm.c index e59152ad5c4a..39c779084430 100644 --- a/arch/arm64/kvm/arm.c +++ b/arch/arm64/kvm/arm.c @@ -357,8 +357,6 @@ int kvm_arch_init_vm(struct kvm *kvm, unsigned long type) if (ret) goto err_free_cpumask; - kvm_vgic_early_init(kvm); - kvm_timer_init_vm(kvm); /* The maximum number of VCPUs is limited by the host's GIC model */ diff --git a/arch/arm64/kvm/vgic/vgic-init.c b/arch/arm64/kvm/vgic/vgic-init.c index 3e4be3c80290..52eac2530d54 100644 --- a/arch/arm64/kvm/vgic/vgic-init.c +++ b/arch/arm64/kvm/vgic/vgic-init.c @@ -55,7 +55,7 @@ * allocation or sizing information from userspace. vgic_init() called * kvm_vgic_dist_init() which takes care of the rest. */ -void kvm_vgic_early_init(struct kvm *kvm) +static void kvm_vgic_early_init(struct kvm *kvm) { struct vgic_dist *dist = &kvm->arch.vgic; #ifdef CONFIG_KVM_ARM_MULTI_LPI_TRANSLATE_CACHE @@ -146,6 +146,8 @@ int kvm_vgic_create(struct kvm *kvm, u32 type) else INIT_LIST_HEAD(&kvm->arch.vgic.rd_regions); + kvm_vgic_early_init(kvm); + out_unlock: mutex_unlock(&kvm->arch.config_lock); unlock_all_vcpus(kvm); diff --git a/include/kvm/arm_vgic.h b/include/kvm/arm_vgic.h index 640507053c69..ad78351fa905 100644 --- a/include/kvm/arm_vgic.h +++ b/include/kvm/arm_vgic.h @@ -442,7 +442,6 @@ extern struct static_key_false vgic_v2_cpuif_trap; extern struct static_key_false vgic_v3_cpuif_trap; int kvm_set_legacy_vgic_v2_addr(struct kvm *kvm, struct kvm_arm_device_addr *dev_addr); -void kvm_vgic_early_init(struct kvm *kvm); int kvm_vgic_vcpu_init(struct kvm_vcpu *vcpu); int kvm_vgic_create(struct kvm *kvm, u32 type); void kvm_vgic_destroy(struct kvm *kvm); -- 2.33.0
From: wanghaibin <wanghaibin.wang@huawei.com> As we are about to abstract part of the vgic implementation in order to make it more modular, let's start by adding a data structure that will eventually contain interrupt controller specific callbacks, as well as helpers to call them (or gracefully skip them if they aren't implemented. It is empty so far, so no functional changes are anticipated. Signed-off-by: Marc Zyngier <maz@kernel.org> Signed-off-by: wanghaibin <wanghaibin.wang@huawei.com> --- arch/arm64/include/asm/kvm_host.h | 1 + arch/arm64/include/asm/kvm_irq.h | 29 +++++++++++++++++++++++++++++ arch/arm64/kvm/vgic/vgic-init.c | 5 +++++ 3 files changed, 35 insertions(+) diff --git a/arch/arm64/include/asm/kvm_host.h b/arch/arm64/include/asm/kvm_host.h index badc60577f91..3526c2256e94 100644 --- a/arch/arm64/include/asm/kvm_host.h +++ b/arch/arm64/include/asm/kvm_host.h @@ -219,6 +219,7 @@ struct kvm_arch { /* Interrupt controller */ enum kvm_irqchip_type irqchip_type; + struct kvm_irqchip_flow irqchip_flow; struct vgic_dist vgic; /* Timers */ diff --git a/arch/arm64/include/asm/kvm_irq.h b/arch/arm64/include/asm/kvm_irq.h index 46bffb6026f8..7a70bb803560 100644 --- a/arch/arm64/include/asm/kvm_irq.h +++ b/arch/arm64/include/asm/kvm_irq.h @@ -17,4 +17,33 @@ enum kvm_irqchip_type { #define irqchip_is_gic_v2(k) ((k)->arch.irqchip_type == IRQCHIP_GICv2) #define irqchip_is_gic_v3(k) ((k)->arch.irqchip_type == IRQCHIP_GICv3) +struct kvm_irqchip_flow { +}; + +/* + * Macro galore. At the point this is included, the various types are + * not defined yet. Yes, this is terminally ugly. + */ +#define __kvm_irqchip_action(k, x, ...) \ + do { \ + if (likely((k)->arch.irqchip_flow.irqchip_##x)) \ + (k)->arch.irqchip_flow.irqchip_##x(__VA_ARGS__); \ + } while (0) + +#define __kvm_irqchip_action_ret(k, x, ...) \ + ({ \ + typeof ((k)->arch.irqchip_flow.irqchip_##x(__VA_ARGS__)) ret; \ + ret = (likely((k)->arch.irqchip_flow.irqchip_##x) ? \ + (k)->arch.irqchip_flow.irqchip_##x(__VA_ARGS__) : \ + 0); \ + \ + ret; \ + }) + +#define __vcpu_irqchip_action(v, ...) \ + __kvm_irqchip_action((v)->kvm, __VA_ARGS__) + +#define __vcpu_irqchip_action_ret(v, ...) \ + __kvm_irqchip_action_ret((v)->kvm, __VA_ARGS__) + #endif diff --git a/arch/arm64/kvm/vgic/vgic-init.c b/arch/arm64/kvm/vgic/vgic-init.c index 52eac2530d54..be105bb97cb8 100644 --- a/arch/arm64/kvm/vgic/vgic-init.c +++ b/arch/arm64/kvm/vgic/vgic-init.c @@ -18,6 +18,9 @@ #include "hisilicon/hisi_virt.h" #endif +static struct kvm_irqchip_flow vgic_irqchip_flow = { +}; + /* * Initialization rules: there are multiple stages to the vgic * initialization, both for the distributor and the CPU interfaces. The basic @@ -63,6 +66,8 @@ static void kvm_vgic_early_init(struct kvm *kvm) int i; #endif + kvm->arch.irqchip_flow = vgic_irqchip_flow; + INIT_LIST_HEAD(&dist->lpi_list_head); #ifdef CONFIG_KVM_ARM_MULTI_LPI_TRANSLATE_CACHE for (i = 0; i < LPI_TRANS_CACHES_NUM; i++) { -- 2.33.0
From: wanghaibin <wanghaibin.wang@huawei.com> Let's start the VGIC split by moving the act of destroying it, as it is simple and doesn't require much effort. Whilst we're at it, make kvm_vgic_vcpu_destroy() static, as it isn't called from anywhere else. fix: vcpu_destroy called form arm.c now, so abstract it; Signed-off-by: Marc Zyngier <maz@kernel.org> Signed-off-by: wanghaibin <wanghaibin.wang@huawei.com> --- arch/arm64/include/asm/kvm_irq.h | 9 +++++++++ arch/arm64/kvm/arm.c | 6 +++--- arch/arm64/kvm/vgic/vgic-init.c | 9 +++++++-- include/kvm/arm_vgic.h | 2 -- 4 files changed, 19 insertions(+), 7 deletions(-) diff --git a/arch/arm64/include/asm/kvm_irq.h b/arch/arm64/include/asm/kvm_irq.h index 7a70bb803560..42d110155627 100644 --- a/arch/arm64/include/asm/kvm_irq.h +++ b/arch/arm64/include/asm/kvm_irq.h @@ -18,6 +18,8 @@ enum kvm_irqchip_type { #define irqchip_is_gic_v3(k) ((k)->arch.irqchip_type == IRQCHIP_GICv3) struct kvm_irqchip_flow { + void (*irqchip_destroy)(struct kvm *); + void (*irqchip_vcpu_destroy)(struct kvm_vcpu *vcpu); }; /* @@ -46,4 +48,11 @@ struct kvm_irqchip_flow { #define __vcpu_irqchip_action_ret(v, ...) \ __kvm_irqchip_action_ret((v)->kvm, __VA_ARGS__) + +#define kvm_irqchip_destroy(k) \ + __kvm_irqchip_action((k), destroy, (k)) + +#define kvm_irqchip_vcpu_destroy(v) \ + __vcpu_irqchip_action((v), vcpu_destroy, (v)) + #endif diff --git a/arch/arm64/kvm/arm.c b/arch/arm64/kvm/arm.c index 39c779084430..9a21a064afb3 100644 --- a/arch/arm64/kvm/arm.c +++ b/arch/arm64/kvm/arm.c @@ -402,7 +402,7 @@ void kvm_arch_destroy_vm(struct kvm *kvm) bitmap_free(kvm->arch.pmu_filter); free_cpumask_var(kvm->arch.supported_cpus); - kvm_vgic_destroy(kvm); + kvm_irqchip_destroy(kvm); if (is_protected_kvm_enabled()) pkvm_destroy_hyp_vm(kvm); @@ -661,7 +661,7 @@ int kvm_arch_vcpu_create(struct kvm_vcpu *vcpu) kvm_sched_affinity_vcpu_destroy(vcpu); vgic_vcpu_destroy: - kvm_vgic_vcpu_destroy(vcpu); + kvm_irqchip_vcpu_destroy(vcpu); return err; } @@ -675,7 +675,7 @@ void kvm_arch_vcpu_destroy(struct kvm_vcpu *vcpu) kvm_mmu_free_memory_cache(&vcpu->arch.mmu_page_cache); kvm_timer_vcpu_terminate(vcpu); kvm_pmu_vcpu_destroy(vcpu); - kvm_vgic_vcpu_destroy(vcpu); + kvm_irqchip_vcpu_destroy(vcpu); kvm_arm_vcpu_destroy(vcpu); kvm_sched_affinity_vcpu_destroy(vcpu); diff --git a/arch/arm64/kvm/vgic/vgic-init.c b/arch/arm64/kvm/vgic/vgic-init.c index be105bb97cb8..0c8972404b79 100644 --- a/arch/arm64/kvm/vgic/vgic-init.c +++ b/arch/arm64/kvm/vgic/vgic-init.c @@ -18,7 +18,12 @@ #include "hisilicon/hisi_virt.h" #endif +static void kvm_vgic_destroy(struct kvm *kvm); +static void kvm_vgic_vcpu_destroy(struct kvm_vcpu *vcpu); + static struct kvm_irqchip_flow vgic_irqchip_flow = { + .irqchip_destroy = kvm_vgic_destroy, + .irqchip_vcpu_destroy = kvm_vgic_vcpu_destroy, }; /* @@ -426,7 +431,7 @@ static void __kvm_vgic_vcpu_destroy(struct kvm_vcpu *vcpu) } } -void kvm_vgic_vcpu_destroy(struct kvm_vcpu *vcpu) +static void kvm_vgic_vcpu_destroy(struct kvm_vcpu *vcpu) { struct kvm *kvm = vcpu->kvm; @@ -435,7 +440,7 @@ void kvm_vgic_vcpu_destroy(struct kvm_vcpu *vcpu) mutex_unlock(&kvm->slots_lock); } -void kvm_vgic_destroy(struct kvm *kvm) +static void kvm_vgic_destroy(struct kvm *kvm) { struct kvm_vcpu *vcpu; unsigned long i; diff --git a/include/kvm/arm_vgic.h b/include/kvm/arm_vgic.h index ad78351fa905..6b650434e499 100644 --- a/include/kvm/arm_vgic.h +++ b/include/kvm/arm_vgic.h @@ -444,8 +444,6 @@ extern struct static_key_false vgic_v3_cpuif_trap; int kvm_set_legacy_vgic_v2_addr(struct kvm *kvm, struct kvm_arm_device_addr *dev_addr); int kvm_vgic_vcpu_init(struct kvm_vcpu *vcpu); int kvm_vgic_create(struct kvm *kvm, u32 type); -void kvm_vgic_destroy(struct kvm *kvm); -void kvm_vgic_vcpu_destroy(struct kvm_vcpu *vcpu); int kvm_vgic_map_resources(struct kvm *kvm); int kvm_vgic_hyp_init(void); void kvm_vgic_init_cpu_hardware(void); -- 2.33.0
From: wanghaibin <wanghaibin.wang@huawei.com> Abstract kvm_vgic_vcpu_init() by moving it to the irqchip_flow structure. This results in a minor change of the way we initialize vcpus: VCPUs created prior to the creation of the vgic device don't have their local view of the vgic initialized. This means that on vgic instantiation, we must "catch up" and initialise the CPU interfaces for these vcpus. VCPUs created after the vgic device will follow the unusual flow. Special care must be taken to accomodate the different locking contexts though. The function can then be made static and the irqchip_in_kernel() test dropped, as we only get here if a vgic has been created. Signed-off-by: Marc Zyngier <maz@kernel.org> Signed-off-by: wanghaibin <wanghaibin.wang@huawei.com> --- arch/arm64/include/asm/kvm_irq.h | 4 ++++ arch/arm64/kvm/arm.c | 2 +- arch/arm64/kvm/vgic/vgic-init.c | 39 ++++++++++++++++++++++++++------ include/kvm/arm_vgic.h | 1 - 4 files changed, 37 insertions(+), 9 deletions(-) diff --git a/arch/arm64/include/asm/kvm_irq.h b/arch/arm64/include/asm/kvm_irq.h index 42d110155627..8fa29ede1835 100644 --- a/arch/arm64/include/asm/kvm_irq.h +++ b/arch/arm64/include/asm/kvm_irq.h @@ -20,6 +20,7 @@ enum kvm_irqchip_type { struct kvm_irqchip_flow { void (*irqchip_destroy)(struct kvm *); void (*irqchip_vcpu_destroy)(struct kvm_vcpu *vcpu); + int (*irqchip_vcpu_init)(struct kvm_vcpu *); }; /* @@ -55,4 +56,7 @@ struct kvm_irqchip_flow { #define kvm_irqchip_vcpu_destroy(v) \ __vcpu_irqchip_action((v), vcpu_destroy, (v)) +#define kvm_irqchip_vcpu_init(v) \ + __vcpu_irqchip_action_ret((v), vcpu_init, (v)) + #endif diff --git a/arch/arm64/kvm/arm.c b/arch/arm64/kvm/arm.c index 9a21a064afb3..3fc15c48ec0c 100644 --- a/arch/arm64/kvm/arm.c +++ b/arch/arm64/kvm/arm.c @@ -636,7 +636,7 @@ int kvm_arch_vcpu_create(struct kvm_vcpu *vcpu) vcpu->arch.hw_mmu = &vcpu->kvm->arch.mmu; - err = kvm_vgic_vcpu_init(vcpu); + err = kvm_irqchip_vcpu_init(vcpu); if (err) return err; diff --git a/arch/arm64/kvm/vgic/vgic-init.c b/arch/arm64/kvm/vgic/vgic-init.c index 0c8972404b79..45a5dc95a910 100644 --- a/arch/arm64/kvm/vgic/vgic-init.c +++ b/arch/arm64/kvm/vgic/vgic-init.c @@ -18,12 +18,14 @@ #include "hisilicon/hisi_virt.h" #endif +static int kvm_vgic_vcpu_init(struct kvm_vcpu *vcpu); static void kvm_vgic_destroy(struct kvm *kvm); static void kvm_vgic_vcpu_destroy(struct kvm_vcpu *vcpu); static struct kvm_irqchip_flow vgic_irqchip_flow = { .irqchip_destroy = kvm_vgic_destroy, .irqchip_vcpu_destroy = kvm_vgic_vcpu_destroy, + .irqchip_vcpu_init = kvm_vgic_vcpu_init, }; /* @@ -90,6 +92,8 @@ static void kvm_vgic_early_init(struct kvm *kvm) #endif } +static int __kvm_vgic_vcpu_init(struct kvm_vcpu *vcpu); + /* CREATION */ /** @@ -158,8 +162,22 @@ int kvm_vgic_create(struct kvm *kvm, u32 type) kvm_vgic_early_init(kvm); + out_unlock: mutex_unlock(&kvm->arch.config_lock); + + /* + * vcpus may have been created before the GIC. Initialize + * them. Careful that kvm->lock is held already on the + * KVM_CREATE_DEVICE path, so use the non-locking version. + * fix: need slot lock, use the lock vcpu init + */ + kvm_for_each_vcpu(i, vcpu, kvm) { + ret = kvm_vgic_vcpu_init(vcpu); + if (ret) + break; + } + unlock_all_vcpus(kvm); return ret; } @@ -225,7 +243,7 @@ static int kvm_vgic_dist_init(struct kvm *kvm, unsigned int nr_spis) * Only do initialization, but do not actually enable the * VGIC CPU interface */ -int kvm_vgic_vcpu_init(struct kvm_vcpu *vcpu) +static int __kvm_vgic_vcpu_init(struct kvm_vcpu *vcpu) { struct vgic_cpu *vgic_cpu = &vcpu->arch.vgic_cpu; int ret = 0; @@ -265,18 +283,25 @@ int kvm_vgic_vcpu_init(struct kvm_vcpu *vcpu) #endif } - if (!irqchip_in_kernel(vcpu->kvm)) - return 0; /* * If we are creating a VCPU with a GICv3 we must also register the * KVM io device for the redistributor that belongs to this VCPU. */ - if (irqchip_is_gic_v3(vcpu->kvm)) { - mutex_lock(&vcpu->kvm->slots_lock); + if (irqchip_is_gic_v3(vcpu->kvm)) ret = vgic_register_redist_iodev(vcpu); - mutex_unlock(&vcpu->kvm->slots_lock); - } + + return ret; +} + +static int kvm_vgic_vcpu_init(struct kvm_vcpu *vcpu) +{ + int ret; + + mutex_lock(&vcpu->kvm->slots_lock); + ret = __kvm_vgic_vcpu_init(vcpu); + mutex_unlock(&vcpu->kvm->slots_lock); + return ret; } diff --git a/include/kvm/arm_vgic.h b/include/kvm/arm_vgic.h index 6b650434e499..b6132c062725 100644 --- a/include/kvm/arm_vgic.h +++ b/include/kvm/arm_vgic.h @@ -442,7 +442,6 @@ extern struct static_key_false vgic_v2_cpuif_trap; extern struct static_key_false vgic_v3_cpuif_trap; int kvm_set_legacy_vgic_v2_addr(struct kvm *kvm, struct kvm_arm_device_addr *dev_addr); -int kvm_vgic_vcpu_init(struct kvm_vcpu *vcpu); int kvm_vgic_create(struct kvm *kvm, u32 type); int kvm_vgic_map_resources(struct kvm *kvm); int kvm_vgic_hyp_init(void); -- 2.33.0
From: wanghaibin <wanghaibin.wang@huawei.com> Move the code dedicated to blocking/unblocking on WFI to the vgic code itself, and abstract it via the irqchip_flow structure. No functional change. Signed-off-by: Marc Zyngier <maz@kernel.org> Signed-off-by: wanghaibin <wanghaibin.wang@huawei.com> --- arch/arm64/include/asm/kvm_irq.h | 8 ++++++++ arch/arm64/kvm/arm.c | 7 ++++--- arch/arm64/kvm/vgic/vgic-init.c | 2 ++ arch/arm64/kvm/vgic/vgic.c | 25 ++++++++++++++++++++++--- arch/arm64/kvm/vgic/vgic.h | 3 +++ include/kvm/arm_vgic.h | 1 - 6 files changed, 39 insertions(+), 7 deletions(-) diff --git a/arch/arm64/include/asm/kvm_irq.h b/arch/arm64/include/asm/kvm_irq.h index 8fa29ede1835..efb12693db13 100644 --- a/arch/arm64/include/asm/kvm_irq.h +++ b/arch/arm64/include/asm/kvm_irq.h @@ -21,6 +21,8 @@ struct kvm_irqchip_flow { void (*irqchip_destroy)(struct kvm *); void (*irqchip_vcpu_destroy)(struct kvm_vcpu *vcpu); int (*irqchip_vcpu_init)(struct kvm_vcpu *); + void (*irqchip_vcpu_blocking)(struct kvm_vcpu *); + void (*irqchip_vcpu_unblocking)(struct kvm_vcpu *); }; /* @@ -59,4 +61,10 @@ struct kvm_irqchip_flow { #define kvm_irqchip_vcpu_init(v) \ __vcpu_irqchip_action_ret((v), vcpu_init, (v)) +#define kvm_irqchip_vcpu_blocking(v) \ + __vcpu_irqchip_action((v), vcpu_blocking, (v)) + +#define kvm_irqchip_vcpu_unblocking(v) \ + __vcpu_irqchip_action((v), vcpu_unblocking, (v)) + #endif diff --git a/arch/arm64/kvm/arm.c b/arch/arm64/kvm/arm.c index 3fc15c48ec0c..11a1bbc4a3b7 100644 --- a/arch/arm64/kvm/arm.c +++ b/arch/arm64/kvm/arm.c @@ -1040,18 +1040,19 @@ void kvm_vcpu_wfi(struct kvm_vcpu *vcpu) * doorbells to be signalled, should an interrupt become pending. */ preempt_disable(); - kvm_vgic_vmcr_sync(vcpu); vcpu_set_flag(vcpu, IN_WFI); - vgic_v4_put(vcpu); preempt_enable(); + kvm_irqchip_vcpu_blocking(vcpu); + kvm_vcpu_halt(vcpu); vcpu_clear_flag(vcpu, IN_WFIT); preempt_disable(); vcpu_clear_flag(vcpu, IN_WFI); - vgic_v4_load(vcpu); preempt_enable(); + + kvm_irqchip_vcpu_unblocking(vcpu); } static int kvm_vcpu_suspend(struct kvm_vcpu *vcpu) diff --git a/arch/arm64/kvm/vgic/vgic-init.c b/arch/arm64/kvm/vgic/vgic-init.c index 45a5dc95a910..7367bb5bff1e 100644 --- a/arch/arm64/kvm/vgic/vgic-init.c +++ b/arch/arm64/kvm/vgic/vgic-init.c @@ -26,6 +26,8 @@ static struct kvm_irqchip_flow vgic_irqchip_flow = { .irqchip_destroy = kvm_vgic_destroy, .irqchip_vcpu_destroy = kvm_vgic_vcpu_destroy, .irqchip_vcpu_init = kvm_vgic_vcpu_init, + .irqchip_vcpu_blocking = kvm_vgic_vcpu_blocking, + .irqchip_vcpu_unblocking = kvm_vgic_vcpu_unblocking, }; /* diff --git a/arch/arm64/kvm/vgic/vgic.c b/arch/arm64/kvm/vgic/vgic.c index 13af2566f56c..63f97cc2ca32 100644 --- a/arch/arm64/kvm/vgic/vgic.c +++ b/arch/arm64/kvm/vgic/vgic.c @@ -1082,15 +1082,34 @@ void kvm_vgic_put(struct kvm_vcpu *vcpu) vgic_v3_put(vcpu); } -void kvm_vgic_vmcr_sync(struct kvm_vcpu *vcpu) +void kvm_vgic_vcpu_blocking(struct kvm_vcpu *vcpu) { - if (unlikely(!irqchip_in_kernel(vcpu->kvm))) - return; + /* + * If we're about to block (most likely because we've just hit a + * WFI), we need to sync back the state of the GIC CPU interface + * so that we have the latest PMR and group enables. This ensures + * that kvm_arch_vcpu_runnable has up-to-date data to decide + * whether we have pending interrupts. + * + * For the same reason, we want to tell GICv4 that we need + * doorbells to be signalled, should an interrupt become pending. + */ + preempt_disable(); if (kvm_vgic_global_state.type == VGIC_V2) vgic_v2_vmcr_sync(vcpu); else vgic_v3_vmcr_sync(vcpu); + + vgic_v4_put(vcpu); + preempt_enable(); +} + +void kvm_vgic_vcpu_unblocking(struct kvm_vcpu *vcpu) +{ + preempt_disable(); + vgic_v4_load(vcpu); + preempt_enable(); } int kvm_vgic_vcpu_pending_irq(struct kvm_vcpu *vcpu) diff --git a/arch/arm64/kvm/vgic/vgic.h b/arch/arm64/kvm/vgic/vgic.h index b7405cf95624..3cf986eeec93 100644 --- a/arch/arm64/kvm/vgic/vgic.h +++ b/arch/arm64/kvm/vgic/vgic.h @@ -260,6 +260,9 @@ void vgic_v3_load(struct kvm_vcpu *vcpu); void vgic_v3_put(struct kvm_vcpu *vcpu); void vgic_v3_vmcr_sync(struct kvm_vcpu *vcpu); +void kvm_vgic_vcpu_blocking(struct kvm_vcpu *vcpu); +void kvm_vgic_vcpu_unblocking(struct kvm_vcpu *vcpu); + bool vgic_has_its(struct kvm *kvm); int kvm_vgic_register_its_device(void); void vgic_enable_lpis(struct kvm_vcpu *vcpu); diff --git a/include/kvm/arm_vgic.h b/include/kvm/arm_vgic.h index b6132c062725..54e222fb0aa8 100644 --- a/include/kvm/arm_vgic.h +++ b/include/kvm/arm_vgic.h @@ -459,7 +459,6 @@ int kvm_vgic_vcpu_pending_irq(struct kvm_vcpu *vcpu); void kvm_vgic_load(struct kvm_vcpu *vcpu); void kvm_vgic_put(struct kvm_vcpu *vcpu); -void kvm_vgic_vmcr_sync(struct kvm_vcpu *vcpu); #define vgic_initialized(k) ((k)->arch.vgic.initialized) #define vgic_ready(k) ((k)->arch.vgic.ready) -- 2.33.0
From: wanghaibin <wanghaibin.wang@huawei.com> Abstract the calls to kvm_vgic_vcpu_load/put() via the irqchip_flow structure. No functional change. Signed-off-by: Marc Zyngier <maz@kernel.org> Signed-off-by: wanghaibin <wanghaibin.wang@huawei.com> --- arch/arm64/include/asm/kvm_irq.h | 8 ++++++++ arch/arm64/kvm/arm.c | 4 ++-- arch/arm64/kvm/vgic/vgic-init.c | 2 ++ arch/arm64/kvm/vgic/vgic.h | 3 +++ arch/arm64/kvm/virtcca_cvm.c | 2 +- include/kvm/arm_vgic.h | 3 --- 6 files changed, 16 insertions(+), 6 deletions(-) diff --git a/arch/arm64/include/asm/kvm_irq.h b/arch/arm64/include/asm/kvm_irq.h index efb12693db13..08c93bab3036 100644 --- a/arch/arm64/include/asm/kvm_irq.h +++ b/arch/arm64/include/asm/kvm_irq.h @@ -23,6 +23,8 @@ struct kvm_irqchip_flow { int (*irqchip_vcpu_init)(struct kvm_vcpu *); void (*irqchip_vcpu_blocking)(struct kvm_vcpu *); void (*irqchip_vcpu_unblocking)(struct kvm_vcpu *); + void (*irqchip_vcpu_load)(struct kvm_vcpu *); + void (*irqchip_vcpu_put)(struct kvm_vcpu *); }; /* @@ -67,4 +69,10 @@ struct kvm_irqchip_flow { #define kvm_irqchip_vcpu_unblocking(v) \ __vcpu_irqchip_action((v), vcpu_unblocking, (v)) +#define kvm_irqchip_vcpu_load(v) \ + __vcpu_irqchip_action((v), vcpu_load, (v)) + +#define kvm_irqchip_vcpu_put(v) \ + __vcpu_irqchip_action((v), vcpu_put, (v)) + #endif diff --git a/arch/arm64/kvm/arm.c b/arch/arm64/kvm/arm.c index 11a1bbc4a3b7..8e8526bf8b3a 100644 --- a/arch/arm64/kvm/arm.c +++ b/arch/arm64/kvm/arm.c @@ -731,7 +731,7 @@ void kvm_arch_vcpu_load(struct kvm_vcpu *vcpu, int cpu) vcpu->cpu = cpu; - kvm_vgic_load(vcpu); + kvm_irqchip_vcpu_load(vcpu); kvm_timer_vcpu_load(vcpu); if (kvm_arm_is_pvtime_enabled(&vcpu->arch)) kvm_make_request(KVM_REQ_RECORD_STEAL, vcpu); @@ -769,7 +769,7 @@ void kvm_arch_vcpu_load(struct kvm_vcpu *vcpu, int cpu) void kvm_arch_vcpu_put(struct kvm_vcpu *vcpu) { kvm_timer_vcpu_put(vcpu); - kvm_vgic_put(vcpu); + kvm_irqchip_vcpu_put(vcpu); vcpu->cpu = -1; diff --git a/arch/arm64/kvm/vgic/vgic-init.c b/arch/arm64/kvm/vgic/vgic-init.c index 7367bb5bff1e..62e352e5aeed 100644 --- a/arch/arm64/kvm/vgic/vgic-init.c +++ b/arch/arm64/kvm/vgic/vgic-init.c @@ -28,6 +28,8 @@ static struct kvm_irqchip_flow vgic_irqchip_flow = { .irqchip_vcpu_init = kvm_vgic_vcpu_init, .irqchip_vcpu_blocking = kvm_vgic_vcpu_blocking, .irqchip_vcpu_unblocking = kvm_vgic_vcpu_unblocking, + .irqchip_vcpu_load = kvm_vgic_load, + .irqchip_vcpu_put = kvm_vgic_put, }; /* diff --git a/arch/arm64/kvm/vgic/vgic.h b/arch/arm64/kvm/vgic/vgic.h index 3cf986eeec93..c18604139cd5 100644 --- a/arch/arm64/kvm/vgic/vgic.h +++ b/arch/arm64/kvm/vgic/vgic.h @@ -369,4 +369,7 @@ static inline bool kvm_has_gicv3(struct kvm *kvm) irqchip_is_gic_v3(kvm)); } +void kvm_vgic_load(struct kvm_vcpu *vcpu); +void kvm_vgic_put(struct kvm_vcpu *vcpu); + #endif diff --git a/arch/arm64/kvm/virtcca_cvm.c b/arch/arm64/kvm/virtcca_cvm.c index c270f33ce933..4558afa969bd 100644 --- a/arch/arm64/kvm/virtcca_cvm.c +++ b/arch/arm64/kvm/virtcca_cvm.c @@ -964,7 +964,7 @@ int kvm_load_user_data(struct kvm *kvm, unsigned long arg) void kvm_cvm_vcpu_put(struct kvm_vcpu *vcpu) { kvm_timer_vcpu_put(vcpu); - kvm_vgic_put(vcpu); + kvm_irqchip_vcpu_put(vcpu); vcpu->cpu = -1; } diff --git a/include/kvm/arm_vgic.h b/include/kvm/arm_vgic.h index 54e222fb0aa8..598fde9c578b 100644 --- a/include/kvm/arm_vgic.h +++ b/include/kvm/arm_vgic.h @@ -457,9 +457,6 @@ bool kvm_vgic_map_is_active(struct kvm_vcpu *vcpu, unsigned int vintid); int kvm_vgic_vcpu_pending_irq(struct kvm_vcpu *vcpu); -void kvm_vgic_load(struct kvm_vcpu *vcpu); -void kvm_vgic_put(struct kvm_vcpu *vcpu); - #define vgic_initialized(k) ((k)->arch.vgic.initialized) #define vgic_ready(k) ((k)->arch.vgic.ready) #define vgic_valid_spi(k, i) (((i) >= VGIC_NR_PRIVATE_IRQS) && \ -- 2.33.0
From: wanghaibin <wanghaibin.wang@huawei.com> Abstract the calls to kvm_vgic_vcpu_pending_irq() via the irqchip_flow structure. No functional change. Signed-off-by: Marc Zyngier <maz@kernel.org> Signed-off-by: wanghaibin <wanghaibin.wang@huawei.com> --- arch/arm64/include/asm/kvm_irq.h | 4 ++++ arch/arm64/kvm/arm.c | 5 +++-- arch/arm64/kvm/vgic/vgic-init.c | 1 + arch/arm64/kvm/vgic/vgic.h | 6 ++++++ include/kvm/arm_vgic.h | 3 --- 5 files changed, 14 insertions(+), 5 deletions(-) diff --git a/arch/arm64/include/asm/kvm_irq.h b/arch/arm64/include/asm/kvm_irq.h index 08c93bab3036..00debef7c2ed 100644 --- a/arch/arm64/include/asm/kvm_irq.h +++ b/arch/arm64/include/asm/kvm_irq.h @@ -25,6 +25,7 @@ struct kvm_irqchip_flow { void (*irqchip_vcpu_unblocking)(struct kvm_vcpu *); void (*irqchip_vcpu_load)(struct kvm_vcpu *); void (*irqchip_vcpu_put)(struct kvm_vcpu *); + int (*irqchip_vcpu_pending_irq)(struct kvm_vcpu *); }; /* @@ -75,4 +76,7 @@ struct kvm_irqchip_flow { #define kvm_irqchip_vcpu_put(v) \ __vcpu_irqchip_action((v), vcpu_put, (v)) +#define kvm_irqchip_vcpu_pending_irq(v) \ + __vcpu_irqchip_action_ret((v), vcpu_pending_irq, (v)) + #endif diff --git a/arch/arm64/kvm/arm.c b/arch/arm64/kvm/arm.c index 8e8526bf8b3a..89c5c3b9403c 100644 --- a/arch/arm64/kvm/arm.c +++ b/arch/arm64/kvm/arm.c @@ -867,8 +867,9 @@ int kvm_arch_vcpu_ioctl_set_mpstate(struct kvm_vcpu *vcpu, int kvm_arch_vcpu_runnable(struct kvm_vcpu *v) { bool irq_lines = *vcpu_hcr(v) & (HCR_VI | HCR_VF); - return ((irq_lines || kvm_vgic_vcpu_pending_irq(v)) - && !kvm_arm_vcpu_stopped(v) && !v->arch.pause); + + return ((irq_lines || kvm_irqchip_vcpu_pending_irq(v)) && + !kvm_arm_vcpu_stopped(v) && !v->arch.pause); } bool kvm_arch_vcpu_in_kernel(struct kvm_vcpu *vcpu) diff --git a/arch/arm64/kvm/vgic/vgic-init.c b/arch/arm64/kvm/vgic/vgic-init.c index 62e352e5aeed..d63989cca594 100644 --- a/arch/arm64/kvm/vgic/vgic-init.c +++ b/arch/arm64/kvm/vgic/vgic-init.c @@ -30,6 +30,7 @@ static struct kvm_irqchip_flow vgic_irqchip_flow = { .irqchip_vcpu_unblocking = kvm_vgic_vcpu_unblocking, .irqchip_vcpu_load = kvm_vgic_load, .irqchip_vcpu_put = kvm_vgic_put, + .irqchip_vcpu_pending_irq = kvm_vgic_vcpu_pending_irq, }; /* diff --git a/arch/arm64/kvm/vgic/vgic.h b/arch/arm64/kvm/vgic/vgic.h index c18604139cd5..6858d233c05c 100644 --- a/arch/arm64/kvm/vgic/vgic.h +++ b/arch/arm64/kvm/vgic/vgic.h @@ -369,7 +369,13 @@ static inline bool kvm_has_gicv3(struct kvm *kvm) irqchip_is_gic_v3(kvm)); } +int kvm_vgic_vcpu_pending_irq(struct kvm_vcpu *vcpu); + void kvm_vgic_load(struct kvm_vcpu *vcpu); void kvm_vgic_put(struct kvm_vcpu *vcpu); +void kvm_vgic_sync_hwstate(struct kvm_vcpu *vcpu); +void kvm_vgic_flush_hwstate(struct kvm_vcpu *vcpu); + + #endif diff --git a/include/kvm/arm_vgic.h b/include/kvm/arm_vgic.h index 598fde9c578b..9c2573e3d35f 100644 --- a/include/kvm/arm_vgic.h +++ b/include/kvm/arm_vgic.h @@ -455,14 +455,11 @@ int kvm_vgic_unmap_phys_irq(struct kvm_vcpu *vcpu, unsigned int vintid); int kvm_vgic_get_map(struct kvm_vcpu *vcpu, unsigned int vintid); bool kvm_vgic_map_is_active(struct kvm_vcpu *vcpu, unsigned int vintid); -int kvm_vgic_vcpu_pending_irq(struct kvm_vcpu *vcpu); - #define vgic_initialized(k) ((k)->arch.vgic.initialized) #define vgic_ready(k) ((k)->arch.vgic.ready) #define vgic_valid_spi(k, i) (((i) >= VGIC_NR_PRIVATE_IRQS) && \ ((i) < (k)->arch.vgic.nr_spis + VGIC_NR_PRIVATE_IRQS)) -bool kvm_vcpu_has_pending_irqs(struct kvm_vcpu *vcpu); void kvm_vgic_sync_hwstate(struct kvm_vcpu *vcpu); void kvm_vgic_flush_hwstate(struct kvm_vcpu *vcpu); void kvm_vgic_reset_mapped_irq(struct kvm_vcpu *vcpu, u32 vintid); -- 2.33.0
From: wanghaibin <wanghaibin.wang@huawei.com> The "first run" part of the vgic init is pretty cumbersome, as it leaks all over the place. Reduce its footprint by moving it to an actual per-vcpu "first run" callback, and let it deal with the resource mapping. This allows the vgic_ready() macro to be made vgic-private, and placed in the common vgic code instead of the architecture backends. Signed-off-by: Marc Zyngier <maz@kernel.org> Signed-off-by: wanghaibin <wanghaibin.wang@huawei.com> --- arch/arm64/include/asm/kvm_irq.h | 4 ++++ arch/arm64/kvm/arm.c | 6 +----- arch/arm64/kvm/vgic/vgic-init.c | 12 ++++++------ arch/arm64/kvm/vgic/vgic.h | 2 ++ include/kvm/arm_vgic.h | 2 -- 5 files changed, 13 insertions(+), 13 deletions(-) diff --git a/arch/arm64/include/asm/kvm_irq.h b/arch/arm64/include/asm/kvm_irq.h index 00debef7c2ed..460e32d89d0f 100644 --- a/arch/arm64/include/asm/kvm_irq.h +++ b/arch/arm64/include/asm/kvm_irq.h @@ -26,6 +26,7 @@ struct kvm_irqchip_flow { void (*irqchip_vcpu_load)(struct kvm_vcpu *); void (*irqchip_vcpu_put)(struct kvm_vcpu *); int (*irqchip_vcpu_pending_irq)(struct kvm_vcpu *); + int (*irqchip_vcpu_first_run)(struct kvm_vcpu *); }; /* @@ -79,4 +80,7 @@ struct kvm_irqchip_flow { #define kvm_irqchip_vcpu_pending_irq(v) \ __vcpu_irqchip_action_ret((v), vcpu_pending_irq, (v)) +#define kvm_irqchip_vcpu_first_run(v) \ + __vcpu_irqchip_action_ret((v), vcpu_first_run, (v)) + #endif diff --git a/arch/arm64/kvm/arm.c b/arch/arm64/kvm/arm.c index 89c5c3b9403c..cfd0c5034c04 100644 --- a/arch/arm64/kvm/arm.c +++ b/arch/arm64/kvm/arm.c @@ -916,11 +916,7 @@ int kvm_arch_vcpu_run_pid_change(struct kvm_vcpu *vcpu) #endif if (likely(irqchip_in_kernel(kvm))) { - /* - * Map the VGIC hardware resources before running a vcpu the - * first time on this VM. - */ - ret = kvm_vgic_map_resources(kvm); + ret = kvm_irqchip_vcpu_first_run(vcpu); if (ret) return ret; } diff --git a/arch/arm64/kvm/vgic/vgic-init.c b/arch/arm64/kvm/vgic/vgic-init.c index d63989cca594..a58855f485ae 100644 --- a/arch/arm64/kvm/vgic/vgic-init.c +++ b/arch/arm64/kvm/vgic/vgic-init.c @@ -18,6 +18,7 @@ #include "hisilicon/hisi_virt.h" #endif +static int kvm_vgic_vcpu_first_run(struct kvm_vcpu *vcpu); static int kvm_vgic_vcpu_init(struct kvm_vcpu *vcpu); static void kvm_vgic_destroy(struct kvm *kvm); static void kvm_vgic_vcpu_destroy(struct kvm_vcpu *vcpu); @@ -31,6 +32,7 @@ static struct kvm_irqchip_flow vgic_irqchip_flow = { .irqchip_vcpu_load = kvm_vgic_load, .irqchip_vcpu_put = kvm_vgic_put, .irqchip_vcpu_pending_irq = kvm_vgic_vcpu_pending_irq, + .irqchip_vcpu_first_run = kvm_vgic_vcpu_first_run, }; /* @@ -527,10 +529,11 @@ int vgic_lazy_init(struct kvm *kvm) * v2 calls vgic_init() if not already done. * v3 and derivatives return an error if the VGIC is not initialized. * vgic_ready() returns true if this function has succeeded. - * @kvm: kvm struct pointer - */ -int kvm_vgic_map_resources(struct kvm *kvm) + * @vcpu: vcpu struct pointer +*/ +static int kvm_vgic_vcpu_first_run(struct kvm_vcpu *vcpu) { + struct kvm *kvm = vcpu->kvm; struct vgic_dist *dist = &kvm->arch.vgic; enum vgic_type type; gpa_t dist_base; @@ -544,9 +547,6 @@ int kvm_vgic_map_resources(struct kvm *kvm) if (vgic_ready(kvm)) goto out; - if (!irqchip_in_kernel(kvm)) - goto out; - if (irqchip_is_gic_v2(kvm)) { ret = vgic_v2_map_resources(kvm); type = VGIC_V2; diff --git a/arch/arm64/kvm/vgic/vgic.h b/arch/arm64/kvm/vgic/vgic.h index 6858d233c05c..3f2b8236ebcd 100644 --- a/arch/arm64/kvm/vgic/vgic.h +++ b/arch/arm64/kvm/vgic/vgic.h @@ -104,6 +104,8 @@ static inline u32 vgic_get_implementation_rev(struct kvm_vcpu *vcpu) return vcpu->kvm->arch.vgic.implementation_rev; } +#define vgic_ready(k) ((k)->arch.vgic.ready) + /* Requires the irq_lock to be held by the caller. */ static inline bool irq_is_pending(struct vgic_irq *irq) { diff --git a/include/kvm/arm_vgic.h b/include/kvm/arm_vgic.h index 9c2573e3d35f..b260f4064aab 100644 --- a/include/kvm/arm_vgic.h +++ b/include/kvm/arm_vgic.h @@ -443,7 +443,6 @@ extern struct static_key_false vgic_v3_cpuif_trap; int kvm_set_legacy_vgic_v2_addr(struct kvm *kvm, struct kvm_arm_device_addr *dev_addr); int kvm_vgic_create(struct kvm *kvm, u32 type); -int kvm_vgic_map_resources(struct kvm *kvm); int kvm_vgic_hyp_init(void); void kvm_vgic_init_cpu_hardware(void); @@ -456,7 +455,6 @@ int kvm_vgic_get_map(struct kvm_vcpu *vcpu, unsigned int vintid); bool kvm_vgic_map_is_active(struct kvm_vcpu *vcpu, unsigned int vintid); #define vgic_initialized(k) ((k)->arch.vgic.initialized) -#define vgic_ready(k) ((k)->arch.vgic.ready) #define vgic_valid_spi(k, i) (((i) >= VGIC_NR_PRIVATE_IRQS) && \ ((i) < (k)->arch.vgic.nr_spis + VGIC_NR_PRIVATE_IRQS)) -- 2.33.0
From: wanghaibin <wanghaibin.wang@huawei.com> Abstract the calls to kvm_vgic_vcpu_({sync,flush}_hwstate) via the irqchip_flow structure. No functional change. Signed-off-by: Marc Zyngier <maz@kernel.org> Signed-off-by: wanghaibin <wanghaibin.wang@huawei.com> --- arch/arm64/include/asm/kvm_irq.h | 8 ++++++++ arch/arm64/kvm/arm.c | 6 +++--- arch/arm64/kvm/vgic/vgic-init.c | 2 ++ include/kvm/arm_vgic.h | 2 -- 4 files changed, 13 insertions(+), 5 deletions(-) diff --git a/arch/arm64/include/asm/kvm_irq.h b/arch/arm64/include/asm/kvm_irq.h index 460e32d89d0f..be9c4b1fd2a1 100644 --- a/arch/arm64/include/asm/kvm_irq.h +++ b/arch/arm64/include/asm/kvm_irq.h @@ -27,6 +27,8 @@ struct kvm_irqchip_flow { void (*irqchip_vcpu_put)(struct kvm_vcpu *); int (*irqchip_vcpu_pending_irq)(struct kvm_vcpu *); int (*irqchip_vcpu_first_run)(struct kvm_vcpu *); + void (*irqchip_vcpu_flush_hwstate)(struct kvm_vcpu *); + void (*irqchip_vcpu_sync_hwstate)(struct kvm_vcpu *); }; /* @@ -83,4 +85,10 @@ struct kvm_irqchip_flow { #define kvm_irqchip_vcpu_first_run(v) \ __vcpu_irqchip_action_ret((v), vcpu_first_run, (v)) +#define kvm_irqchip_vcpu_flush_hwstate(v) \ + __vcpu_irqchip_action((v), vcpu_flush_hwstate, (v)) + +#define kvm_irqchip_vcpu_sync_hwstate(v) \ + __vcpu_irqchip_action((v), vcpu_sync_hwstate, (v)) + #endif diff --git a/arch/arm64/kvm/arm.c b/arch/arm64/kvm/arm.c index cfd0c5034c04..5fc108f28bc2 100644 --- a/arch/arm64/kvm/arm.c +++ b/arch/arm64/kvm/arm.c @@ -1307,7 +1307,7 @@ int kvm_arch_vcpu_ioctl_run(struct kvm_vcpu *vcpu) local_irq_disable(); - kvm_vgic_flush_hwstate(vcpu); + kvm_irqchip_vcpu_flush_hwstate(vcpu); kvm_pmu_update_vcpu_events(vcpu); @@ -1325,7 +1325,7 @@ int kvm_arch_vcpu_ioctl_run(struct kvm_vcpu *vcpu) kvm_pmu_sync_hwstate(vcpu); if (unlikely(!irqchip_in_kernel(vcpu->kvm))) kvm_timer_sync_user(vcpu); - kvm_vgic_sync_hwstate(vcpu); + kvm_irqchip_vcpu_sync_hwstate(vcpu); local_irq_enable(); preempt_enable(); continue; @@ -1367,7 +1367,7 @@ int kvm_arch_vcpu_ioctl_run(struct kvm_vcpu *vcpu) * the timer code needs to know if the virtual timer * interrupts are active. */ - kvm_vgic_sync_hwstate(vcpu); + kvm_irqchip_vcpu_sync_hwstate(vcpu); /* * Sync the timer hardware state before enabling interrupts as diff --git a/arch/arm64/kvm/vgic/vgic-init.c b/arch/arm64/kvm/vgic/vgic-init.c index a58855f485ae..3e90db585038 100644 --- a/arch/arm64/kvm/vgic/vgic-init.c +++ b/arch/arm64/kvm/vgic/vgic-init.c @@ -33,6 +33,8 @@ static struct kvm_irqchip_flow vgic_irqchip_flow = { .irqchip_vcpu_put = kvm_vgic_put, .irqchip_vcpu_pending_irq = kvm_vgic_vcpu_pending_irq, .irqchip_vcpu_first_run = kvm_vgic_vcpu_first_run, + .irqchip_vcpu_flush_hwstate = kvm_vgic_flush_hwstate, + .irqchip_vcpu_sync_hwstate = kvm_vgic_sync_hwstate, }; /* diff --git a/include/kvm/arm_vgic.h b/include/kvm/arm_vgic.h index b260f4064aab..158c45df5422 100644 --- a/include/kvm/arm_vgic.h +++ b/include/kvm/arm_vgic.h @@ -458,8 +458,6 @@ bool kvm_vgic_map_is_active(struct kvm_vcpu *vcpu, unsigned int vintid); #define vgic_valid_spi(k, i) (((i) >= VGIC_NR_PRIVATE_IRQS) && \ ((i) < (k)->arch.vgic.nr_spis + VGIC_NR_PRIVATE_IRQS)) -void kvm_vgic_sync_hwstate(struct kvm_vcpu *vcpu); -void kvm_vgic_flush_hwstate(struct kvm_vcpu *vcpu); void kvm_vgic_reset_mapped_irq(struct kvm_vcpu *vcpu, u32 vintid); void vgic_v3_dispatch_sgi(struct kvm_vcpu *vcpu, u64 reg, bool allow_group1); -- 2.33.0
From: wanghaibin <wanghaibin.wang@huawei.com> If we aren't modeling a GIC, there is no need to save/restore the GICv3 state at all. Note that this only matters for nVHE, as VHE already has this code outside of the world switch. Signed-off-by: Marc Zyngier <maz@kernel.org> Signed-off-by: wanghaibin <wanghaibin.wang@huawei.com> --- arch/arm64/kvm/hyp/nvhe/switch.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/arch/arm64/kvm/hyp/nvhe/switch.c b/arch/arm64/kvm/hyp/nvhe/switch.c index ff502b5e1480..68f2f7bf5fc1 100644 --- a/arch/arm64/kvm/hyp/nvhe/switch.c +++ b/arch/arm64/kvm/hyp/nvhe/switch.c @@ -115,7 +115,10 @@ static void __deactivate_traps(struct kvm_vcpu *vcpu) /* Save VGICv3 state on non-VHE systems */ static void __hyp_vgic_save_state(struct kvm_vcpu *vcpu) { - if (static_branch_unlikely(&kvm_vgic_global_state.gicv3_cpuif)) { + struct kvm *kvm = kern_hyp_va(vcpu->kvm); + + if (static_branch_unlikely(&kvm_vgic_global_state.gicv3_cpuif) && + (irqchip_is_gic_v2(kvm) || irqchip_is_gic_v3(kvm))) { __vgic_v3_save_state(&vcpu->arch.vgic_cpu.vgic_v3); __vgic_v3_deactivate_traps(&vcpu->arch.vgic_cpu.vgic_v3); } @@ -124,7 +127,10 @@ static void __hyp_vgic_save_state(struct kvm_vcpu *vcpu) /* Restore VGICv3 state on non-VHE systems */ static void __hyp_vgic_restore_state(struct kvm_vcpu *vcpu) { - if (static_branch_unlikely(&kvm_vgic_global_state.gicv3_cpuif)) { + struct kvm *kvm = kern_hyp_va(vcpu->kvm); + + if (static_branch_unlikely(&kvm_vgic_global_state.gicv3_cpuif) && + (irqchip_is_gic_v2(kvm) || irqchip_is_gic_v3(kvm))) { __vgic_v3_activate_traps(&vcpu->arch.vgic_cpu.vgic_v3); __vgic_v3_restore_state(&vcpu->arch.vgic_cpu.vgic_v3); } -- 2.33.0
From: wanghaibin <wanghaibin.wang@huawei.com> As we continue abstracting away the VGIC, let's make a small change while we're at it: Let's offer two callbacks for "wired" interrupt injection: - Interrupts generated from the kernel itself - Interrupts generated by userspace via the KVM_IRQ_LINE ioctl The various checks are pushed into the vgic code. MSI injection, such as the one used by userspace to tickle the ITS are left alone for now. Signed-off-by: Marc Zyngier <maz@kernel.org> Signed-off-by: wanghaibin <wanghaibin.wang@huawei.com> --- arch/arm64/include/asm/kvm_irq.h | 11 +++++++++++ arch/arm64/kvm/arch_timer.c | 9 +++++---- arch/arm64/kvm/arm.c | 12 ++++-------- arch/arm64/kvm/pmu-emul.c | 4 ++-- arch/arm64/kvm/vgic/vgic-init.c | 2 ++ arch/arm64/kvm/vgic/vgic.c | 20 +++++++++++++++++++- arch/arm64/kvm/vgic/vgic.h | 6 ++++++ include/kvm/arm_vgic.h | 2 -- 8 files changed, 49 insertions(+), 17 deletions(-) diff --git a/arch/arm64/include/asm/kvm_irq.h b/arch/arm64/include/asm/kvm_irq.h index be9c4b1fd2a1..4b5e4a280395 100644 --- a/arch/arm64/include/asm/kvm_irq.h +++ b/arch/arm64/include/asm/kvm_irq.h @@ -29,6 +29,11 @@ struct kvm_irqchip_flow { int (*irqchip_vcpu_first_run)(struct kvm_vcpu *); void (*irqchip_vcpu_flush_hwstate)(struct kvm_vcpu *); void (*irqchip_vcpu_sync_hwstate)(struct kvm_vcpu *); + int (*irqchip_inject_irq)(struct kvm *, unsigned int cpu, + unsigned int intid, bool, void *); + int (*irqchip_inject_userspace_irq)(struct kvm *, unsigned int type, + unsigned int cpu, + unsigned int intid, bool); }; /* @@ -91,4 +96,10 @@ struct kvm_irqchip_flow { #define kvm_irqchip_vcpu_sync_hwstate(v) \ __vcpu_irqchip_action((v), vcpu_sync_hwstate, (v)) +#define kvm_irqchip_inject_irq(k, ...) \ + __kvm_irqchip_action_ret((k), inject_irq, (k), __VA_ARGS__) + +#define kvm_irqchip_inject_userspace_irq(k, ...) \ + __kvm_irqchip_action_ret((k), inject_userspace_irq, (k), __VA_ARGS__) + #endif diff --git a/arch/arm64/kvm/arch_timer.c b/arch/arm64/kvm/arch_timer.c index 42eeec7b9454..d02c7414c4da 100644 --- a/arch/arm64/kvm/arch_timer.c +++ b/arch/arm64/kvm/arch_timer.c @@ -551,10 +551,11 @@ static void kvm_timer_update_irq(struct kvm_vcpu *vcpu, bool new_level, timer_ctx->irq.level); if (!userspace_irqchip(vcpu->kvm)) { - ret = kvm_vgic_inject_irq(vcpu->kvm, vcpu->vcpu_id, - timer_irq(timer_ctx), - timer_ctx->irq.level, - timer_ctx); + ret = kvm_irqchip_inject_irq(vcpu->kvm, vcpu->vcpu_id, + timer_irq(timer_ctx), + timer_ctx->irq.level, + timer_ctx); + WARN_ON(ret); } } diff --git a/arch/arm64/kvm/arm.c b/arch/arm64/kvm/arm.c index 5fc108f28bc2..a0c048944ec2 100644 --- a/arch/arm64/kvm/arm.c +++ b/arch/arm64/kvm/arm.c @@ -1541,18 +1541,14 @@ int kvm_vm_ioctl_irq_line(struct kvm *kvm, struct kvm_irq_level *irq_level, if (!vcpu) return -EINVAL; - if (irq_num < VGIC_NR_SGIS || irq_num >= VGIC_NR_PRIVATE_IRQS) - return -EINVAL; - - return kvm_vgic_inject_irq(kvm, vcpu->vcpu_id, irq_num, level, NULL); + return kvm_irqchip_inject_userspace_irq(kvm, irq_type, vcpu_idx, + irq_num, level); case KVM_ARM_IRQ_TYPE_SPI: if (!irqchip_in_kernel(kvm)) return -ENXIO; - if (irq_num < VGIC_NR_PRIVATE_IRQS) - return -EINVAL; - - return kvm_vgic_inject_irq(kvm, 0, irq_num, level, NULL); + return kvm_irqchip_inject_userspace_irq(kvm, irq_type, 0, + irq_num, level); } return -EINVAL; diff --git a/arch/arm64/kvm/pmu-emul.c b/arch/arm64/kvm/pmu-emul.c index 2cf3c4462b64..9b7c3cb9f76b 100644 --- a/arch/arm64/kvm/pmu-emul.c +++ b/arch/arm64/kvm/pmu-emul.c @@ -372,8 +372,8 @@ static void kvm_pmu_update_state(struct kvm_vcpu *vcpu) pmu->irq_level = overflow; if (likely(irqchip_in_kernel(vcpu->kvm))) { - int ret = kvm_vgic_inject_irq(vcpu->kvm, vcpu->vcpu_id, - pmu->irq_num, overflow, pmu); + int ret = kvm_irqchip_inject_irq(vcpu->kvm, vcpu->vcpu_id, + pmu->irq_num, overflow, pmu); WARN_ON(ret); } } diff --git a/arch/arm64/kvm/vgic/vgic-init.c b/arch/arm64/kvm/vgic/vgic-init.c index 3e90db585038..71fb7b6b1a1b 100644 --- a/arch/arm64/kvm/vgic/vgic-init.c +++ b/arch/arm64/kvm/vgic/vgic-init.c @@ -35,6 +35,8 @@ static struct kvm_irqchip_flow vgic_irqchip_flow = { .irqchip_vcpu_first_run = kvm_vgic_vcpu_first_run, .irqchip_vcpu_flush_hwstate = kvm_vgic_flush_hwstate, .irqchip_vcpu_sync_hwstate = kvm_vgic_sync_hwstate, + .irqchip_inject_irq = kvm_vgic_inject_irq, + .irqchip_inject_userspace_irq = kvm_vgic_inject_userspace_irq, }; /* diff --git a/arch/arm64/kvm/vgic/vgic.c b/arch/arm64/kvm/vgic/vgic.c index 63f97cc2ca32..934ef4918655 100644 --- a/arch/arm64/kvm/vgic/vgic.c +++ b/arch/arm64/kvm/vgic/vgic.c @@ -452,7 +452,7 @@ bool vgic_queue_irq_unlock(struct kvm *kvm, struct vgic_irq *irq, * level-sensitive interrupts. You can think of the level parameter as 1 * being HIGH and 0 being LOW and all devices being active-HIGH. */ -int kvm_vgic_inject_irq(struct kvm *kvm, int cpuid, unsigned int intid, +int kvm_vgic_inject_irq(struct kvm *kvm, unsigned int cpuid, unsigned int intid, bool level, void *owner) { struct kvm_vcpu *vcpu; @@ -494,6 +494,24 @@ int kvm_vgic_inject_irq(struct kvm *kvm, int cpuid, unsigned int intid, return 0; } +int kvm_vgic_inject_userspace_irq(struct kvm *kvm, unsigned int type, + unsigned int cpuid, unsigned int intid, + bool level) +{ + switch (type) { + case KVM_ARM_IRQ_TYPE_PPI: + if (intid < VGIC_NR_SGIS || intid >= VGIC_NR_PRIVATE_IRQS) + return -EINVAL; + return kvm_vgic_inject_irq(kvm, cpuid, intid, level, NULL); + case KVM_ARM_IRQ_TYPE_SPI: + if (intid < VGIC_NR_PRIVATE_IRQS) + return -EINVAL; + return kvm_vgic_inject_irq(kvm, 0, intid, level, NULL); + default: + return -EINVAL; + } +} + /* @irq->irq_lock must be held */ static int kvm_vgic_map_irq(struct kvm_vcpu *vcpu, struct vgic_irq *irq, unsigned int host_irq, diff --git a/arch/arm64/kvm/vgic/vgic.h b/arch/arm64/kvm/vgic/vgic.h index 3f2b8236ebcd..ec149e8dd59b 100644 --- a/arch/arm64/kvm/vgic/vgic.h +++ b/arch/arm64/kvm/vgic/vgic.h @@ -265,6 +265,12 @@ void vgic_v3_vmcr_sync(struct kvm_vcpu *vcpu); void kvm_vgic_vcpu_blocking(struct kvm_vcpu *vcpu); void kvm_vgic_vcpu_unblocking(struct kvm_vcpu *vcpu); +int kvm_vgic_inject_irq(struct kvm *kvm, unsigned int cpuid, unsigned int intid, + bool level, void *owner); +int kvm_vgic_inject_userspace_irq(struct kvm *kvm, unsigned int type, + unsigned int cpuid, unsigned int intid, + bool level); + bool vgic_has_its(struct kvm *kvm); int kvm_vgic_register_its_device(void); void vgic_enable_lpis(struct kvm_vcpu *vcpu); diff --git a/include/kvm/arm_vgic.h b/include/kvm/arm_vgic.h index 158c45df5422..53a0c3d20fc6 100644 --- a/include/kvm/arm_vgic.h +++ b/include/kvm/arm_vgic.h @@ -446,8 +446,6 @@ int kvm_vgic_create(struct kvm *kvm, u32 type); int kvm_vgic_hyp_init(void); void kvm_vgic_init_cpu_hardware(void); -int kvm_vgic_inject_irq(struct kvm *kvm, int cpuid, unsigned int intid, - bool level, void *owner); int kvm_vgic_map_phys_irq(struct kvm_vcpu *vcpu, unsigned int host_irq, u32 vintid, struct irq_ops *ops); int kvm_vgic_unmap_phys_irq(struct kvm_vcpu *vcpu, unsigned int vintid); -- 2.33.0
From: wanghaibin <wanghaibin.wang@huawei.com> As we continue cutting a VGIC-shaped hole in KVM, let's indirect all of the handling of mapped interrupts into the bit irqchip_flow bucket. Signed-off-by: Marc Zyngier <maz@kernel.org> Signed-off-by: wanghaibin <wanghaibin.wang@huawei.com> --- arch/arm64/include/asm/kvm_irq.h | 18 +++++++++++++ arch/arm64/kvm/arch_timer.c | 44 ++++++++++++++++---------------- arch/arm64/kvm/vgic/vgic-init.c | 4 +++ arch/arm64/kvm/vgic/vgic.h | 8 ++++++ include/kvm/arm_vgic.h | 6 ----- 5 files changed, 52 insertions(+), 28 deletions(-) diff --git a/arch/arm64/include/asm/kvm_irq.h b/arch/arm64/include/asm/kvm_irq.h index 4b5e4a280395..40fe9e826ee4 100644 --- a/arch/arm64/include/asm/kvm_irq.h +++ b/arch/arm64/include/asm/kvm_irq.h @@ -6,6 +6,7 @@ #ifndef __ARM64_KVM_IRQ_H__ #define __ARM64_KVM_IRQ_H__ +#include <kvm/arm_vgic.h> /* fix */ enum kvm_irqchip_type { IRQCHIP_USER, /* Implemented in userspace */ @@ -34,6 +35,11 @@ struct kvm_irqchip_flow { int (*irqchip_inject_userspace_irq)(struct kvm *, unsigned int type, unsigned int cpu, unsigned int intid, bool); + bool (*irqchip_map_is_active)(struct kvm_vcpu *, unsigned in); + void (*irqchip_reset_mapped_irq)(struct kvm_vcpu *, u32); + int (*irqchip_map_phys_irq)(struct kvm_vcpu *, unsigned int, + u32, struct irq_ops *); + int (*irqchip_unmap_phys_irq)(struct kvm_vcpu *, unsigned int); }; /* @@ -102,4 +108,16 @@ struct kvm_irqchip_flow { #define kvm_irqchip_inject_userspace_irq(k, ...) \ __kvm_irqchip_action_ret((k), inject_userspace_irq, (k), __VA_ARGS__) +#define kvm_irqchip_map_is_active(v, ...) \ + __vcpu_irqchip_action_ret((v), map_is_active, (v), __VA_ARGS__) + +#define kvm_irqchip_reset_mapped_irq(v, ...) \ + __vcpu_irqchip_action((v), reset_mapped_irq, (v), __VA_ARGS__) + +#define kvm_irqchip_map_phys_irq(v, ...) \ + __vcpu_irqchip_action_ret((v), map_phys_irq, (v), __VA_ARGS__) + +#define kvm_irqchip_unmap_phys_irq(v, ...) \ + __vcpu_irqchip_action_ret((v), unmap_phys_irq, (v), __VA_ARGS__) + #endif diff --git a/arch/arm64/kvm/arch_timer.c b/arch/arm64/kvm/arch_timer.c index d02c7414c4da..cb652edb04a7 100644 --- a/arch/arm64/kvm/arch_timer.c +++ b/arch/arm64/kvm/arch_timer.c @@ -811,7 +811,7 @@ static void kvm_timer_vcpu_load_gic(struct arch_timer_context *ctx) kvm_timer_update_irq(ctx->vcpu, kvm_timer_should_fire(ctx), ctx); if (irqchip_in_kernel(vcpu->kvm)) - phys_active = kvm_vgic_map_is_active(vcpu, timer_irq(ctx)); + phys_active = kvm_irqchip_map_is_active(vcpu, timer_irq(ctx)); phys_active |= ctx->irq.level; @@ -875,18 +875,18 @@ static void kvm_timer_vcpu_load_nested_switch(struct kvm_vcpu *vcpu, */ hw = kvm_vgic_get_map(vcpu, timer_irq(map->direct_vtimer)); if (hw < 0) { - kvm_vgic_unmap_phys_irq(vcpu, timer_irq(map->emul_vtimer)); - kvm_vgic_unmap_phys_irq(vcpu, timer_irq(map->emul_ptimer)); + kvm_irqchip_unmap_phys_irq(vcpu, timer_irq(map->emul_vtimer)); + kvm_irqchip_unmap_phys_irq(vcpu, timer_irq(map->emul_ptimer)); - ret = kvm_vgic_map_phys_irq(vcpu, - map->direct_vtimer->host_timer_irq, - timer_irq(map->direct_vtimer), - &arch_timer_irq_ops); + ret = kvm_irqchip_map_phys_irq(vcpu, + map->direct_vtimer->host_timer_irq, + timer_irq(map->direct_vtimer), + &arch_timer_irq_ops); WARN_ON_ONCE(ret); - ret = kvm_vgic_map_phys_irq(vcpu, - map->direct_ptimer->host_timer_irq, - timer_irq(map->direct_ptimer), - &arch_timer_irq_ops); + ret = kvm_irqchip_map_phys_irq(vcpu, + map->direct_ptimer->host_timer_irq, + timer_irq(map->direct_ptimer), + &arch_timer_irq_ops); WARN_ON_ONCE(ret); /* @@ -1190,7 +1190,7 @@ int kvm_timer_vcpu_reset(struct kvm_vcpu *vcpu) kvm_timer_update_irq(vcpu, false, vcpu_ptimer(vcpu)); if (irqchip_in_kernel(vcpu->kvm) && map.direct_ptimer) - kvm_vgic_reset_mapped_irq(vcpu, timer_irq(map.direct_ptimer)); + kvm_irqchip_reset_mapped_irq(vcpu, timer_irq(map.direct_ptimer)); goto skip_reset_vtimer; } @@ -1200,9 +1200,9 @@ int kvm_timer_vcpu_reset(struct kvm_vcpu *vcpu) vcpu_get_timer(vcpu, i)); if (irqchip_in_kernel(vcpu->kvm)) { - kvm_vgic_reset_mapped_irq(vcpu, timer_irq(map.direct_vtimer)); + kvm_irqchip_reset_mapped_irq(vcpu, timer_irq(map.direct_vtimer)); if (map.direct_ptimer) - kvm_vgic_reset_mapped_irq(vcpu, timer_irq(map.direct_ptimer)); + kvm_irqchip_reset_mapped_irq(vcpu, timer_irq(map.direct_ptimer)); } } @@ -1901,10 +1901,10 @@ int kvm_timer_enable(struct kvm_vcpu *vcpu) if (vtimer_is_irqbypass()) goto skip_map_vtimer; #endif - ret = kvm_vgic_map_phys_irq(vcpu, - map.direct_vtimer->host_timer_irq, - timer_irq(map.direct_vtimer), - &arch_timer_irq_ops); + ret = kvm_irqchip_map_phys_irq(vcpu, + map.direct_vtimer->host_timer_irq, + timer_irq(map.direct_vtimer), + &arch_timer_irq_ops); if (ret) return ret; @@ -1912,10 +1912,10 @@ int kvm_timer_enable(struct kvm_vcpu *vcpu) skip_map_vtimer: #endif if (map.direct_ptimer) { - ret = kvm_vgic_map_phys_irq(vcpu, - map.direct_ptimer->host_timer_irq, - timer_irq(map.direct_ptimer), - &arch_timer_irq_ops); + ret = kvm_irqchip_map_phys_irq(vcpu, + map.direct_ptimer->host_timer_irq, + timer_irq(map.direct_ptimer), + &arch_timer_irq_ops); } if (ret) diff --git a/arch/arm64/kvm/vgic/vgic-init.c b/arch/arm64/kvm/vgic/vgic-init.c index 71fb7b6b1a1b..e2667f8c5002 100644 --- a/arch/arm64/kvm/vgic/vgic-init.c +++ b/arch/arm64/kvm/vgic/vgic-init.c @@ -37,6 +37,10 @@ static struct kvm_irqchip_flow vgic_irqchip_flow = { .irqchip_vcpu_sync_hwstate = kvm_vgic_sync_hwstate, .irqchip_inject_irq = kvm_vgic_inject_irq, .irqchip_inject_userspace_irq = kvm_vgic_inject_userspace_irq, + .irqchip_map_is_active = kvm_vgic_map_is_active, + .irqchip_reset_mapped_irq = kvm_vgic_reset_mapped_irq, + .irqchip_map_phys_irq = kvm_vgic_map_phys_irq, + .irqchip_unmap_phys_irq = kvm_vgic_unmap_phys_irq, }; /* diff --git a/arch/arm64/kvm/vgic/vgic.h b/arch/arm64/kvm/vgic/vgic.h index ec149e8dd59b..612d161dc409 100644 --- a/arch/arm64/kvm/vgic/vgic.h +++ b/arch/arm64/kvm/vgic/vgic.h @@ -377,6 +377,14 @@ static inline bool kvm_has_gicv3(struct kvm *kvm) irqchip_is_gic_v3(kvm)); } +bool kvm_vgic_map_is_active(struct kvm_vcpu *vcpu, unsigned int vintid); +void kvm_vgic_reset_mapped_irq(struct kvm_vcpu *vcpu, u32 vintid); + +int kvm_vgic_map_phys_irq(struct kvm_vcpu *vcpu, unsigned int host_irq, + u32 vintid, struct irq_ops *ops); +int kvm_vgic_unmap_phys_irq(struct kvm_vcpu *vcpu, unsigned int vintid); + + int kvm_vgic_vcpu_pending_irq(struct kvm_vcpu *vcpu); void kvm_vgic_load(struct kvm_vcpu *vcpu); diff --git a/include/kvm/arm_vgic.h b/include/kvm/arm_vgic.h index 53a0c3d20fc6..9bd83bc8e1aa 100644 --- a/include/kvm/arm_vgic.h +++ b/include/kvm/arm_vgic.h @@ -446,18 +446,12 @@ int kvm_vgic_create(struct kvm *kvm, u32 type); int kvm_vgic_hyp_init(void); void kvm_vgic_init_cpu_hardware(void); -int kvm_vgic_map_phys_irq(struct kvm_vcpu *vcpu, unsigned int host_irq, - u32 vintid, struct irq_ops *ops); -int kvm_vgic_unmap_phys_irq(struct kvm_vcpu *vcpu, unsigned int vintid); int kvm_vgic_get_map(struct kvm_vcpu *vcpu, unsigned int vintid); -bool kvm_vgic_map_is_active(struct kvm_vcpu *vcpu, unsigned int vintid); #define vgic_initialized(k) ((k)->arch.vgic.initialized) #define vgic_valid_spi(k, i) (((i) >= VGIC_NR_PRIVATE_IRQS) && \ ((i) < (k)->arch.vgic.nr_spis + VGIC_NR_PRIVATE_IRQS)) -void kvm_vgic_reset_mapped_irq(struct kvm_vcpu *vcpu, u32 vintid); - void vgic_v3_dispatch_sgi(struct kvm_vcpu *vcpu, u64 reg, bool allow_group1); #ifdef CONFIG_VIRT_VTIMER_IRQ_BYPASS -- 2.33.0
From: wanghaibin <wanghaibin.wang@huawei.com> Move the set_owner callback into irqchip_flow. It's not that useful an API anyway, and we should consider getting rid of it. Signed-off-by: Marc Zyngier <maz@kernel.org> Signed-off-by: wanghaibin <wanghaibin.wang@huawei.com> --- arch/arm64/include/asm/kvm_irq.h | 4 ++++ arch/arm64/kvm/arch_timer.c | 2 +- arch/arm64/kvm/pmu-emul.c | 4 ++-- arch/arm64/kvm/vgic/vgic-init.c | 1 + arch/arm64/kvm/vgic/vgic.h | 1 + include/kvm/arm_vgic.h | 2 -- 6 files changed, 9 insertions(+), 5 deletions(-) diff --git a/arch/arm64/include/asm/kvm_irq.h b/arch/arm64/include/asm/kvm_irq.h index 40fe9e826ee4..5665bb668c19 100644 --- a/arch/arm64/include/asm/kvm_irq.h +++ b/arch/arm64/include/asm/kvm_irq.h @@ -40,6 +40,7 @@ struct kvm_irqchip_flow { int (*irqchip_map_phys_irq)(struct kvm_vcpu *, unsigned int, u32, struct irq_ops *); int (*irqchip_unmap_phys_irq)(struct kvm_vcpu *, unsigned int); + int (*irqchip_set_owner)(struct kvm_vcpu *, unsigned int, void *); }; /* @@ -120,4 +121,7 @@ struct kvm_irqchip_flow { #define kvm_irqchip_unmap_phys_irq(v, ...) \ __vcpu_irqchip_action_ret((v), unmap_phys_irq, (v), __VA_ARGS__) +#define kvm_irqchip_set_owner(v, ...) \ + __vcpu_irqchip_action_ret((v), set_owner, (v), __VA_ARGS__) + #endif diff --git a/arch/arm64/kvm/arch_timer.c b/arch/arm64/kvm/arch_timer.c index cb652edb04a7..5548d04b0bf9 100644 --- a/arch/arm64/kvm/arch_timer.c +++ b/arch/arm64/kvm/arch_timer.c @@ -1746,7 +1746,7 @@ static bool timer_irqs_are_valid(struct kvm_vcpu *vcpu) ctx = vcpu_get_timer(vcpu, i); irq = timer_irq(ctx); - if (kvm_vgic_set_owner(vcpu, irq, ctx)) + if (kvm_irqchip_set_owner(vcpu, irq, ctx)) break; /* diff --git a/arch/arm64/kvm/pmu-emul.c b/arch/arm64/kvm/pmu-emul.c index 9b7c3cb9f76b..fdc27f16e157 100644 --- a/arch/arm64/kvm/pmu-emul.c +++ b/arch/arm64/kvm/pmu-emul.c @@ -859,8 +859,8 @@ static int kvm_arm_pmu_v3_init(struct kvm_vcpu *vcpu) if (!kvm_arm_pmu_irq_initialized(vcpu)) return -ENXIO; - ret = kvm_vgic_set_owner(vcpu, vcpu->arch.pmu.irq_num, - &vcpu->arch.pmu); + ret = kvm_irqchip_set_owner(vcpu, vcpu->arch.pmu.irq_num, + &vcpu->arch.pmu); if (ret) return ret; } diff --git a/arch/arm64/kvm/vgic/vgic-init.c b/arch/arm64/kvm/vgic/vgic-init.c index e2667f8c5002..8e584fefe768 100644 --- a/arch/arm64/kvm/vgic/vgic-init.c +++ b/arch/arm64/kvm/vgic/vgic-init.c @@ -41,6 +41,7 @@ static struct kvm_irqchip_flow vgic_irqchip_flow = { .irqchip_reset_mapped_irq = kvm_vgic_reset_mapped_irq, .irqchip_map_phys_irq = kvm_vgic_map_phys_irq, .irqchip_unmap_phys_irq = kvm_vgic_unmap_phys_irq, + .irqchip_set_owner = kvm_vgic_set_owner, }; /* diff --git a/arch/arm64/kvm/vgic/vgic.h b/arch/arm64/kvm/vgic/vgic.h index 612d161dc409..c759bef004ba 100644 --- a/arch/arm64/kvm/vgic/vgic.h +++ b/arch/arm64/kvm/vgic/vgic.h @@ -383,6 +383,7 @@ void kvm_vgic_reset_mapped_irq(struct kvm_vcpu *vcpu, u32 vintid); int kvm_vgic_map_phys_irq(struct kvm_vcpu *vcpu, unsigned int host_irq, u32 vintid, struct irq_ops *ops); int kvm_vgic_unmap_phys_irq(struct kvm_vcpu *vcpu, unsigned int vintid); +int kvm_vgic_set_owner(struct kvm_vcpu *vcpu, unsigned int intid, void *owner); int kvm_vgic_vcpu_pending_irq(struct kvm_vcpu *vcpu); diff --git a/include/kvm/arm_vgic.h b/include/kvm/arm_vgic.h index 9bd83bc8e1aa..45a4d786b75a 100644 --- a/include/kvm/arm_vgic.h +++ b/include/kvm/arm_vgic.h @@ -481,8 +481,6 @@ static inline int kvm_vgic_get_max_vcpus(void) */ int kvm_vgic_setup_default_irq_routing(struct kvm *kvm); -int kvm_vgic_set_owner(struct kvm_vcpu *vcpu, unsigned int intid, void *owner); - struct kvm_kernel_irq_routing_entry; int kvm_vgic_v4_set_forwarding(struct kvm *kvm, int irq, -- 2.33.0
From: wanghaibin <wanghaibin.wang@huawei.com> As we aim to make the core KVM/arm64 code GIC-agnostic, let's turn vgic_initialized into something more generic, and move the corresponding flag outside of the vgic data structure. Signed-off-by: Marc Zyngier <maz@kernel.org> Signed-off-by: wanghaibin <wanghaibin.wang@huawei.com> --- arch/arm64/include/asm/kvm_host.h | 1 + arch/arm64/include/asm/kvm_irq.h | 2 ++ arch/arm64/kvm/arm.c | 4 ++-- arch/arm64/kvm/pmu-emul.c | 2 +- arch/arm64/kvm/vgic/vgic-debug.c | 2 +- arch/arm64/kvm/vgic/vgic-init.c | 11 +++++------ arch/arm64/kvm/vgic/vgic-irqfd.c | 2 +- arch/arm64/kvm/vgic/vgic-its.c | 2 +- arch/arm64/kvm/vgic/vgic-kvm-device.c | 2 +- arch/arm64/kvm/vgic/vgic-v3.c | 4 ++-- arch/arm64/kvm/vgic/vgic.c | 10 +++++----- include/kvm/arm_vgic.h | 2 -- 12 files changed, 22 insertions(+), 22 deletions(-) diff --git a/arch/arm64/include/asm/kvm_host.h b/arch/arm64/include/asm/kvm_host.h index 3526c2256e94..9329163ffc93 100644 --- a/arch/arm64/include/asm/kvm_host.h +++ b/arch/arm64/include/asm/kvm_host.h @@ -219,6 +219,7 @@ struct kvm_arch { /* Interrupt controller */ enum kvm_irqchip_type irqchip_type; + bool irqchip_finalized; struct kvm_irqchip_flow irqchip_flow; struct vgic_dist vgic; diff --git a/arch/arm64/include/asm/kvm_irq.h b/arch/arm64/include/asm/kvm_irq.h index 5665bb668c19..5696dd949ad1 100644 --- a/arch/arm64/include/asm/kvm_irq.h +++ b/arch/arm64/include/asm/kvm_irq.h @@ -18,6 +18,8 @@ enum kvm_irqchip_type { #define irqchip_is_gic_v2(k) ((k)->arch.irqchip_type == IRQCHIP_GICv2) #define irqchip_is_gic_v3(k) ((k)->arch.irqchip_type == IRQCHIP_GICv3) +#define irqchip_finalized(k) ((k)->arch.irqchip_finalized) + struct kvm_irqchip_flow { void (*irqchip_destroy)(struct kvm *); void (*irqchip_vcpu_destroy)(struct kvm_vcpu *vcpu); diff --git a/arch/arm64/kvm/arm.c b/arch/arm64/kvm/arm.c index a0c048944ec2..328afbfb9c96 100644 --- a/arch/arm64/kvm/arm.c +++ b/arch/arm64/kvm/arm.c @@ -582,7 +582,7 @@ struct kvm *kvm_arch_alloc_vm(void) int kvm_arch_vcpu_precreate(struct kvm *kvm, unsigned int id) { - if (irqchip_in_kernel(kvm) && vgic_initialized(kvm)) + if (irqchip_in_kernel(kvm) && irqchip_finalized(kvm)) return -EBUSY; if (id >= kvm->max_vcpus) @@ -971,7 +971,7 @@ void kvm_arch_pinned_vmid_put(struct kvm *kvm) bool kvm_arch_intc_initialized(struct kvm *kvm) { - return vgic_initialized(kvm); + return (irqchip_in_kernel(kvm) && irqchip_finalized(kvm)); } void kvm_arm_halt_guest(struct kvm *kvm) diff --git a/arch/arm64/kvm/pmu-emul.c b/arch/arm64/kvm/pmu-emul.c index fdc27f16e157..9776e4259021 100644 --- a/arch/arm64/kvm/pmu-emul.c +++ b/arch/arm64/kvm/pmu-emul.c @@ -853,7 +853,7 @@ static int kvm_arm_pmu_v3_init(struct kvm_vcpu *vcpu) * implementation, we require the GIC to be already * initialized when initializing the PMU. */ - if (!vgic_initialized(vcpu->kvm)) + if (!irqchip_finalized(vcpu->kvm)) return -ENODEV; if (!kvm_arm_pmu_irq_initialized(vcpu)) diff --git a/arch/arm64/kvm/vgic/vgic-debug.c b/arch/arm64/kvm/vgic/vgic-debug.c index 660499e5c797..9600fd891376 100644 --- a/arch/arm64/kvm/vgic/vgic-debug.c +++ b/arch/arm64/kvm/vgic/vgic-debug.c @@ -242,7 +242,7 @@ static int vgic_debug_show(struct seq_file *s, void *v) return 0; } - if (!kvm->arch.vgic.initialized) + if (!irqchip_finalized(kvm)) return 0; if (iter->vcpu_id < iter->nr_cpus) diff --git a/arch/arm64/kvm/vgic/vgic-init.c b/arch/arm64/kvm/vgic/vgic-init.c index 8e584fefe768..5f37d2362453 100644 --- a/arch/arm64/kvm/vgic/vgic-init.c +++ b/arch/arm64/kvm/vgic/vgic-init.c @@ -336,7 +336,7 @@ static void kvm_vgic_vcpu_enable(struct kvm_vcpu *vcpu) * - the number of vcpus * The function is generally called when nr_spis has been explicitly set * by the guest through the KVM DEVICE API. If not nr_spis is set to 256. - * vgic_initialized() returns true when this function has succeeded. + * irqchip_finalized() returns true when this function has succeeded. */ int vgic_init(struct kvm *kvm) { @@ -347,7 +347,7 @@ int vgic_init(struct kvm *kvm) lockdep_assert_held(&kvm->arch.config_lock); - if (vgic_initialized(kvm)) + if (irqchip_finalized(kvm)) return 0; /* Are we also in the middle of creating a VCPU? */ @@ -421,8 +421,7 @@ int vgic_init(struct kvm *kvm) dist->has_nmi = kvm_vgic_global_state.has_nmi; } - dist->initialized = true; - + kvm->arch.irqchip_finalized = true; out: return ret; } @@ -432,8 +431,8 @@ static void kvm_vgic_dist_destroy(struct kvm *kvm) struct vgic_dist *dist = &kvm->arch.vgic; struct vgic_redist_region *rdreg, *next; + kvm->arch.irqchip_finalized = false; dist->ready = false; - dist->initialized = false; kfree(dist->spis); dist->spis = NULL; @@ -511,7 +510,7 @@ int vgic_lazy_init(struct kvm *kvm) { int ret = 0; - if (unlikely(!vgic_initialized(kvm))) { + if (unlikely(!irqchip_finalized(kvm))) { /* * We only provide the automatic initialization of the VGIC * for the legacy case of a GICv2. Any other type must diff --git a/arch/arm64/kvm/vgic/vgic-irqfd.c b/arch/arm64/kvm/vgic/vgic-irqfd.c index f7de55ae55be..e3c1197332b5 100644 --- a/arch/arm64/kvm/vgic/vgic-irqfd.c +++ b/arch/arm64/kvm/vgic/vgic-irqfd.c @@ -168,7 +168,7 @@ int kvm_arch_set_irq_inatomic(struct kvm_kernel_irq_routing_entry *e, * Injecting SPIs is always possible in atomic context * as long as the damn vgic is initialized. */ - if (unlikely(!vgic_initialized(kvm))) + if (unlikely(!irqchip_finalized(kvm))) break; return vgic_irqfd_set_irq(e, kvm, irq_source_id, 1, line_status); } diff --git a/arch/arm64/kvm/vgic/vgic-its.c b/arch/arm64/kvm/vgic/vgic-its.c index fdeb22083196..f1e88e08564b 100644 --- a/arch/arm64/kvm/vgic/vgic-its.c +++ b/arch/arm64/kvm/vgic/vgic-its.c @@ -2209,7 +2209,7 @@ static int vgic_its_create(struct kvm_device *dev, u32 type) mutex_lock(&dev->kvm->arch.config_lock); - if (vgic_initialized(dev->kvm)) { + if (irqchip_finalized(dev->kvm)) { ret = vgic_v4_init(dev->kvm); if (ret < 0) { mutex_unlock(&dev->kvm->arch.config_lock); diff --git a/arch/arm64/kvm/vgic/vgic-kvm-device.c b/arch/arm64/kvm/vgic/vgic-kvm-device.c index d035d9eedb6d..4cfb873ebaf8 100644 --- a/arch/arm64/kvm/vgic/vgic-kvm-device.c +++ b/arch/arm64/kvm/vgic/vgic-kvm-device.c @@ -554,7 +554,7 @@ static int vgic_v3_attr_regs_access(struct kvm_device *dev, mutex_lock(&dev->kvm->arch.config_lock); - if (unlikely(!vgic_initialized(dev->kvm))) { + if (unlikely(!irqchip_finalized(dev->kvm))) { ret = -EBUSY; goto out; } diff --git a/arch/arm64/kvm/vgic/vgic-v3.c b/arch/arm64/kvm/vgic/vgic-v3.c index f0fe7737a8ed..9c4b397b9cba 100644 --- a/arch/arm64/kvm/vgic/vgic-v3.c +++ b/arch/arm64/kvm/vgic/vgic-v3.c @@ -386,7 +386,7 @@ int vgic_v3_save_pending_tables(struct kvm *kvm) int ret = 0; u8 val; - if (unlikely(!vgic_initialized(kvm))) + if (unlikely(!irqchip_finalized(kvm))) return -ENXIO; /* @@ -567,7 +567,7 @@ int vgic_v3_map_resources(struct kvm *kvm) * For a VGICv3 we require the userland to explicitly initialize * the VGIC before we need to use it. */ - if (!vgic_initialized(kvm)) { + if (!irqchip_finalized(kvm)) { return -EBUSY; } diff --git a/arch/arm64/kvm/vgic/vgic.c b/arch/arm64/kvm/vgic/vgic.c index 934ef4918655..8ce7666972d3 100644 --- a/arch/arm64/kvm/vgic/vgic.c +++ b/arch/arm64/kvm/vgic/vgic.c @@ -595,7 +595,7 @@ int kvm_vgic_unmap_phys_irq(struct kvm_vcpu *vcpu, unsigned int vintid) struct vgic_irq *irq; unsigned long flags; - if (!vgic_initialized(vcpu->kvm)) + if (!irqchip_finalized(vcpu->kvm)) return -EAGAIN; irq = vgic_get_irq(vcpu->kvm, vcpu, vintid); @@ -666,7 +666,7 @@ int kvm_vgic_set_owner(struct kvm_vcpu *vcpu, unsigned int intid, void *owner) unsigned long flags; int ret = 0; - if (!vgic_initialized(vcpu->kvm)) + if (!irqchip_finalized(vcpu->kvm)) return -EAGAIN; /* SGIs and LPIs cannot be wired up to any device */ @@ -1071,7 +1071,7 @@ void kvm_vgic_flush_hwstate(struct kvm_vcpu *vcpu) void kvm_vgic_load(struct kvm_vcpu *vcpu) { if (unlikely(!irqchip_in_kernel(vcpu->kvm) || - !vgic_initialized(vcpu->kvm) || + !irqchip_finalized(vcpu->kvm) || vcpu_is_rec(vcpu))) { if (has_vhe() && static_branch_unlikely(&kvm_vgic_global_state.gicv3_cpuif)) __vgic_v3_activate_traps(&vcpu->arch.vgic_cpu.vgic_v3); @@ -1087,7 +1087,7 @@ void kvm_vgic_load(struct kvm_vcpu *vcpu) void kvm_vgic_put(struct kvm_vcpu *vcpu) { if (unlikely(!irqchip_in_kernel(vcpu->kvm) || - !vgic_initialized(vcpu->kvm) || + !irqchip_finalized(vcpu->kvm) || vcpu_is_rec(vcpu))) { if (has_vhe() && static_branch_unlikely(&kvm_vgic_global_state.gicv3_cpuif)) __vgic_v3_deactivate_traps(&vcpu->arch.vgic_cpu.vgic_v3); @@ -1187,7 +1187,7 @@ bool kvm_vgic_map_is_active(struct kvm_vcpu *vcpu, unsigned int vintid) bool map_is_active; unsigned long flags; - if (!vgic_initialized(vcpu->kvm)) + if (!irqchip_finalized(vcpu->kvm)) return false; irq = vgic_get_irq(vcpu->kvm, vcpu, vintid); diff --git a/include/kvm/arm_vgic.h b/include/kvm/arm_vgic.h index 45a4d786b75a..1c11a13716e0 100644 --- a/include/kvm/arm_vgic.h +++ b/include/kvm/arm_vgic.h @@ -282,7 +282,6 @@ struct vgic_redist_region { struct vgic_dist { bool ready; - bool initialized; /* Implementation revision as reported in the GICD_IIDR */ u32 implementation_rev; @@ -448,7 +447,6 @@ void kvm_vgic_init_cpu_hardware(void); int kvm_vgic_get_map(struct kvm_vcpu *vcpu, unsigned int vintid); -#define vgic_initialized(k) ((k)->arch.vgic.initialized) #define vgic_valid_spi(k, i) (((i) >= VGIC_NR_PRIVATE_IRQS) && \ ((i) < (k)->arch.vgic.nr_spis + VGIC_NR_PRIVATE_IRQS)) -- 2.33.0
From: wanghaibin <wanghaibin.wang@huawei.com> irqfd handling is still hidden away in the vgic code. Let's extract it and move the generic part in the non-GIC code, with the now required abstraction in the irqchip_flow struct. Signed-off-by: Marc Zyngier <maz@kernel.org> Signed-off-by: wanghaibin <wanghaibin.wang@huawei.com> --- arch/arm64/include/asm/kvm_irq.h | 11 +++++ arch/arm64/kvm/arm.c | 68 ++++++++++++++++++++++++++++++ arch/arm64/kvm/vgic/vgic-init.c | 3 ++ arch/arm64/kvm/vgic/vgic-irqfd.c | 72 ++++++-------------------------- arch/arm64/kvm/vgic/vgic.h | 10 +++++ 5 files changed, 104 insertions(+), 60 deletions(-) diff --git a/arch/arm64/include/asm/kvm_irq.h b/arch/arm64/include/asm/kvm_irq.h index 5696dd949ad1..8676c68085a6 100644 --- a/arch/arm64/include/asm/kvm_irq.h +++ b/arch/arm64/include/asm/kvm_irq.h @@ -20,6 +20,8 @@ enum kvm_irqchip_type { #define irqchip_finalized(k) ((k)->arch.irqchip_finalized) +struct kvm_kernel_irq_routing_entry; + struct kvm_irqchip_flow { void (*irqchip_destroy)(struct kvm *); void (*irqchip_vcpu_destroy)(struct kvm_vcpu *vcpu); @@ -43,6 +45,15 @@ struct kvm_irqchip_flow { u32, struct irq_ops *); int (*irqchip_unmap_phys_irq)(struct kvm_vcpu *, unsigned int); int (*irqchip_set_owner)(struct kvm_vcpu *, unsigned int, void *); + int (*irqchip_irqfd_set_irq)(struct kvm_kernel_irq_routing_entry *e, + struct kvm *kvm, int irq_source_id, + int level, bool line_status); + int (*irqchip_set_msi)(struct kvm_kernel_irq_routing_entry *e, + struct kvm *kvm, int irq_source_id, + int level, bool line_status); + int (*irqchip_set_irq_inatomic)(struct kvm_kernel_irq_routing_entry *e, + struct kvm *kvm, int irq_source_id, + int level, bool line_status); }; /* diff --git a/arch/arm64/kvm/arm.c b/arch/arm64/kvm/arm.c index 328afbfb9c96..2e67b6353c06 100644 --- a/arch/arm64/kvm/arm.c +++ b/arch/arm64/kvm/arm.c @@ -1233,6 +1233,74 @@ static int noinstr kvm_arm_vcpu_enter_exit(struct kvm_vcpu *vcpu) return ret; } +/** + * kvm_set_routing_entry: populate a kvm routing entry + * from a user routing entry + * + * @kvm: the VM this entry is applied to + * @e: kvm kernel routing entry handle + * @ue: user api routing entry handle + * return 0 on success, -EINVAL on errors. + */ +int kvm_set_routing_entry(struct kvm *kvm, + struct kvm_kernel_irq_routing_entry *e, + const struct kvm_irq_routing_entry *ue) +{ + int r = -EINVAL; + + switch (ue->type) { + case KVM_IRQ_ROUTING_IRQCHIP: + e->set = kvm->arch.irqchip_flow.irqchip_irqfd_set_irq; + e->irqchip.irqchip = ue->u.irqchip.irqchip; + e->irqchip.pin = ue->u.irqchip.pin; + if ((e->irqchip.pin >= KVM_IRQCHIP_NUM_PINS) || + (e->irqchip.irqchip >= KVM_NR_IRQCHIPS)) + goto out; + break; + case KVM_IRQ_ROUTING_MSI: + e->set = kvm->arch.irqchip_flow.irqchip_set_msi; + e->msi.address_lo = ue->u.msi.address_lo; + e->msi.address_hi = ue->u.msi.address_hi; + e->msi.data = ue->u.msi.data; + e->msi.flags = ue->flags; + e->msi.devid = ue->u.msi.devid; + break; + default: + goto out; + } + + if (!e->set) + goto out; + + r = 0; +out: + return r; +} + +int kvm_set_msi(struct kvm_kernel_irq_routing_entry *e, + struct kvm *kvm, int irq_source_id, + int level, bool line_status) +{ + if (!kvm->arch.irqchip_flow.irqchip_set_msi) + return -ENODEV; + return kvm->arch.irqchip_flow.irqchip_set_msi(e, kvm, irq_source_id, + level, line_status); +} + +int kvm_arch_set_irq_inatomic(struct kvm_kernel_irq_routing_entry *e, + struct kvm *kvm, int irq_source_id, int level, + bool line_status) +{ + if (!level || !irqchip_finalized(kvm) || + !kvm->arch.irqchip_flow.irqchip_set_irq_inatomic) + return -EWOULDBLOCK; + + return kvm->arch.irqchip_flow.irqchip_set_irq_inatomic(e, kvm, + irq_source_id, + level, + line_status); +} + /** * kvm_arch_vcpu_ioctl_run - the main VCPU run function to execute guest code * @vcpu: The VCPU pointer diff --git a/arch/arm64/kvm/vgic/vgic-init.c b/arch/arm64/kvm/vgic/vgic-init.c index 5f37d2362453..dddd231f134c 100644 --- a/arch/arm64/kvm/vgic/vgic-init.c +++ b/arch/arm64/kvm/vgic/vgic-init.c @@ -42,6 +42,9 @@ static struct kvm_irqchip_flow vgic_irqchip_flow = { .irqchip_map_phys_irq = kvm_vgic_map_phys_irq, .irqchip_unmap_phys_irq = kvm_vgic_unmap_phys_irq, .irqchip_set_owner = kvm_vgic_set_owner, + .irqchip_irqfd_set_irq = vgic_irqfd_set_irq, + .irqchip_set_msi = vgic_set_msi, + .irqchip_set_irq_inatomic = vgic_set_irq_inatomic, }; /* diff --git a/arch/arm64/kvm/vgic/vgic-irqfd.c b/arch/arm64/kvm/vgic/vgic-irqfd.c index e3c1197332b5..950684fb84e5 100644 --- a/arch/arm64/kvm/vgic/vgic-irqfd.c +++ b/arch/arm64/kvm/vgic/vgic-irqfd.c @@ -38,9 +38,9 @@ void kire_arch_cached_data_update(struct kvm *kvm, * * This is the entry point for irqfd IRQ injection */ -static int vgic_irqfd_set_irq(struct kvm_kernel_irq_routing_entry *e, - struct kvm *kvm, int irq_source_id, - int level, bool line_status) +int vgic_irqfd_set_irq(struct kvm_kernel_irq_routing_entry *e, + struct kvm *kvm, int irq_source_id, + int level, bool line_status) { unsigned int spi_id = e->irqchip.pin + VGIC_NR_PRIVATE_IRQS; @@ -49,46 +49,6 @@ static int vgic_irqfd_set_irq(struct kvm_kernel_irq_routing_entry *e, return kvm_vgic_inject_irq(kvm, 0, spi_id, level, NULL); } -/** - * kvm_set_routing_entry: populate a kvm routing entry - * from a user routing entry - * - * @kvm: the VM this entry is applied to - * @e: kvm kernel routing entry handle - * @ue: user api routing entry handle - * return 0 on success, -EINVAL on errors. - */ -int kvm_set_routing_entry(struct kvm *kvm, - struct kvm_kernel_irq_routing_entry *e, - const struct kvm_irq_routing_entry *ue) -{ - int r = -EINVAL; - - switch (ue->type) { - case KVM_IRQ_ROUTING_IRQCHIP: - e->set = vgic_irqfd_set_irq; - e->irqchip.irqchip = ue->u.irqchip.irqchip; - e->irqchip.pin = ue->u.irqchip.pin; - if ((e->irqchip.pin >= KVM_IRQCHIP_NUM_PINS) || - (e->irqchip.irqchip >= KVM_NR_IRQCHIPS)) - goto out; - break; - case KVM_IRQ_ROUTING_MSI: - e->set = kvm_set_msi; - e->msi.address_lo = ue->u.msi.address_lo; - e->msi.address_hi = ue->u.msi.address_hi; - e->msi.data = ue->u.msi.data; - e->msi.flags = ue->flags; - e->msi.devid = ue->u.msi.devid; - break; - default: - goto out; - } - r = 0; -out: - return r; -} - static void kvm_populate_msi(struct kvm_kernel_irq_routing_entry *e, struct kvm_msi *msi) { @@ -98,16 +58,17 @@ static void kvm_populate_msi(struct kvm_kernel_irq_routing_entry *e, msi->flags = e->msi.flags; msi->devid = e->msi.devid; } + /** - * kvm_set_msi: inject the MSI corresponding to the + * vgic_set_msi: inject the MSI corresponding to the * MSI routing entry * * This is the entry point for irqfd MSI injection * and userspace MSI injection. */ -int kvm_set_msi(struct kvm_kernel_irq_routing_entry *e, - struct kvm *kvm, int irq_source_id, - int level, bool line_status) +int vgic_set_msi(struct kvm_kernel_irq_routing_entry *e, + struct kvm *kvm, int irq_source_id, + int level, bool line_status) { struct kvm_msi msi; @@ -139,15 +100,12 @@ static int kvm_arch_set_irq_bypass(struct kvm_kernel_irq_routing_entry *e, #endif /** - * kvm_arch_set_irq_inatomic: fast-path for irqfd injection + * vgic_set_irq_inatomic: fast-path for irqfd injection */ -int kvm_arch_set_irq_inatomic(struct kvm_kernel_irq_routing_entry *e, - struct kvm *kvm, int irq_source_id, int level, - bool line_status) +int vgic_set_irq_inatomic(struct kvm_kernel_irq_routing_entry *e, + struct kvm *kvm, int irq_source_id, int level, + bool line_status) { - if (!level) - return -EWOULDBLOCK; - switch (e->type) { case KVM_IRQ_ROUTING_MSI: { struct kvm_msi msi; @@ -164,12 +122,6 @@ int kvm_arch_set_irq_inatomic(struct kvm_kernel_irq_routing_entry *e, } case KVM_IRQ_ROUTING_IRQCHIP: - /* - * Injecting SPIs is always possible in atomic context - * as long as the damn vgic is initialized. - */ - if (unlikely(!irqchip_finalized(kvm))) - break; return vgic_irqfd_set_irq(e, kvm, irq_source_id, 1, line_status); } diff --git a/arch/arm64/kvm/vgic/vgic.h b/arch/arm64/kvm/vgic/vgic.h index c759bef004ba..2793d88c2c22 100644 --- a/arch/arm64/kvm/vgic/vgic.h +++ b/arch/arm64/kvm/vgic/vgic.h @@ -359,6 +359,16 @@ void vgic_its_invalidate_cache(struct kvm *kvm); int vgic_its_inv_lpi(struct kvm *kvm, struct vgic_irq *irq); int vgic_its_invall(struct kvm_vcpu *vcpu); +int vgic_irqfd_set_irq(struct kvm_kernel_irq_routing_entry *e, + struct kvm *kvm, int irq_source_id, + int level, bool line_status); +int vgic_set_msi(struct kvm_kernel_irq_routing_entry *e, + struct kvm *kvm, int irq_source_id, + int level, bool line_status); +int vgic_set_irq_inatomic(struct kvm_kernel_irq_routing_entry *e, + struct kvm *kvm, int irq_source_id, int level, + bool line_status); + bool vgic_supports_direct_msis(struct kvm *kvm); int vgic_v4_init(struct kvm *kvm); void vgic_v4_teardown(struct kvm *kvm); -- 2.33.0
From: Marc Zyngier <maz@kernel.org> Although it is safe for now, do condition the returning of a msis_require_devid capability on the irqchip being a GICv3. Signed-off-by: Marc Zyngier <maz@kernel.org> Signed-off-by: wanghaibin <wanghaibin.wang@huawei.com> --- arch/arm64/kvm/arm.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/arch/arm64/kvm/arm.c b/arch/arm64/kvm/arm.c index 2e67b6353c06..5b207573fbfb 100644 --- a/arch/arm64/kvm/arm.c +++ b/arch/arm64/kvm/arm.c @@ -478,7 +478,8 @@ int kvm_vm_ioctl_check_extension(struct kvm *kvm, long ext) if (!kvm) r = -EINVAL; else - r = kvm->arch.vgic.msis_require_devid; + r = (irqchip_is_gic_v3(kvm) && + kvm->arch.vgic.msis_require_devid); break; case KVM_CAP_ARM_USER_IRQ: /* -- 2.33.0
From: wanghaibin <wanghaibin.wang@huawei.com> The rVIC (reduced Virtual Interrupt Controller), and its rVID (reduced Virtual Interrupt Distributor) companion are the two parts of a PV interrupt controller architecture, aiming at supporting VMs with minimal interrupt requirements. Signed-off-by: Marc Zyngier <maz@kernel.org> Signed-off-by: wanghaibin <wanghaibin.wang@huawei.com> --- arch/arm64/include/asm/kvm_host.h | 7 +- arch/arm64/include/asm/kvm_irq.h | 2 + arch/arm64/include/uapi/asm/kvm.h | 10 + arch/arm64/kvm/Makefile | 2 +- arch/arm64/kvm/arm.c | 3 + arch/arm64/kvm/hypercalls.c | 13 + arch/arm64/kvm/rvic-cpu.c | 1077 +++++++++++++++++++++++++++++ include/kvm/arm_rvic.h | 41 ++ include/linux/irqchip/irq-rvic.h | 4 + include/uapi/linux/kvm.h | 3 +- 10 files changed, 1159 insertions(+), 3 deletions(-) create mode 100644 arch/arm64/kvm/rvic-cpu.c create mode 100644 include/kvm/arm_rvic.h diff --git a/arch/arm64/include/asm/kvm_host.h b/arch/arm64/include/asm/kvm_host.h index 9329163ffc93..ad7966d3c0b3 100644 --- a/arch/arm64/include/asm/kvm_host.h +++ b/arch/arm64/include/asm/kvm_host.h @@ -40,6 +40,7 @@ #include <kvm/arm_vgic.h> #include <kvm/arm_arch_timer.h> #include <kvm/arm_pmu.h> +#include <kvm/arm_rvic.h> #define KVM_MAX_VCPUS VGIC_V3_MAX_CPUS @@ -221,6 +222,7 @@ struct kvm_arch { enum kvm_irqchip_type irqchip_type; bool irqchip_finalized; struct kvm_irqchip_flow irqchip_flow; + void *irqchip_data; struct vgic_dist vgic; /* Timers */ @@ -617,7 +619,10 @@ struct kvm_vcpu_arch { struct task_struct *parent_task; /* VGIC state */ - struct vgic_cpu vgic_cpu; + union { + struct vgic_cpu vgic_cpu; + struct rvic rvic; + }; struct arch_timer_cpu timer_cpu; struct kvm_pmu pmu; diff --git a/arch/arm64/include/asm/kvm_irq.h b/arch/arm64/include/asm/kvm_irq.h index 8676c68085a6..d0b3a4168efe 100644 --- a/arch/arm64/include/asm/kvm_irq.h +++ b/arch/arm64/include/asm/kvm_irq.h @@ -12,11 +12,13 @@ enum kvm_irqchip_type { IRQCHIP_USER, /* Implemented in userspace */ IRQCHIP_GICv2, /* v2 on v2, or v2 on v3 */ IRQCHIP_GICv3, /* v3 on v3 */ + IRQCHIP_RVIC, /* PV irqchip */ }; #define irqchip_in_kernel(k) ((k)->arch.irqchip_type != IRQCHIP_USER) #define irqchip_is_gic_v2(k) ((k)->arch.irqchip_type == IRQCHIP_GICv2) #define irqchip_is_gic_v3(k) ((k)->arch.irqchip_type == IRQCHIP_GICv3) +#define irqchip_is_rvic(k) ((k)->arch.irqchip_type == IRQCHIP_RVIC) #define irqchip_finalized(k) ((k)->arch.irqchip_finalized) diff --git a/arch/arm64/include/uapi/asm/kvm.h b/arch/arm64/include/uapi/asm/kvm.h index 99660288bd94..635d5f2fb796 100644 --- a/arch/arm64/include/uapi/asm/kvm.h +++ b/arch/arm64/include/uapi/asm/kvm.h @@ -383,6 +383,7 @@ enum { * current sequence, add in sequence. */ KVM_REG_ARM_VENDOR_HYP_BIT_IPIV = 2, + KVM_REG_ARM_VENDOR_HYP_BIT_RVIC = 3, #ifdef __KERNEL__ KVM_REG_ARM_VENDOR_HYP_BMAP_BIT_COUNT, #endif @@ -498,6 +499,15 @@ struct arm_rme_init_ripas { #define KVM_ARM_VCPU_PVTIME_CTRL 2 #define KVM_ARM_VCPU_PVTIME_IPA 0 +/* + * Device Control API: ARM RVIC. We only use the group, not the group + * attributes. They must be set to 0 for now. + */ +#define KVM_DEV_ARM_RVIC_GRP_NR_IRQS 0 +#define KVM_DEV_ARM_RVIC_GRP_NR_TRUSTED_MASK 0xffff +#define KVM_DEV_ARM_RVIC_GRP_NR_TOTAL_MASK (0xffff << 16) +#define KVM_DEV_ARM_RVIC_GRP_INIT 1 + /* KVM_IRQ_LINE irq field index values */ #define KVM_ARM_IRQ_VCPU2_SHIFT 28 #define KVM_ARM_IRQ_VCPU2_MASK 0xf diff --git a/arch/arm64/kvm/Makefile b/arch/arm64/kvm/Makefile index f48a34d5b0bc..a69301906b43 100644 --- a/arch/arm64/kvm/Makefile +++ b/arch/arm64/kvm/Makefile @@ -14,7 +14,7 @@ kvm-y += arm.o mmu.o mmio.o psci.o hypercalls.o pvtime.o pvsched.o \ inject_fault.o va_layout.o handle_exit.o \ guest.o debug.o reset.o sys_regs.o stacktrace.o \ vgic-sys-reg-v3.o fpsimd.o pkvm.o \ - arch_timer.o trng.o vmid.o emulate-nested.o nested.o \ + arch_timer.o rvic-cpu.o trng.o vmid.o emulate-nested.o nested.o \ vgic/vgic.o vgic/vgic-init.o \ vgic/vgic-irqfd.o vgic/vgic-v2.o \ vgic/vgic-v3.o vgic/vgic-v4.o \ diff --git a/arch/arm64/kvm/arm.c b/arch/arm64/kvm/arm.c index 5b207573fbfb..4abfd7a9a2c2 100644 --- a/arch/arm64/kvm/arm.c +++ b/arch/arm64/kvm/arm.c @@ -46,6 +46,7 @@ #include <kvm/arm_hypercalls.h> #include <kvm/arm_pmu.h> #include <kvm/arm_psci.h> +#include <kvm/arm_rvic.h> static enum kvm_mode kvm_mode = KVM_MODE_DEFAULT; @@ -2592,6 +2593,8 @@ static int __init init_subsystems(void) switch (err) { case 0: vgic_present = true; + if (kvm_register_rvic_device()) + kvm_err("Failed to register rvic device type\n"); break; case -ENODEV: case -ENXIO: diff --git a/arch/arm64/kvm/hypercalls.c b/arch/arm64/kvm/hypercalls.c index e71fed7f7135..f1b1e0db273e 100644 --- a/arch/arm64/kvm/hypercalls.c +++ b/arch/arm64/kvm/hypercalls.c @@ -8,6 +8,9 @@ #include <kvm/arm_hypercalls.h> #include <kvm/arm_psci.h> +#include <kvm/arm_rvic.h> + +#include <linux/irqchip/irq-rvic.h> #ifdef CONFIG_ARM64_HISI_IPIV #include "hisilicon/hisi_virt.h" @@ -122,6 +125,12 @@ static bool kvm_smccc_test_fw_bmap(struct kvm_vcpu *vcpu, u32 func_id) case ARM_SMCCC_VENDOR_HYP_KVM_PTP_FUNC_ID: return test_bit(KVM_REG_ARM_VENDOR_HYP_BIT_PTP, &smccc_feat->vendor_hyp_bmap); + case SMC64_RVIC_BASE ... SMC64_RVIC_LAST: + return test_bit(KVM_REG_ARM_VENDOR_HYP_BIT_RVIC, + &smccc_feat->vendor_hyp_bmap); + case SMC64_RVID_BASE ... SMC64_RVID_LAST: + return test_bit(KVM_REG_ARM_VENDOR_HYP_BIT_RVIC, + &smccc_feat->vendor_hyp_bmap); #ifdef CONFIG_ARM64_HISI_IPIV case ARM_SMCCC_VENDOR_PV_SGI_FEATURES: case ARM_SMCCC_VENDOR_PV_SGI_ENABLE: @@ -411,6 +420,10 @@ int kvm_smccc_call_handler(struct kvm_vcpu *vcpu) case ARM_SMCCC_TRNG_RND32: case ARM_SMCCC_TRNG_RND64: return kvm_trng_call(vcpu); + case SMC64_RVIC_BASE ... SMC64_RVIC_LAST: + return kvm_rvic_handle_hcall(vcpu); + case SMC64_RVID_BASE ... SMC64_RVID_LAST: + return kvm_rvid_handle_hcall(vcpu); default: return kvm_psci_call(vcpu); } diff --git a/arch/arm64/kvm/rvic-cpu.c b/arch/arm64/kvm/rvic-cpu.c new file mode 100644 index 000000000000..f951ab381fbd --- /dev/null +++ b/arch/arm64/kvm/rvic-cpu.c @@ -0,0 +1,1077 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * rVIC/rVID PV interrupt controller implementation for KVM/arm64. + * + * Copyright 2020 Google LLC. + * Author: Marc Zyngier <maz@kernel.org> + */ + +#include <linux/kernel.h> +#include <linux/kvm_host.h> +#include <linux/list.h> +#include <linux/spinlock.h> + +#include <kvm/arm_hypercalls.h> +#include <kvm/arm_rvic.h> + +#include <linux/irqchip/irq-rvic.h> + +/* FIXME: lock/unlock_all_vcpus */ +#include "vgic/vgic.h" + +#include <kvm/arm_vgic.h> /* fix */ + +#define kvm_vcpu_to_rvic(v) (&(v)->arch.rvic) +#define kvm_rvic_to_vcpu(r) (container_of((r), struct kvm_vcpu, arch.rvic)) + +#define rvic_nr_untrusted(r) ((r)->nr_total - (r)->nr_trusted) + +struct rvic_vm_data { + u16 nr_trusted; + u16 nr_total; + spinlock_t lock; + /* Map is a dynamically allocated array of (total-trusted) elements */ + struct { + u16 target_vcpu; + u16 intid; + } rvid_map[]; +}; + +/* + * rvic_irq state machine: + * + * idle <- S/C -> pending + * ^ / ^ + * | / | + * U/M A U/M + * | / | + * v v V + * masked <- S/C -> masked+pending + * + * [S]: Set Pending, [C]: Clear Pending + * [U]: Unmask, [M]: Mask + * [A]: Ack + */ + +static struct rvic_irq *rvic_get_irq(struct rvic *rvic, unsigned int intid) +{ + if (intid >= rvic->nr_total) + return NULL; + return &rvic->irqs[intid]; +} + +static bool rvic_irq_queued(struct rvic_irq *irq) +{ + return !list_empty(&irq->delivery_entry); +} + +/* RVIC primitives. They all imply that the RVIC lock is held */ +static void __rvic_enable(struct rvic *rvic) +{ + rvic->enabled = true; +} + +static void __rvic_disable(struct rvic *rvic) +{ + rvic->enabled = false; +} + +static bool __rvic_is_enabled(struct rvic *rvic) +{ + return rvic->enabled; +} + +static void __rvic_set_pending(struct rvic *rvic, unsigned int intid) +{ + struct rvic_irq *irq = rvic_get_irq(rvic, intid); + unsigned long flags; + + if (!__rvic_is_enabled(rvic)) { + pr_debug("dropping intid %u\n", intid); + return; + } + + spin_lock_irqsave(&irq->lock, flags); + + irq->pending = true; + if (!irq->masked && !rvic_irq_queued(irq)) + list_add_tail(&irq->delivery_entry, &rvic->delivery); + + spin_unlock_irqrestore(&irq->lock, flags); +} + +static void __rvic_clear_pending(struct rvic *rvic, unsigned int intid) +{ + struct rvic_irq *irq = rvic_get_irq(rvic, intid); + unsigned long flags; + + spin_lock_irqsave(&irq->lock, flags); + + irq->pending = false; + list_del_init(&irq->delivery_entry); + + spin_unlock_irqrestore(&irq->lock, flags); +} + +static bool __rvic_is_pending(struct rvic *rvic, unsigned int intid) +{ + struct rvic_irq *irq = rvic_get_irq(rvic, intid); + unsigned long flags; + bool pend; + + spin_lock_irqsave(&irq->lock, flags); + pend = irq->pending; + spin_unlock_irqrestore(&irq->lock, flags); + + return pend; +} + +static void __rvic_set_masked(struct rvic *rvic, unsigned int intid) +{ + struct rvic_irq *irq = rvic_get_irq(rvic, intid); + unsigned long flags; + + spin_lock_irqsave(&irq->lock, flags); + + irq->masked = true; + if (irq->pending) + list_del_init(&irq->delivery_entry); + + spin_unlock_irqrestore(&irq->lock, flags); +} + +static void __rvic_clear_masked(struct rvic *rvic, unsigned int intid) +{ + struct rvic_irq *irq = rvic_get_irq(rvic, intid); + unsigned long flags; + + spin_lock_irqsave(&irq->lock, flags); + + irq->masked = false; + if (__rvic_is_enabled(rvic) && irq->pending && !rvic_irq_queued(irq)) + list_add_tail(&irq->delivery_entry, &rvic->delivery); + + spin_unlock_irqrestore(&irq->lock, flags); +} + +static unsigned int __rvic_ack(struct rvic *rvic) +{ + unsigned int intid = ~0U; + struct rvic_irq *irq; + + if (!__rvic_is_enabled(rvic)) + return intid; + + irq = list_first_entry_or_null(&rvic->delivery, struct rvic_irq, + delivery_entry); + if (irq) { + intid = irq->intid; + __rvic_set_masked(rvic, intid); + __rvic_clear_pending(rvic, intid); + } + + return intid; +} + +static bool __rvic_can_signal(struct rvic *rvic) +{ + return __rvic_is_enabled(rvic) && !list_empty(&rvic->delivery); +} + +static void __rvic_resample(struct rvic *rvic, unsigned int intid) +{ + struct rvic_irq *irq = rvic_get_irq(rvic, intid); + unsigned long flags; + bool pending; + + spin_lock_irqsave(&irq->lock, flags); + if (irq->get_line_level) { + pending = irq->get_line_level(irq->intid); + + /* + * As part of the resampling, tickle the GIC so that + * new interrupts can trickle in. + */ + if (!pending && irq->host_irq) + irq_set_irqchip_state(irq->host_irq, + IRQCHIP_STATE_ACTIVE, false); + } else { + pending = irq->line_level; + } + + spin_unlock_irqrestore(&irq->lock, flags); + + if (pending) + __rvic_set_pending(rvic, intid); +} + +/* + * rVIC hypercall handling. All functions assume they are being called + * from the vcpu thread that triggers the hypercall. + */ +static void __rvic_kick_vcpu(struct kvm_vcpu *vcpu) +{ + kvm_make_request(KVM_REQ_IRQ_PENDING, vcpu); + kvm_vcpu_kick(vcpu); +} + +static void __rvic_sync_hcr(struct kvm_vcpu *vcpu, struct rvic *rvic, + bool was_signaling) +{ + struct kvm_vcpu *target = kvm_rvic_to_vcpu(rvic); + bool signal = __rvic_can_signal(rvic); + + /* We're hitting our own rVIC: update HCR_VI locally */ + if (vcpu == target) { + if (signal) + *vcpu_hcr(vcpu) |= HCR_VI; + else + *vcpu_hcr(vcpu) &= ~HCR_VI; + + return; + } + + /* + * Remote rVIC case: + * + * We kick even if the interrupt disappears, as ISR_EL1.I must + * always reflect the state of the rVIC. This forces a reload + * of the vcpu state, making it consistent. + * + * This avoids modifying the target's own copy of HCR_EL2, as + * we are in a cross-vcpu call, and changing it from under its + * feet is dodgy. + */ + if (was_signaling != signal) + __rvic_kick_vcpu(target); +} + +static void rvic_version(struct kvm_vcpu *vcpu) +{ + /* ALP0.3 is the name of the game */ + smccc_set_retval(vcpu, RVIC_STATUS_SUCCESS, RVIC_VERSION(0, 3), 0, 0); +} + +static void rvic_info(struct kvm_vcpu *vcpu) +{ + struct rvic *rvic = kvm_vcpu_to_rvic(vcpu); + unsigned long what = smccc_get_arg1(vcpu); + unsigned long a0, a1; + + switch (what) { + case RVIC_INFO_KEY_NR_TRUSTED_INTERRUPTS: + a0 = RVIx_STATUS_PACK(RVIC_STATUS_SUCCESS, 0); + a1 = rvic->nr_trusted; + break; + case RVIC_INFO_KEY_NR_UNTRUSTED_INTERRUPTS: + a0 = RVIx_STATUS_PACK(RVIC_STATUS_SUCCESS, 0); + a1 = rvic_nr_untrusted(rvic); + break; + default: + a0 = RVIx_STATUS_PACK(RVIC_STATUS_ERROR_PARAMETER, 0); + a1 = 0; + break; + } + + smccc_set_retval(vcpu, a0, a1, 0, 0); +} + +static void rvic_enable(struct kvm_vcpu *vcpu) +{ + struct rvic *rvic = kvm_vcpu_to_rvic(vcpu); + unsigned long flags; + bool was_signaling; + + spin_lock_irqsave(&rvic->lock, flags); + + was_signaling = __rvic_can_signal(rvic); + __rvic_enable(rvic); + __rvic_sync_hcr(vcpu, rvic, was_signaling); + + spin_unlock_irqrestore(&rvic->lock, flags); + + smccc_set_retval(vcpu, RVIx_STATUS_PACK(RVIC_STATUS_SUCCESS, 0), + 0, 0, 0); +} + +static void rvic_disable(struct kvm_vcpu *vcpu) +{ + struct rvic *rvic = kvm_vcpu_to_rvic(vcpu); + unsigned long flags; + bool was_signaling; + + spin_lock_irqsave(&rvic->lock, flags); + + was_signaling = __rvic_can_signal(rvic); + __rvic_disable(rvic); + __rvic_sync_hcr(vcpu, rvic, was_signaling); + + spin_unlock_irqrestore(&rvic->lock, flags); + + smccc_set_retval(vcpu, RVIx_STATUS_PACK(RVIC_STATUS_SUCCESS, 0), + 0, 0, 0); +} + +typedef void (*rvic_action_fn_t)(struct rvic *, unsigned int); + +static int validate_rvic_call(struct kvm_vcpu *vcpu, struct rvic **rvicp, + unsigned int *intidp) +{ + unsigned long mpidr = smccc_get_arg1(vcpu); + unsigned int intid = smccc_get_arg2(vcpu); + struct kvm_vcpu *target; + struct rvic *rvic; + + /* FIXME: The spec distinguishes between invalid MPIDR and invalid CPU */ + + target = kvm_mpidr_to_vcpu(vcpu->kvm, mpidr); + if (!target) { + smccc_set_retval(vcpu, RVIx_STATUS_PACK(RVIC_STATUS_INVALID_CPU, 0), + 0, 0, 0); + return -1; + } + + rvic = kvm_vcpu_to_rvic(target); + if (intid >= rvic->nr_total) { + smccc_set_retval(vcpu, RVIx_STATUS_PACK(RVIC_STATUS_ERROR_PARAMETER, 1), + 0, 0, 0); + return -1; + } + + *rvicp = rvic; + *intidp = intid; + + return 0; +} + +static void __rvic_action(struct kvm_vcpu *vcpu, rvic_action_fn_t action, + bool check_enabled) +{ + struct rvic *rvic; + unsigned long a0; + unsigned long flags; + int intid; + + if (validate_rvic_call(vcpu, &rvic, &intid)) + return; + + spin_lock_irqsave(&rvic->lock, flags); + + if (unlikely(check_enabled && !__rvic_is_enabled(rvic))) { + a0 = RVIx_STATUS_PACK(RVIC_STATUS_DISABLED, 0); + } else { + bool was_signaling = __rvic_can_signal(rvic); + action(rvic, intid); + __rvic_sync_hcr(vcpu, rvic, was_signaling); + a0 = RVIx_STATUS_PACK(RVIC_STATUS_SUCCESS, 0); + } + + spin_unlock_irqrestore(&rvic->lock, flags); + + smccc_set_retval(vcpu, a0, 0, 0, 0); +} + +static void rvic_set_masked(struct kvm_vcpu *vcpu) +{ + __rvic_action(vcpu, __rvic_set_masked, false); +} + +static void rvic_clear_masked(struct kvm_vcpu *vcpu) +{ + __rvic_action(vcpu, __rvic_clear_masked, false); +} + +static void rvic_clear_pending(struct kvm_vcpu *vcpu) +{ + __rvic_action(vcpu, __rvic_clear_pending, false); +} + +static void rvic_signal(struct kvm_vcpu *vcpu) +{ + __rvic_action(vcpu, __rvic_set_pending, true); +} + +static void rvic_is_pending(struct kvm_vcpu *vcpu) +{ + unsigned long flags; + struct rvic *rvic; + int intid; + bool res; + + if (validate_rvic_call(vcpu, &rvic, &intid)) + return; + + spin_lock_irqsave(&rvic->lock, flags); + + res = __rvic_is_pending(rvic, intid); + + spin_unlock_irqrestore(&rvic->lock, flags); + + smccc_set_retval(vcpu, RVIx_STATUS_PACK(RVIC_STATUS_SUCCESS, 0), + res, 0, 0); +} + +/* + * Ack and Resample are the only "interesting" operations that are + * strictly per-CPU. + */ +static void rvic_acknowledge(struct kvm_vcpu *vcpu) +{ + unsigned long a0, a1; + unsigned long flags; + unsigned int intid; + struct rvic *rvic; + + rvic = kvm_vcpu_to_rvic(vcpu); + + spin_lock_irqsave(&rvic->lock, flags); + + if (unlikely(!__rvic_is_enabled(rvic))) { + a0 = RVIx_STATUS_PACK(RVIC_STATUS_DISABLED, 0); + a1 = 0; + } else { + intid = __rvic_ack(rvic); + __rvic_sync_hcr(vcpu, rvic, true); + if (unlikely(intid >= rvic->nr_total)) { + a0 = RVIx_STATUS_PACK(RVIC_STATUS_NO_INTERRUPTS, 0); + a1 = 0; + } else { + a0 = RVIx_STATUS_PACK(RVIC_STATUS_SUCCESS, 0); + a1 = intid; + } + } + + spin_unlock_irqrestore(&rvic->lock, flags); + + smccc_set_retval(vcpu, a0, a1, 0, 0); +} + +static void rvic_resample(struct kvm_vcpu *vcpu) +{ + unsigned int intid = smccc_get_arg1(vcpu); + unsigned long flags; + unsigned long a0; + struct rvic *rvic; + + rvic = kvm_vcpu_to_rvic(vcpu); + + spin_lock_irqsave(&rvic->lock, flags); + + if (unlikely(intid >= rvic->nr_trusted)) { + a0 = RVIx_STATUS_PACK(RVIC_STATUS_ERROR_PARAMETER, 0); + } else { + __rvic_resample(rvic, intid); + + /* + * Don't bother finding out if we were signalling, we + * will update HCR_EL2 anyway as we are guaranteed not + * to be in a cross-call. + */ + __rvic_sync_hcr(vcpu, rvic, true); + a0 = RVIx_STATUS_PACK(RVIC_STATUS_SUCCESS, 0); + } + + spin_unlock_irqrestore(&rvic->lock, flags); + + smccc_set_retval(vcpu, a0, 0, 0, 0); +} + +int kvm_rvic_handle_hcall(struct kvm_vcpu *vcpu) +{ + pr_debug("RVIC: HC %08x", (unsigned int)smccc_get_function(vcpu)); + switch (smccc_get_function(vcpu)) { + case SMC64_RVIC_VERSION: + rvic_version(vcpu); + break; + case SMC64_RVIC_INFO: + rvic_info(vcpu); + break; + case SMC64_RVIC_ENABLE: + rvic_enable(vcpu); + break; + case SMC64_RVIC_DISABLE: + rvic_disable(vcpu); + break; + case SMC64_RVIC_SET_MASKED: + rvic_set_masked(vcpu); + break; + case SMC64_RVIC_CLEAR_MASKED: + rvic_clear_masked(vcpu); + break; + case SMC64_RVIC_IS_PENDING: + rvic_is_pending(vcpu); + break; + case SMC64_RVIC_SIGNAL: + rvic_signal(vcpu); + break; + case SMC64_RVIC_CLEAR_PENDING: + rvic_clear_pending(vcpu); + break; + case SMC64_RVIC_ACKNOWLEDGE: + rvic_acknowledge(vcpu); + break; + case SMC64_RVIC_RESAMPLE: + rvic_resample(vcpu); + break; + default: + smccc_set_retval(vcpu, SMCCC_RET_NOT_SUPPORTED, 0, 0, 0); + break; + } + + return 1; +} + +static void rvid_version(struct kvm_vcpu *vcpu) +{ + /* ALP0.3 is the name of the game */ + smccc_set_retval(vcpu, RVID_STATUS_SUCCESS, RVID_VERSION(0, 3), 0, 0); +} + +static void rvid_map(struct kvm_vcpu *vcpu) +{ + unsigned long input = smccc_get_arg1(vcpu); + unsigned long mpidr = smccc_get_arg2(vcpu); + unsigned int intid = smccc_get_arg3(vcpu); + unsigned long flags; + struct rvic_vm_data *data; + struct kvm_vcpu *target; + + data = vcpu->kvm->arch.irqchip_data; + + if (input > rvic_nr_untrusted(data)) { + smccc_set_retval(vcpu, RVIx_STATUS_PACK(RVID_STATUS_ERROR_PARAMETER, 0), + 0, 0, 0); + return; + } + + /* FIXME: different error from RVIC. Why? */ + target = kvm_mpidr_to_vcpu(vcpu->kvm, mpidr); + if (!target) { + smccc_set_retval(vcpu, RVIx_STATUS_PACK(RVID_STATUS_ERROR_PARAMETER, 1), + 0, 0, 0); + return; + } + + if (intid < data->nr_trusted || intid >= data->nr_total) { + smccc_set_retval(vcpu, RVIx_STATUS_PACK(RVID_STATUS_ERROR_PARAMETER, 2), + 0, 0, 0); + return; + } + + spin_lock_irqsave(&data->lock, flags); + data->rvid_map[input].target_vcpu = target->vcpu_id; + data->rvid_map[input].intid = intid; + spin_unlock_irqrestore(&data->lock, flags); + + smccc_set_retval(vcpu, 0, 0, 0, 0); +} + +static void rvid_unmap(struct kvm_vcpu *vcpu) +{ + unsigned long input = smccc_get_arg1(vcpu); + unsigned long flags; + struct rvic_vm_data *data; + + data = vcpu->kvm->arch.irqchip_data; + + if (input > rvic_nr_untrusted(data)) { + smccc_set_retval(vcpu, RVIx_STATUS_PACK(RVID_STATUS_ERROR_PARAMETER, 0), + 0, 0, 0); + return; + } + + spin_lock_irqsave(&data->lock, flags); + data->rvid_map[input].target_vcpu = 0; + data->rvid_map[input].intid = 0; + spin_unlock_irqrestore(&data->lock, flags); + + smccc_set_retval(vcpu, 0, 0, 0, 0); +} + +int kvm_rvid_handle_hcall(struct kvm_vcpu *vcpu) +{ + pr_debug("RVID: HC %08x", (unsigned int)smccc_get_function(vcpu)); + switch (smccc_get_function(vcpu)) { + case SMC64_RVID_VERSION: + rvid_version(vcpu); + break; + case SMC64_RVID_MAP: + rvid_map(vcpu); + break; + case SMC64_RVID_UNMAP: + rvid_unmap(vcpu); + break; + default: + smccc_set_retval(vcpu, SMCCC_RET_NOT_SUPPORTED, 0, 0, 0); + break; + } + + return 1; +} + +/* + * KVM internal interface to the rVIC + */ + +/* This *must* be called from the vcpu thread */ +static void rvic_flush_signaling_state(struct kvm_vcpu *vcpu) +{ + struct rvic *rvic = kvm_vcpu_to_rvic(vcpu); + unsigned long flags; + + spin_lock_irqsave(&rvic->lock, flags); + + __rvic_sync_hcr(vcpu, rvic, true); + + spin_unlock_irqrestore(&rvic->lock, flags); +} + +/* This can be called from any context */ +static void rvic_vcpu_inject_irq(struct kvm_vcpu *vcpu, unsigned int intid, + bool level) +{ + struct rvic *rvic = kvm_vcpu_to_rvic(vcpu); + unsigned long flags; + bool prev; + + spin_lock_irqsave(&rvic->lock, flags); + + if (WARN_ON(intid >= rvic->nr_total)) + goto out; + + /* + * Although really ugly, this should be safe as we hold the + * rvic lock, and the only path that uses this information is + * resample, which takes this lock too. + */ + if (!rvic->irqs[intid].get_line_level) + rvic->irqs[intid].line_level = level; + + if (level) { + prev = __rvic_can_signal(rvic); + __rvic_set_pending(rvic, intid); + if (prev != __rvic_can_signal(rvic)) + __rvic_kick_vcpu(vcpu); + } +out: + spin_unlock_irqrestore(&rvic->lock, flags); +} + +static int rvic_inject_irq(struct kvm *kvm, unsigned int cpu, + unsigned int intid, bool level, void *owner) +{ + struct kvm_vcpu *vcpu = kvm_get_vcpu(kvm, cpu); + struct rvic *rvic; + + if (unlikely(!vcpu)) + return -EINVAL; + + rvic = kvm_vcpu_to_rvic(vcpu); + if (unlikely(intid >= rvic->nr_total)) + return -EINVAL; + + /* Ignore interrupt owner for now */ + rvic_vcpu_inject_irq(vcpu, intid, level); + return 0; +} + +static int rvic_inject_userspace_irq(struct kvm *kvm, unsigned int type, + unsigned int cpu, + unsigned int intid, bool level) +{ + struct rvic_vm_data *data = kvm->arch.irqchip_data; + unsigned long flags; + u16 output; + + switch (type) { + case KVM_ARM_IRQ_TYPE_SPI: + /* + * Userspace can only inject interrupts that are + * translated by the rvid, so the cpu parameter is + * irrelevant and we override it when resolving the + * translation. + */ + if (intid >= rvic_nr_untrusted(data)) + return -EINVAL; + + spin_lock_irqsave(&data->lock, flags); + output = data->rvid_map[intid].intid; + cpu = data->rvid_map[intid].target_vcpu; + spin_unlock_irqrestore(&data->lock, flags); + + /* Silently ignore unmapped interrupts */ + if (output < data->nr_trusted) + return 0; + + return rvic_inject_irq(kvm, cpu, output, level, NULL); + default: + return -EINVAL; + } +} + +static int rvic_vcpu_init(struct kvm_vcpu *vcpu) +{ + struct rvic_vm_data *data = vcpu->kvm->arch.irqchip_data; + struct rvic *rvic = kvm_vcpu_to_rvic(vcpu); + int i; + + /* irqchip not ready yet, we will come back later */ + if (!data) + return 0; + + if (WARN_ON(rvic->irqs)) + return -EINVAL; + + spin_lock_init(&rvic->lock); + INIT_LIST_HEAD(&rvic->delivery); + rvic->nr_trusted = data->nr_trusted; + rvic->nr_total = data->nr_total; + rvic->enabled = false; + + rvic->irqs = kcalloc(rvic->nr_total, sizeof(*rvic->irqs), GFP_ATOMIC); + if (!rvic->irqs) + return -ENOMEM; + + for (i = 0; i < rvic->nr_total; i++) { + struct rvic_irq *irq = &rvic->irqs[i]; + + spin_lock_init(&irq->lock); + INIT_LIST_HEAD(&irq->delivery_entry); + irq->get_line_level = NULL; + irq->intid = i; + irq->host_irq = 0; + irq->pending = false; + irq->masked = true; + irq->line_level = false; + } + + return 0; +} + +static void rvic_destroy(struct kvm *kvm) +{ + struct kvm_vcpu *vcpu; + unsigned long i; + + mutex_lock(&kvm->lock); + + kvm_for_each_vcpu(i, vcpu, kvm) { + struct rvic *rvic = kvm_vcpu_to_rvic(vcpu); + + INIT_LIST_HEAD(&rvic->delivery); + kfree(rvic->irqs); + rvic->irqs = NULL; + } + + mutex_unlock(&kvm->lock); +} + +static int rvic_pending_irq(struct kvm_vcpu *vcpu) +{ + struct rvic *rvic = kvm_vcpu_to_rvic(vcpu); + unsigned long flags; + bool res; + + spin_lock_irqsave(&rvic->lock, flags); + res = __rvic_can_signal(rvic); + spin_unlock_irqrestore(&rvic->lock, flags); + + return res; +} + +static int rvic_map_phys_irq(struct kvm_vcpu *vcpu, unsigned int host_irq, + u32 intid, struct irq_ops *irq_ops) //bool (*get_line_level)(int)) +{ + struct rvic *rvic = kvm_vcpu_to_rvic(vcpu); + struct rvic_irq *irq = rvic_get_irq(rvic, intid); + unsigned long flags; + + spin_lock_irqsave(&irq->lock, flags); + irq->host_irq = host_irq; + irq->get_line_level = irq_ops->get_input_level; + spin_unlock_irqrestore(&irq->lock, flags); + + return 0; +} + +static int rvic_unmap_phys_irq(struct kvm_vcpu *vcpu, unsigned int intid) +{ + struct rvic *rvic = kvm_vcpu_to_rvic(vcpu); + struct rvic_irq *irq = rvic_get_irq(rvic, intid); + unsigned long flags; + + spin_lock_irqsave(&irq->lock, flags); + irq->host_irq = 0; + irq->get_line_level = NULL; + spin_unlock_irqrestore(&irq->lock, flags); + + return 0; +} + +static int rvic_irqfd_set_irq(struct kvm_kernel_irq_routing_entry *e, + struct kvm *kvm, int irq_source_id, + int level, bool line_status) +{ + /* Abuse the userspace interface to perform the routing*/ + return rvic_inject_userspace_irq(kvm, KVM_ARM_IRQ_TYPE_SPI, 0, + e->irqchip.pin, level); +} + +static int rvic_set_msi(struct kvm_kernel_irq_routing_entry *e, + struct kvm *kvm, int irq_source_id, + int level, bool line_status) +{ + return -ENODEV; +} + +static int rvic_set_irq_inatomic(struct kvm_kernel_irq_routing_entry *e, + struct kvm *kvm, int irq_source_id, + int level, bool line_status) +{ + if (e->type != KVM_IRQ_ROUTING_IRQCHIP) + return -EWOULDBLOCK; + + return rvic_irqfd_set_irq(e, kvm, irq_source_id, level, line_status); +} + +static const struct kvm_irqchip_flow rvic_irqchip_flow = { + .irqchip_destroy = rvic_destroy, + .irqchip_vcpu_init = rvic_vcpu_init, + /* Nothing to do on block/unblock */ + /* Nothing to do on load/put */ + .irqchip_vcpu_pending_irq = rvic_pending_irq, + .irqchip_vcpu_flush_hwstate = rvic_flush_signaling_state, + /* Nothing tp do on sync_hwstate */ + .irqchip_inject_irq = rvic_inject_irq, + .irqchip_inject_userspace_irq = rvic_inject_userspace_irq, + /* No reset_mapped_irq as we allow spurious interrupts */ + .irqchip_map_phys_irq = rvic_map_phys_irq, + .irqchip_unmap_phys_irq = rvic_unmap_phys_irq, + .irqchip_irqfd_set_irq = rvic_irqfd_set_irq, + .irqchip_set_msi = rvic_set_msi, + .irqchip_set_irq_inatomic = rvic_set_irq_inatomic, +}; + +static int rvic_setup_default_irq_routing(struct kvm *kvm) +{ + struct rvic_vm_data *data = kvm->arch.irqchip_data; + unsigned int nr = rvic_nr_untrusted(data); + struct kvm_irq_routing_entry *entries; + int i, ret; + + entries = kcalloc(nr, sizeof(*entries), GFP_KERNEL); + if (!entries) + return -ENOMEM; + + for (i = 0; i < nr; i++) { + entries[i].gsi = i; + entries[i].type = KVM_IRQ_ROUTING_IRQCHIP; + entries[i].u.irqchip.irqchip = 0; + entries[i].u.irqchip.pin = i; + } + ret = kvm_set_irq_routing(kvm, entries, nr, 0); + kfree(entries); + return ret; +} + +/* Device management */ +static int rvic_device_create(struct kvm_device *dev, u32 type) +{ + struct kvm *kvm = dev->kvm; + struct kvm_vcpu *vcpu; + int ret; + unsigned long i; + + if (irqchip_in_kernel(kvm)) + return -EEXIST; + + ret = -EBUSY; + if (!lock_all_vcpus(kvm)) + return ret; + + kvm_for_each_vcpu(i, vcpu, kvm) { + if (vcpu_has_run_once(vcpu)) + goto out_unlock; + } + + ret = 0; + + /* + * The good thing about not having any HW is that you don't + * get the limitations of the HW... + */ + kvm->max_vcpus = KVM_MAX_VCPUS; + kvm->arch.irqchip_type = IRQCHIP_RVIC; + kvm->arch.irqchip_flow = rvic_irqchip_flow; + kvm->arch.irqchip_data = NULL; + +out_unlock: + unlock_all_vcpus(kvm); + return ret; +} + +static void rvic_device_destroy(struct kvm_device *dev) +{ + kfree(dev->kvm->arch.irqchip_data); + kfree(dev); +} + +static int rvic_set_attr(struct kvm_device *dev, struct kvm_device_attr *attr) +{ + struct rvic_vm_data *data; + struct kvm_vcpu *vcpu; + u32 __user *uaddr, val; + u16 trusted, total; + int ret = -ENXIO; + unsigned long i; + + mutex_lock(&dev->kvm->lock); + + switch (attr->group) { + case KVM_DEV_ARM_RVIC_GRP_NR_IRQS: + if (attr->attr) + break; + + if (dev->kvm->arch.irqchip_data) { + ret = -EBUSY; + break; + } + + uaddr = (u32 __user *)(uintptr_t)attr->addr; + if (get_user(val, uaddr)) { + ret = -EFAULT; + break; + } + + trusted = FIELD_GET(KVM_DEV_ARM_RVIC_GRP_NR_TRUSTED_MASK, val); + total = FIELD_GET(KVM_DEV_ARM_RVIC_GRP_NR_TOTAL_MASK, val); + if (total < trusted || trusted < 32 || total < 64 || + trusted % 32 || total % 32 || total > 2048) { + ret = -EINVAL; + break; + } + + data = kzalloc(struct_size(data, rvid_map, (total - trusted)), + GFP_KERNEL); + if (!data) { + ret = -ENOMEM; + break; + } + + data->nr_trusted = trusted; + data->nr_total = total; + spin_lock_init(&data->lock); + /* Default to no mapping */ + for (i = 0; i < (total - trusted); i++) { + /* + * an intid < nr_trusted is invalid as the + * result of a translation through the rvid, + * hence the input in unmapped. + */ + data->rvid_map[i].target_vcpu = 0; + data->rvid_map[i].intid = 0; + } + + dev->kvm->arch.irqchip_data = data; + + ret = 0; + break; + + case KVM_DEV_ARM_RVIC_GRP_INIT: + if (attr->attr) + break; + + if (!dev->kvm->arch.irqchip_data) + break; + + ret = 0; + + /* Init the rvic on any already created vcpu */ + kvm_for_each_vcpu(i, vcpu, dev->kvm) { + ret = rvic_vcpu_init(vcpu); + if (ret) + break; + } + + if (!ret) + ret = rvic_setup_default_irq_routing(dev->kvm); + if (!ret) + dev->kvm->arch.irqchip_finalized = true; + break; + + default: + break; + } + + mutex_unlock(&dev->kvm->lock); + + return ret; +} + +static int rvic_get_attr(struct kvm_device *dev, struct kvm_device_attr *attr) +{ + struct rvic_vm_data *data; + u32 __user *uaddr, val; + int ret = -ENXIO; + + mutex_lock(&dev->kvm->lock); + + switch (attr->group) { + case KVM_DEV_ARM_RVIC_GRP_NR_IRQS: + if (attr->attr) + break; + + data = dev->kvm->arch.irqchip_data; + if (!data) + break; + + val = FIELD_PREP(KVM_DEV_ARM_RVIC_GRP_NR_TRUSTED_MASK, + data->nr_trusted); + val |= FIELD_PREP(KVM_DEV_ARM_RVIC_GRP_NR_TOTAL_MASK, + data->nr_total); + + uaddr = (u32 __user *)(uintptr_t)attr->addr; + ret = put_user(val, uaddr); + break; + + default: + break; + } + + mutex_unlock(&dev->kvm->lock); + + return ret; +} + +static int rvic_has_attr(struct kvm_device *dev, struct kvm_device_attr *attr) +{ + int ret = -ENXIO; + + switch (attr->group) { + case KVM_DEV_ARM_RVIC_GRP_NR_IRQS: + case KVM_DEV_ARM_RVIC_GRP_INIT: + if (attr->attr) + break; + ret = 0; + break; + + default: + break; + } + + return ret; +} + +static const struct kvm_device_ops rvic_dev_ops = { + .name = "kvm-arm-rvic", + .create = rvic_device_create, + .destroy = rvic_device_destroy, + .set_attr = rvic_set_attr, + .get_attr = rvic_get_attr, + .has_attr = rvic_has_attr, +}; + +int kvm_register_rvic_device(void) +{ + return kvm_register_device_ops(&rvic_dev_ops, KVM_DEV_TYPE_ARM_RVIC); +} diff --git a/include/kvm/arm_rvic.h b/include/kvm/arm_rvic.h new file mode 100644 index 000000000000..9e67a83fa384 --- /dev/null +++ b/include/kvm/arm_rvic.h @@ -0,0 +1,41 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * rVIC/rVID PV interrupt controller implementation for KVM/arm64. + * + * Copyright 2020 Google LLC. + * Author: Marc Zyngier <maz@kernel.org> + */ + +#ifndef __KVM_ARM_RVIC_H__ +#define __KVM_ARM_RVIC_H__ + +#include <linux/list.h> +#include <linux/spinlock.h> + +struct kvm_vcpu; + +struct rvic_irq { + spinlock_t lock; + struct list_head delivery_entry; + bool (*get_line_level)(int intid); + unsigned int intid; + unsigned int host_irq; + bool pending; + bool masked; + bool line_level; /* If get_line_level == NULL */ +}; + +struct rvic { + spinlock_t lock; + struct list_head delivery; + struct rvic_irq *irqs; + unsigned int nr_trusted; + unsigned int nr_total; + bool enabled; +}; + +int kvm_rvic_handle_hcall(struct kvm_vcpu *vcpu); +int kvm_rvid_handle_hcall(struct kvm_vcpu *vcpu); +int kvm_register_rvic_device(void); + +#endif diff --git a/include/linux/irqchip/irq-rvic.h b/include/linux/irqchip/irq-rvic.h index 4545c1e89741..b188773729fb 100644 --- a/include/linux/irqchip/irq-rvic.h +++ b/include/linux/irqchip/irq-rvic.h @@ -57,6 +57,8 @@ #define SMC64_RVIC_ACKNOWLEDGE SMC64_RVIC_FN(9) #define SMC64_RVIC_RESAMPLE SMC64_RVIC_FN(10) +#define SMC64_RVIC_LAST SMC64_RVIC_RESAMPLE + #define RVIC_INFO_KEY_NR_TRUSTED_INTERRUPTS 0 #define RVIC_INFO_KEY_NR_UNTRUSTED_INTERRUPTS 1 @@ -82,6 +84,8 @@ #define SMC64_RVID_MAP SMC64_RVID_FN(1) #define SMC64_RVID_UNMAP SMC64_RVID_FN(2) +#define SMC64_RVID_LAST SMC64_RVID_UNMAP + #define RVID_VERSION(M, m) RVIx_VERSION((M), (m)) #define RVID_VERSION_MAJOR(v) RVIx_VERSION_MAJOR((v)) diff --git a/include/uapi/linux/kvm.h b/include/uapi/linux/kvm.h index 0036cfaf5d69..2cffa93309e9 100644 --- a/include/uapi/linux/kvm.h +++ b/include/uapi/linux/kvm.h @@ -1505,7 +1505,8 @@ enum kvm_device_type { #define KVM_DEV_TYPE_LOONGARCH_EIOINTC KVM_DEV_TYPE_LOONGARCH_EIOINTC KVM_DEV_TYPE_LOONGARCH_PCHPIC, #define KVM_DEV_TYPE_LOONGARCH_PCHPIC KVM_DEV_TYPE_LOONGARCH_PCHPIC - + KVM_DEV_TYPE_ARM_RVIC, +#define KVM_DEV_TYPE_ARM_RVIC KVM_DEV_TYPE_ARM_RVIC KVM_DEV_TYPE_MAX, }; -- 2.33.0
From: Marc Zyngier <maz@kernel.org> It turns out that having these debugfs information is really useful when trying to understand what is going wrong in a guest, or even in the host kernel... Signed-off-by: Marc Zyngier <maz@kernel.org> Signed-off-by: wanghaibin <wanghaibin.wang@huawei.com> --- arch/arm64/kvm/rvic-cpu.c | 140 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 140 insertions(+) diff --git a/arch/arm64/kvm/rvic-cpu.c b/arch/arm64/kvm/rvic-cpu.c index f951ab381fbd..6386dd3a4bbb 100644 --- a/arch/arm64/kvm/rvic-cpu.c +++ b/arch/arm64/kvm/rvic-cpu.c @@ -6,6 +6,7 @@ * Author: Marc Zyngier <maz@kernel.org> */ +#include <linux/debugfs.h> #include <linux/kernel.h> #include <linux/kvm_host.h> #include <linux/list.h> @@ -709,6 +710,8 @@ static int rvic_inject_userspace_irq(struct kvm *kvm, unsigned int type, } } +static void rvic_create_debugfs(struct kvm_vcpu *vcpu); + static int rvic_vcpu_init(struct kvm_vcpu *vcpu) { struct rvic_vm_data *data = vcpu->kvm->arch.irqchip_data; @@ -745,6 +748,8 @@ static int rvic_vcpu_init(struct kvm_vcpu *vcpu) irq->line_level = false; } + rvic_create_debugfs(vcpu); + return 0; } @@ -916,6 +921,8 @@ static void rvic_device_destroy(struct kvm_device *dev) kfree(dev); } +static void rvid_create_debugfs(struct kvm *kvm); + static int rvic_set_attr(struct kvm_device *dev, struct kvm_device_attr *attr) { struct rvic_vm_data *data; @@ -973,6 +980,7 @@ static int rvic_set_attr(struct kvm_device *dev, struct kvm_device_attr *attr) } dev->kvm->arch.irqchip_data = data; + rvid_create_debugfs(dev->kvm); ret = 0; break; @@ -1075,3 +1083,135 @@ int kvm_register_rvic_device(void) { return kvm_register_device_ops(&rvic_dev_ops, KVM_DEV_TYPE_ARM_RVIC); } + +static void rvic_irq_debug_show_one(struct seq_file *s, struct rvic_irq *irq) +{ + unsigned long flags; + + spin_lock_irqsave(&irq->lock, flags); + + seq_printf(s, "%d: [%d] %c %c %ps %c %c\n", + irq->intid, irq->host_irq, + irq->pending ? 'P' : 'p', + irq->masked ? 'M' : 'm', + irq->get_line_level, + irq->get_line_level ? 'x' : (irq->line_level ? 'H' : 'L'), + rvic_irq_queued(irq) ? 'Q' : 'i'); + + spin_unlock_irqrestore(&irq->lock, flags); +} + +static int rvic_irq_debug_show(struct seq_file *s, void *p) +{ + rvic_irq_debug_show_one(s, s->private); + return 0; +} + +static int rvic_irq_debug_open(struct inode *inode, struct file *file) +{ + return single_open(file, rvic_irq_debug_show, inode->i_private); +} + +static const struct file_operations rvic_irq_debug_fops = { + .open = rvic_irq_debug_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + +static int rvic_debug_show(struct seq_file *s, void *p) +{ + struct kvm_vcpu *vcpu = s->private; + struct rvic *rvic = kvm_vcpu_to_rvic(vcpu); + struct rvic_irq *irq; + unsigned long flags; + + spin_lock_irqsave(&rvic->lock, flags); + + seq_printf(s, "%s\n", rvic->enabled ? "Enabled" : "Disabled"); + seq_printf(s, "%d Trusted\n", rvic->nr_trusted); + seq_printf(s, "%d Total\n", rvic->nr_total); + list_for_each_entry(irq, &rvic->delivery, delivery_entry) + rvic_irq_debug_show_one(s, irq); + + spin_unlock_irqrestore(&rvic->lock, flags); + + return 0; +} + +static int rvic_debug_open(struct inode *inode, struct file *file) +{ + return single_open(file, rvic_debug_show, inode->i_private); +} + +static const struct file_operations rvic_debug_fops = { + .open = rvic_debug_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + +static void rvic_create_debugfs(struct kvm_vcpu *vcpu) +{ + struct rvic *rvic = kvm_vcpu_to_rvic(vcpu); + struct dentry *rvic_root; + char dname[128]; + int i; + + snprintf(dname, sizeof(dname), "rvic-%d", vcpu->vcpu_id); + rvic_root = debugfs_create_dir(dname, vcpu->kvm->debugfs_dentry); + if (!rvic_root) + return; + + debugfs_create_file("state", 0444, rvic_root, vcpu, &rvic_debug_fops); + for (i = 0; i < rvic->nr_total; i++) { + snprintf(dname, sizeof(dname), "%d", i); + debugfs_create_file(dname, 0444, rvic_root, + rvic_get_irq(rvic, i), + &rvic_irq_debug_fops); + } +} + +static int rvid_debug_show(struct seq_file *s, void *p) +{ + struct kvm *kvm = s->private; + struct rvic_vm_data *data = kvm->arch.irqchip_data; + unsigned long flags; + int i; + + spin_lock_irqsave(&data->lock, flags); + + seq_printf(s, "%d Trusted\n", data->nr_trusted); + seq_printf(s, "%d Total\n", data->nr_total); + + for (i = 0; i < rvic_nr_untrusted(data); i++) { + if (data->rvid_map[i].intid < data->nr_trusted) + continue; + + seq_printf(s, "%4u: vcpu-%u %u\n", + i, data->rvid_map[i].target_vcpu, + data->rvid_map[i].intid); + } + + spin_unlock_irqrestore(&data->lock, flags); + + return 0; +} + +static int rvid_debug_open(struct inode *inode, struct file *file) +{ + return single_open(file, rvid_debug_show, inode->i_private); +} + +static const struct file_operations rvid_debug_fops = { + .open = rvid_debug_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + +static void rvid_create_debugfs(struct kvm *kvm) +{ + debugfs_create_file("rvid", 0444, kvm->debugfs_dentry, + kvm, &rvid_debug_fops); +} -- 2.33.0
participants (1)
-
Kunkun Jiang