This is required for ARM GICv5 support. At this stage, only PPIs are supported as the creating of the IRS and ITS are not imlemented in KVM. This change needs to be refreshed once the GICv5 KVM support has made it into Linux. For now, it is based on WIP changes. Change generated using util/update_headers.sh. Signed-off-by: Sascha Bischoff --- arm64/include/asm/kvm.h | 3 ++- include/linux/kvm.h | 18 ++++++++++++++++++ include/linux/virtio_ids.h | 1 + include/linux/virtio_net.h | 36 +++++++++++++++++++++++++++++++++++- include/linux/virtio_pci.h | 2 +- powerpc/include/asm/kvm.h | 13 ------------- riscv/include/asm/kvm.h | 27 ++++++++++++++++++++++++++- x86/include/asm/kvm.h | 35 +++++++++++++++++++++++++++++++++++ 8 files changed, 118 insertions(+), 17 deletions(-) diff --git a/arm64/include/asm/kvm.h b/arm64/include/asm/kvm.h index ed5f3892..1c13bfa2 100644 --- a/arm64/include/asm/kvm.h +++ b/arm64/include/asm/kvm.h @@ -31,7 +31,7 @@ #define KVM_SPSR_FIQ 4 #define KVM_NR_SPSR 5 -#ifndef __ASSEMBLY__ +#ifndef __ASSEMBLER__ #include #include #include @@ -428,6 +428,7 @@ enum { #define KVM_DEV_ARM_ITS_RESTORE_TABLES 2 #define KVM_DEV_ARM_VGIC_SAVE_PENDING_TABLES 3 #define KVM_DEV_ARM_ITS_CTRL_RESET 4 +#define KVM_DEV_ARM_VGIC_USERSPACE_PPIS 5 /* Device Control API on vcpu fd */ #define KVM_ARM_VCPU_PMU_V3_CTRL 0 diff --git a/include/linux/kvm.h b/include/linux/kvm.h index 7a4c35ff..f7dabbf1 100644 --- a/include/linux/kvm.h +++ b/include/linux/kvm.h @@ -179,6 +179,7 @@ struct kvm_xen_exit { #define KVM_EXIT_LOONGARCH_IOCSR 38 #define KVM_EXIT_MEMORY_FAULT 39 #define KVM_EXIT_TDX 40 +#define KVM_EXIT_ARM_SEA 41 /* For KVM_EXIT_INTERNAL_ERROR */ /* Emulate instruction failed. */ @@ -473,6 +474,14 @@ struct kvm_run { } setup_event_notify; }; } tdx; + /* KVM_EXIT_ARM_SEA */ + struct { +#define KVM_EXIT_ARM_SEA_FLAG_GPA_VALID (1ULL << 0) + __u64 flags; + __u64 esr; + __u64 gva; + __u64 gpa; + } arm_sea; /* Fix the size of the union. */ char padding[256]; }; @@ -644,6 +653,7 @@ struct kvm_ioeventfd { #define KVM_X86_DISABLE_EXITS_HLT (1 << 1) #define KVM_X86_DISABLE_EXITS_PAUSE (1 << 2) #define KVM_X86_DISABLE_EXITS_CSTATE (1 << 3) +#define KVM_X86_DISABLE_EXITS_APERFMPERF (1 << 4) /* for KVM_ENABLE_CAP */ struct kvm_enable_cap { @@ -960,6 +970,10 @@ struct kvm_enable_cap { #define KVM_CAP_ARM_EL2 240 #define KVM_CAP_ARM_EL2_E2H0 241 #define KVM_CAP_RISCV_MP_STATE_RESET 242 +#define KVM_CAP_ARM_CACHEABLE_PFNMAP_SUPPORTED 243 +#define KVM_CAP_GUEST_MEMFD_FLAGS 244 +#define KVM_CAP_ARM_SEA_TO_USER 245 +#define KVM_CAP_S390_USER_OPEREXEC 246 struct kvm_irq_routing_irqchip { __u32 irqchip; @@ -1195,6 +1209,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_VGIC_V5, +#define KVM_DEV_TYPE_ARM_VGIC_V5 KVM_DEV_TYPE_ARM_VGIC_V5 KVM_DEV_TYPE_MAX, @@ -1596,6 +1612,8 @@ struct kvm_memory_attributes { #define KVM_MEMORY_ATTRIBUTE_PRIVATE (1ULL << 3) #define KVM_CREATE_GUEST_MEMFD _IOWR(KVMIO, 0xd4, struct kvm_create_guest_memfd) +#define GUEST_MEMFD_FLAG_MMAP (1ULL << 0) +#define GUEST_MEMFD_FLAG_INIT_SHARED (1ULL << 1) struct kvm_create_guest_memfd { __u64 size; diff --git a/include/linux/virtio_ids.h b/include/linux/virtio_ids.h index 7aa2eb76..6c12db16 100644 --- a/include/linux/virtio_ids.h +++ b/include/linux/virtio_ids.h @@ -68,6 +68,7 @@ #define VIRTIO_ID_AUDIO_POLICY 39 /* virtio audio policy */ #define VIRTIO_ID_BT 40 /* virtio bluetooth */ #define VIRTIO_ID_GPIO 41 /* virtio gpio */ +#define VIRTIO_ID_SPI 45 /* virtio spi */ /* * Virtio Transitional IDs diff --git a/include/linux/virtio_net.h b/include/linux/virtio_net.h index 963540de..1db45b01 100644 --- a/include/linux/virtio_net.h +++ b/include/linux/virtio_net.h @@ -70,6 +70,28 @@ * with the same MAC. */ #define VIRTIO_NET_F_SPEED_DUPLEX 63 /* Device set linkspeed and duplex */ +#define VIRTIO_NET_F_GUEST_UDP_TUNNEL_GSO 65 /* Driver can receive + * GSO-over-UDP-tunnel packets + */ +#define VIRTIO_NET_F_GUEST_UDP_TUNNEL_GSO_CSUM 66 /* Driver handles + * GSO-over-UDP-tunnel + * packets with partial csum + * for the outer header + */ +#define VIRTIO_NET_F_HOST_UDP_TUNNEL_GSO 67 /* Device can receive + * GSO-over-UDP-tunnel packets + */ +#define VIRTIO_NET_F_HOST_UDP_TUNNEL_GSO_CSUM 68 /* Device handles + * GSO-over-UDP-tunnel + * packets with partial csum + * for the outer header + */ + +/* Offloads bits corresponding to VIRTIO_NET_F_HOST_UDP_TUNNEL_GSO{,_CSUM} + * features + */ +#define VIRTIO_NET_F_GUEST_UDP_TUNNEL_GSO_MAPPED 46 +#define VIRTIO_NET_F_GUEST_UDP_TUNNEL_GSO_CSUM_MAPPED 47 #ifndef VIRTIO_NET_NO_LEGACY #define VIRTIO_NET_F_GSO 6 /* Host handles pkts w/ any GSO type */ @@ -131,12 +153,17 @@ struct virtio_net_hdr_v1 { #define VIRTIO_NET_HDR_F_NEEDS_CSUM 1 /* Use csum_start, csum_offset */ #define VIRTIO_NET_HDR_F_DATA_VALID 2 /* Csum is valid */ #define VIRTIO_NET_HDR_F_RSC_INFO 4 /* rsc info in csum_ fields */ +#define VIRTIO_NET_HDR_F_UDP_TUNNEL_CSUM 8 /* UDP tunnel csum offload */ __u8 flags; #define VIRTIO_NET_HDR_GSO_NONE 0 /* Not a GSO frame */ #define VIRTIO_NET_HDR_GSO_TCPV4 1 /* GSO frame, IPv4 TCP (TSO) */ #define VIRTIO_NET_HDR_GSO_UDP 3 /* GSO frame, IPv4 UDP (UFO) */ #define VIRTIO_NET_HDR_GSO_TCPV6 4 /* GSO frame, IPv6 TCP */ #define VIRTIO_NET_HDR_GSO_UDP_L4 5 /* GSO frame, IPv4& IPv6 UDP (USO) */ +#define VIRTIO_NET_HDR_GSO_UDP_TUNNEL_IPV4 0x20 /* UDPv4 tunnel present */ +#define VIRTIO_NET_HDR_GSO_UDP_TUNNEL_IPV6 0x40 /* UDPv6 tunnel present */ +#define VIRTIO_NET_HDR_GSO_UDP_TUNNEL (VIRTIO_NET_HDR_GSO_UDP_TUNNEL_IPV4 | \ + VIRTIO_NET_HDR_GSO_UDP_TUNNEL_IPV6) #define VIRTIO_NET_HDR_GSO_ECN 0x80 /* TCP has ECN set */ __u8 gso_type; __virtio16 hdr_len; /* Ethernet + IP + tcp/udp hdrs */ @@ -166,7 +193,8 @@ struct virtio_net_hdr_v1 { struct virtio_net_hdr_v1_hash { struct virtio_net_hdr_v1 hdr; - __le32 hash_value; + __le16 hash_value_lo; + __le16 hash_value_hi; #define VIRTIO_NET_HASH_REPORT_NONE 0 #define VIRTIO_NET_HASH_REPORT_IPv4 1 #define VIRTIO_NET_HASH_REPORT_TCPv4 2 @@ -181,6 +209,12 @@ struct virtio_net_hdr_v1_hash { __le16 padding; }; +struct virtio_net_hdr_v1_hash_tunnel { + struct virtio_net_hdr_v1_hash hash_hdr; + __le16 outer_th_offset; + __le16 inner_nh_offset; +}; + #ifndef VIRTIO_NET_NO_LEGACY /* This header comes first in the scatter-gather list. * For legacy virtio, if VIRTIO_F_ANY_LAYOUT is not negotiated, it must diff --git a/include/linux/virtio_pci.h b/include/linux/virtio_pci.h index c691ac21..e732e345 100644 --- a/include/linux/virtio_pci.h +++ b/include/linux/virtio_pci.h @@ -40,7 +40,7 @@ #define _LINUX_VIRTIO_PCI_H #include -#include +#include #ifndef VIRTIO_PCI_NO_LEGACY diff --git a/powerpc/include/asm/kvm.h b/powerpc/include/asm/kvm.h index eaeda001..077c5437 100644 --- a/powerpc/include/asm/kvm.h +++ b/powerpc/include/asm/kvm.h @@ -1,18 +1,5 @@ /* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ /* - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License, version 2, as - * published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - * * Copyright IBM Corp. 2007 * * Authors: Hollis Blanchard diff --git a/riscv/include/asm/kvm.h b/riscv/include/asm/kvm.h index 5f59fd22..54f3ad7e 100644 --- a/riscv/include/asm/kvm.h +++ b/riscv/include/asm/kvm.h @@ -9,7 +9,7 @@ #ifndef __LINUX_KVM_RISCV_H #define __LINUX_KVM_RISCV_H -#ifndef __ASSEMBLY__ +#ifndef __ASSEMBLER__ #include #include @@ -18,10 +18,13 @@ #define __KVM_HAVE_IRQ_LINE #define KVM_COALESCED_MMIO_PAGE_OFFSET 1 +#define KVM_DIRTY_LOG_PAGE_OFFSET 64 #define KVM_INTERRUPT_SET -1U #define KVM_INTERRUPT_UNSET -2U +#define KVM_EXIT_FAIL_ENTRY_NO_VSFILE (1ULL << 0) + /* for KVM_GET_REGS and KVM_SET_REGS */ struct kvm_regs { }; @@ -55,6 +58,7 @@ struct kvm_riscv_config { unsigned long mimpid; unsigned long zicboz_block_size; unsigned long satp_mode; + unsigned long zicbop_block_size; }; /* CORE registers for KVM_GET_ONE_REG and KVM_SET_ONE_REG */ @@ -184,6 +188,10 @@ enum KVM_RISCV_ISA_EXT_ID { KVM_RISCV_ISA_EXT_ZICCRSE, KVM_RISCV_ISA_EXT_ZAAMO, KVM_RISCV_ISA_EXT_ZALRSC, + KVM_RISCV_ISA_EXT_ZICBOP, + KVM_RISCV_ISA_EXT_ZFBFMIN, + KVM_RISCV_ISA_EXT_ZVFBFMIN, + KVM_RISCV_ISA_EXT_ZVFBFWMA, KVM_RISCV_ISA_EXT_MAX, }; @@ -204,6 +212,8 @@ enum KVM_RISCV_SBI_EXT_ID { KVM_RISCV_SBI_EXT_DBCN, KVM_RISCV_SBI_EXT_STA, KVM_RISCV_SBI_EXT_SUSP, + KVM_RISCV_SBI_EXT_FWFT, + KVM_RISCV_SBI_EXT_MPXY, KVM_RISCV_SBI_EXT_MAX, }; @@ -213,6 +223,18 @@ struct kvm_riscv_sbi_sta { unsigned long shmem_hi; }; +struct kvm_riscv_sbi_fwft_feature { + unsigned long enable; + unsigned long flags; + unsigned long value; +}; + +/* SBI FWFT extension registers for KVM_GET_ONE_REG and KVM_SET_ONE_REG */ +struct kvm_riscv_sbi_fwft { + struct kvm_riscv_sbi_fwft_feature misaligned_deleg; + struct kvm_riscv_sbi_fwft_feature pointer_masking; +}; + /* Possible states for kvm_riscv_timer */ #define KVM_RISCV_TIMER_STATE_OFF 0 #define KVM_RISCV_TIMER_STATE_ON 1 @@ -296,6 +318,9 @@ struct kvm_riscv_sbi_sta { #define KVM_REG_RISCV_SBI_STA (0x0 << KVM_REG_RISCV_SUBTYPE_SHIFT) #define KVM_REG_RISCV_SBI_STA_REG(name) \ (offsetof(struct kvm_riscv_sbi_sta, name) / sizeof(unsigned long)) +#define KVM_REG_RISCV_SBI_FWFT (0x1 << KVM_REG_RISCV_SUBTYPE_SHIFT) +#define KVM_REG_RISCV_SBI_FWFT_REG(name) \ + (offsetof(struct kvm_riscv_sbi_fwft, name) / sizeof(unsigned long)) /* Device Control API: RISC-V AIA */ #define KVM_DEV_RISCV_APLIC_ALIGN 0x1000 diff --git a/x86/include/asm/kvm.h b/x86/include/asm/kvm.h index 0f15d683..7ceff658 100644 --- a/x86/include/asm/kvm.h +++ b/x86/include/asm/kvm.h @@ -35,6 +35,11 @@ #define MC_VECTOR 18 #define XM_VECTOR 19 #define VE_VECTOR 20 +#define CP_VECTOR 21 + +#define HV_VECTOR 28 +#define VC_VECTOR 29 +#define SX_VECTOR 30 /* Select x86 specific features in */ #define __KVM_HAVE_PIT @@ -411,6 +416,35 @@ struct kvm_xcrs { __u64 padding[16]; }; +#define KVM_X86_REG_TYPE_MSR 2 +#define KVM_X86_REG_TYPE_KVM 3 + +#define KVM_X86_KVM_REG_SIZE(reg) \ +({ \ + reg == KVM_REG_GUEST_SSP ? KVM_REG_SIZE_U64 : 0; \ +}) + +#define KVM_X86_REG_TYPE_SIZE(type, reg) \ +({ \ + __u64 type_size = (__u64)type << 32; \ + \ + type_size |= type == KVM_X86_REG_TYPE_MSR ? KVM_REG_SIZE_U64 : \ + type == KVM_X86_REG_TYPE_KVM ? KVM_X86_KVM_REG_SIZE(reg) : \ + 0; \ + type_size; \ +}) + +#define KVM_X86_REG_ID(type, index) \ + (KVM_REG_X86 | KVM_X86_REG_TYPE_SIZE(type, index) | index) + +#define KVM_X86_REG_MSR(index) \ + KVM_X86_REG_ID(KVM_X86_REG_TYPE_MSR, index) +#define KVM_X86_REG_KVM(index) \ + KVM_X86_REG_ID(KVM_X86_REG_TYPE_KVM, index) + +/* KVM-defined registers starting from 0 */ +#define KVM_REG_GUEST_SSP 0 + #define KVM_SYNC_X86_REGS (1UL << 0) #define KVM_SYNC_X86_SREGS (1UL << 1) #define KVM_SYNC_X86_EVENTS (1UL << 2) @@ -468,6 +502,7 @@ struct kvm_sync_regs { /* vendor-specific groups and attributes for system fd */ #define KVM_X86_GRP_SEV 1 # define KVM_X86_SEV_VMSA_FEATURES 0 +# define KVM_X86_SNP_POLICY_BITS 1 struct kvm_vmx_nested_state_data { __u8 vmcs12[KVM_STATE_NESTED_VMX_VMCS_SIZE]; -- 2.34.1 Bump the core arm64 GIC code to also support a GICv5 configuration - invoked with `--irqchip=gicv5`. Only the core GICv5 device is created and initialised. No other GICv5-specific configuration is taking place. These changes are sufficient to start a GICv5-based VM and use PPIs with some big limitations, but do not include any changes to the FDT. Therefore, any guest that requires the FDT to boot will fail. Signed-off-by: Sascha Bischoff --- arm64/gic.c | 23 ++++++++++++++++++----- arm64/include/kvm/gic.h | 1 + 2 files changed, 19 insertions(+), 5 deletions(-) diff --git a/arm64/gic.c b/arm64/gic.c index 7461b0f3..8e4ff846 100644 --- a/arm64/gic.c +++ b/arm64/gic.c @@ -41,6 +41,8 @@ int irqchip_parser(const struct option *opt, const char *arg, int unset) *type = IRQCHIP_GICV3; } else if (!strcmp(arg, "gicv3-its")) { *type = IRQCHIP_GICV3_ITS; + } else if (!strcmp(arg, "gicv5")) { + *type = IRQCHIP_GICV5; } else { pr_err("irqchip: unknown type \"%s\"\n", arg); return -1; @@ -182,6 +184,9 @@ static int gic__create_device(struct kvm *kvm, enum irqchip_type type) gic_device.type = KVM_DEV_TYPE_ARM_VGIC_V3; dist_attr.attr = KVM_VGIC_V3_ADDR_TYPE_DIST; break; + case IRQCHIP_GICV5: + gic_device.type = KVM_DEV_TYPE_ARM_VGIC_V5; + break; case IRQCHIP_AUTO: return -ENODEV; } @@ -201,15 +206,20 @@ static int gic__create_device(struct kvm *kvm, enum irqchip_type type) case IRQCHIP_GICV3: err = ioctl(gic_fd, KVM_SET_DEVICE_ATTR, &redist_attr); break; + case IRQCHIP_GICV5: + break; case IRQCHIP_AUTO: return -ENODEV; } if (err) goto out_err; - err = ioctl(gic_fd, KVM_SET_DEVICE_ATTR, &dist_attr); - if (err) - goto out_err; + /* Only set the dist_attr for non-GICv5 */ + if (type != IRQCHIP_GICV5) { + err = ioctl(gic_fd, KVM_SET_DEVICE_ATTR, &dist_attr); + if (err) + goto out_err; + } err = gic__create_msi_frame(kvm, type, gic_msi_base); if (err) @@ -258,7 +268,7 @@ int gic__create(struct kvm *kvm, enum irqchip_type type) switch (type) { case IRQCHIP_AUTO: - for (try = IRQCHIP_GICV3_ITS; try >= IRQCHIP_GICV2; try--) { + for (try = IRQCHIP_GICV5; try >= IRQCHIP_GICV2; try--) { err = gic__create(kvm, try); if (!err) break; @@ -283,6 +293,8 @@ int gic__create(struct kvm *kvm, enum irqchip_type type) gic_redists_base = ARM_GIC_DIST_BASE - gic_redists_size; gic_msi_base = gic_redists_base - gic_msi_size; break; + case IRQCHIP_GICV5: + break; default: return -ENODEV; } @@ -420,7 +432,8 @@ u32 gic__get_fdt_irq_cpumask(struct kvm *kvm) { /* Only for GICv2 */ if (kvm->cfg.arch.irqchip == IRQCHIP_GICV3 || - kvm->cfg.arch.irqchip == IRQCHIP_GICV3_ITS) + kvm->cfg.arch.irqchip == IRQCHIP_GICV3_ITS || + kvm->cfg.arch.irqchip == IRQCHIP_GICV5) return 0; if (kvm->nrcpus > 8) diff --git a/arm64/include/kvm/gic.h b/arm64/include/kvm/gic.h index 1541a582..83fbf89b 100644 --- a/arm64/include/kvm/gic.h +++ b/arm64/include/kvm/gic.h @@ -29,6 +29,7 @@ enum irqchip_type { IRQCHIP_GICV2M, IRQCHIP_GICV3, IRQCHIP_GICV3_ITS, + IRQCHIP_GICV5, }; struct kvm; -- 2.34.1 In order to correctly describe GICv5 interrupts in the FDT, we add the GICv5-specific FDT identifiers for GICv5's PPIs, SPIs, and LPIs. These match those from GICv5's device tree schema. Signed-off-by: Sascha Bischoff --- arm64/include/kvm/gic.h | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/arm64/include/kvm/gic.h b/arm64/include/kvm/gic.h index 83fbf89b..dd7729a2 100644 --- a/arm64/include/kvm/gic.h +++ b/arm64/include/kvm/gic.h @@ -10,6 +10,12 @@ #define GIC_FDT_IRQ_TYPE_SPI 0 #define GIC_FDT_IRQ_TYPE_PPI 1 +#define GICV5_FDT_IRQ_TYPE_PPI 1 +#define GICV5_FDT_IRQ_TYPE_LPI 2 +#define GICV5_FDT_IRQ_TYPE_SPI 3 +#define GICV5_FDT_IRQ_TYPE_MASK 0x7 +#define GICV5_FDT_IRQ_TYPE_SHIFT 29 + #define GIC_FDT_IRQ_PPI_CPU_SHIFT 8 #define GIC_FDT_IRQ_PPI_CPU_MASK (0xff << GIC_FDT_IRQ_PPI_CPU_SHIFT) -- 2.34.1 GICv5 requires a different set of FDT nodes to what was required for GICv2/v3. Therefore, add in a GICv5-specific function to generate the FDT nodes as this is much cleaner than trying to adapt the existing code to generate both variants. This change generates nodes for the GICv5 CPU interface only. This is enough to support PPIs. Additional FDT changes are to follow as the IRS and ITS support is added. Signed-off-by: Sascha Bischoff --- arm64/arm-cpu.c | 3 ++- arm64/gic.c | 25 ++++++++++++++++++++++++- arm64/include/kvm/gic.h | 3 ++- 3 files changed, 28 insertions(+), 3 deletions(-) diff --git a/arm64/arm-cpu.c b/arm64/arm-cpu.c index abdd6324..f460bee7 100644 --- a/arm64/arm-cpu.c +++ b/arm64/arm-cpu.c @@ -13,7 +13,8 @@ static void generate_fdt_nodes(void *fdt, struct kvm *kvm) { gic__generate_fdt_nodes(fdt, kvm->cfg.arch.irqchip, - kvm->cfg.arch.nested_virt); + kvm->cfg.arch.nested_virt, + kvm->nrcpus); timer__generate_fdt_nodes(fdt, kvm); pmu__generate_fdt_nodes(fdt, kvm); } diff --git a/arm64/gic.c b/arm64/gic.c index 8e4ff846..67b96734 100644 --- a/arm64/gic.c +++ b/arm64/gic.c @@ -368,7 +368,11 @@ static int gic__init_gic(struct kvm *kvm) } late_init(gic__init_gic) -void gic__generate_fdt_nodes(void *fdt, enum irqchip_type type, bool nested) +static void gic__generate_gicv5_fdt_nodes(void *fdt, enum irqchip_type type, + bool nested, int nr_cpus); + +void gic__generate_fdt_nodes(void *fdt, enum irqchip_type type, bool nested, + int nr_cpus) { const char *compatible, *msi_compatible = NULL; u64 msi_prop[2]; @@ -396,6 +400,8 @@ void gic__generate_fdt_nodes(void *fdt, enum irqchip_type type, bool nested) reg_prop[2] = cpu_to_fdt64(gic_redists_base); reg_prop[3] = cpu_to_fdt64(gic_redists_size); break; + case IRQCHIP_GICV5: + return gic__generate_gicv5_fdt_nodes(fdt, type, nested, nr_cpus); default: return; } @@ -428,6 +434,23 @@ void gic__generate_fdt_nodes(void *fdt, enum irqchip_type type, bool nested) _FDT(fdt_end_node(fdt)); } +static void gic__generate_gicv5_fdt_nodes(void *fdt, enum irqchip_type type, + bool nested, int nr_cpus) +{ + _FDT(fdt_begin_node(fdt, "gicv5-cpuif")); + _FDT(fdt_property_string(fdt, "compatible", "arm,gic-v5")); + _FDT(fdt_property_cell(fdt, "#interrupt-cells", GIC_FDT_IRQ_NUM_CELLS)); + _FDT(fdt_property(fdt, "interrupt-controller", NULL, 0)); + _FDT(fdt_property_cell(fdt, "#address-cells", 2)); + _FDT(fdt_property_cell(fdt, "#size-cells", 2)); + _FDT(fdt_property(fdt, "ranges", NULL, 0)); + + /* Use a hard-coded phandle for the GIC to help wire things up */ + _FDT(fdt_property_cell(fdt, "phandle", PHANDLE_GIC)); + + _FDT(fdt_end_node(fdt)); // End of GIC node +} + u32 gic__get_fdt_irq_cpumask(struct kvm *kvm) { /* Only for GICv2 */ diff --git a/arm64/include/kvm/gic.h b/arm64/include/kvm/gic.h index dd7729a2..13742bd5 100644 --- a/arm64/include/kvm/gic.h +++ b/arm64/include/kvm/gic.h @@ -43,7 +43,8 @@ struct kvm; int gic__alloc_irqnum(void); int gic__create(struct kvm *kvm, enum irqchip_type type); int gic__create_gicv2m_frame(struct kvm *kvm, u64 msi_frame_addr); -void gic__generate_fdt_nodes(void *fdt, enum irqchip_type type, bool nested); +void gic__generate_fdt_nodes(void *fdt, enum irqchip_type type, + bool nested, int nr_cpus); u32 gic__get_fdt_irq_cpumask(struct kvm *kvm); int gic__add_irqfd(struct kvm *kvm, unsigned int gsi, int trigger_fd, -- 2.34.1 Update the PMU code to interact correctly with the GICV5 support in KVM and the guest. This change comprises two parts: The first is to update the interrupt specifier used for the FDT to generate a GICv5-compatible description of the PMU overflow interrupt. The second is to correctly convey the PPI used for the overflow interrupt to KVM itself. This needs to be in the correct GICv5 interrupt ID format (type + ID), which requires the interrupt type in the top bits. Moreover, it must match the architecturally defined PMUIRQ for GICv5. Signed-off-by: Sascha Bischoff --- arm64/pmu.c | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/arm64/pmu.c b/arm64/pmu.c index 5f31d6b6..1720cc00 100644 --- a/arm64/pmu.c +++ b/arm64/pmu.c @@ -197,13 +197,19 @@ void pmu__generate_fdt_nodes(void *fdt, struct kvm *kvm) struct kvm_cpu *vcpu; int pmu_id = -ENXIO; int i; - + u32 irq_prop[3]; u32 cpu_mask = gic__get_fdt_irq_cpumask(kvm); - u32 irq_prop[] = { - cpu_to_fdt32(GIC_FDT_IRQ_TYPE_PPI), - cpu_to_fdt32(irq - 16), - cpu_to_fdt32(cpu_mask | IRQ_TYPE_LEVEL_HIGH), - }; + + if (kvm->cfg.arch.irqchip == IRQCHIP_GICV5) { + irq_prop[0] = cpu_to_fdt32(GICV5_FDT_IRQ_TYPE_PPI); + irq_prop[1] = cpu_to_fdt32(irq); + /* For GICv5, encode the full intid by adding the type */ + irq |= GICV5_FDT_IRQ_TYPE_PPI << GICV5_FDT_IRQ_TYPE_SHIFT; + } else { + irq_prop[0] = cpu_to_fdt32(GIC_FDT_IRQ_TYPE_PPI); + irq_prop[1] = cpu_to_fdt32(irq - 16); + } + irq_prop[2] = cpu_to_fdt32(cpu_mask | IRQ_TYPE_LEVEL_HIGH); if (!kvm->cfg.arch.has_pmuv3) return; -- 2.34.1 Update the FDT generation for the per-CPU PPI timers to be GICv5-compatible. In order to keep the code working for both older GICs and GICv5, we introduce an offset for GICv5. This effectively applies the implicit shift that is applied to PPI IDs on older GICs (they follow the SGIs) explicitly. Signed-off-by: Sascha Bischoff --- arm64/timer.c | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/arm64/timer.c b/arm64/timer.c index 2ac6144f..0945510d 100644 --- a/arm64/timer.c +++ b/arm64/timer.c @@ -12,13 +12,22 @@ void timer__generate_fdt_nodes(void *fdt, struct kvm *kvm) int irqs[5] = {13, 14, 11, 10, 12}; int nr = ARRAY_SIZE(irqs); u32 irq_prop[nr * 3]; + u32 type, offset; if (!kvm->cfg.arch.nested_virt) nr--; + if (kvm->cfg.arch.irqchip == IRQCHIP_GICV5) { + type = GICV5_FDT_IRQ_TYPE_PPI; + offset = 16; + } else { + type = GIC_FDT_IRQ_TYPE_PPI; + offset = 0; + } + for (int i = 0; i < nr; i++) { - irq_prop[i * 3 + 0] = cpu_to_fdt32(GIC_FDT_IRQ_TYPE_PPI); - irq_prop[i * 3 + 1] = cpu_to_fdt32(irqs[i]); + irq_prop[i * 3 + 0] = cpu_to_fdt32(type); + irq_prop[i * 3 + 1] = cpu_to_fdt32(irqs[i] + offset); irq_prop[i * 3 + 2] = cpu_to_fdt32(cpu_mask | IRQ_TYPE_LEVEL_LOW); } -- 2.34.1 Add an interface to override the default irq offset (KVM_IRQ_OFFSET) as long as no interrupt lines have been allocated. By default, KVM_IRQ_OFFSET is applied to all allocated interrupt lines, but calling irq__init_irq_offset() prior to allocating any interrupt lines allows this default offset to be overridden. Attempts to change the offset once lines have been allocated are intentionally rejected. This is part of GICv5-enablement as GICv5 SPIs count from 0, whilst older Arm GICs count from 32. Therefore, on a GICv5 system there is no shift that gets applied to the SPI ID, and hence we need to start counting from 0 to ensure correct alignment between kvmtool and Linux. Signed-off-by: Sascha Bischoff --- include/kvm/irq.h | 1 + irq.c | 16 +++++++++++++++- 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/include/kvm/irq.h b/include/kvm/irq.h index 2a3f8c9d..17113979 100644 --- a/include/kvm/irq.h +++ b/include/kvm/irq.h @@ -23,6 +23,7 @@ extern struct msi_routing_ops *msi_routing_ops; extern struct kvm_irq_routing *irq_routing; extern int next_gsi; +int irq__init_irq_offset(u8 offset); int irq__alloc_line(void); int irq__get_nr_allocated_lines(void); diff --git a/irq.c b/irq.c index cdcf9923..8b9daa91 100644 --- a/irq.c +++ b/irq.c @@ -8,6 +8,7 @@ #include "kvm/irq.h" #include "kvm/kvm-arch.h" +static u8 irq_offset = KVM_IRQ_OFFSET; static u8 next_line = KVM_IRQ_OFFSET; static int allocated_gsis = 0; @@ -18,6 +19,19 @@ struct msi_routing_ops *msi_routing_ops = &irq__default_routing_ops; struct kvm_irq_routing *irq_routing = NULL; +/* Override the default KVM_IRQ_OFFSET */ +int irq__init_irq_offset(u8 offset) +{ + /* Block attempt to do this too late */ + if (irq__get_nr_allocated_lines()) + return -EBUSY; + + irq_offset = offset; + next_line = offset; + + return 0; +} + int irq__alloc_line(void) { return next_line++; @@ -25,7 +39,7 @@ int irq__alloc_line(void) int irq__get_nr_allocated_lines(void) { - return next_line - KVM_IRQ_OFFSET; + return next_line - irq_offset; } int irq__allocate_routing_entry(void) -- 2.34.1 GICv5 requires a mapping of Interrupt AFFinity IDs to CPUs, and uses CPU Phandles in the FDT for this purpose. Create a per-CPU phandle when writing the CPU FDT nodes, which can then be used later on when generating the FDT to create this mapping of CPUs to their IAFFIDs. These CPU phandles come after those hard-coded for the GIC and MSE controller. Signed-off-by: Sascha Bischoff --- arm64/fdt.c | 3 +++ arm64/include/kvm/fdt-arch.h | 2 ++ 2 files changed, 5 insertions(+) diff --git a/arm64/fdt.c b/arm64/fdt.c index 98f1dd9d..44361e6b 100644 --- a/arm64/fdt.c +++ b/arm64/fdt.c @@ -54,6 +54,9 @@ static void generate_cpu_nodes(void *fdt, struct kvm *kvm) _FDT(fdt_property_string(fdt, "enable-method", "psci")); _FDT(fdt_property_cell(fdt, "reg", mpidr)); + + _FDT(fdt_property_cell(fdt, "phandle", PHANDLE_CPU_BASE + cpu)); + _FDT(fdt_end_node(fdt)); } diff --git a/arm64/include/kvm/fdt-arch.h b/arm64/include/kvm/fdt-arch.h index 60c2d406..3c3bd682 100644 --- a/arm64/include/kvm/fdt-arch.h +++ b/arm64/include/kvm/fdt-arch.h @@ -3,4 +3,6 @@ enum phandles {PHANDLE_RESERVED = 0, PHANDLE_GIC, PHANDLE_MSI, PHANDLES_MAX}; +#define PHANDLE_CPU_BASE PHANDLES_MAX + #endif /* ARM__FDT_H */ -- 2.34.1 Track if the GIC is GICv5, and provide interface to check that. This avoids having to rely on either struct kvm or passing irqchip information throughout the code, thereby keeping things a tad cleaner in the process. Signed-off-by: Sascha Bischoff --- arm64/gic.c | 20 ++++++++++++++++++++ arm64/include/kvm/gic.h | 1 + 2 files changed, 21 insertions(+) diff --git a/arm64/gic.c b/arm64/gic.c index 67b96734..a49bc9b9 100644 --- a/arm64/gic.c +++ b/arm64/gic.c @@ -19,6 +19,12 @@ static u64 gic_redists_size; static u64 gic_msi_base; static u64 gic_msi_size = 0; static bool vgic_is_init = false; +static bool vgic_is_v5 = false; + +bool gic__is_v5(void) +{ + return vgic_is_v5; +} struct kvm_irqfd_line { unsigned int gsi; @@ -225,6 +231,20 @@ static int gic__create_device(struct kvm *kvm, enum irqchip_type type) if (err) goto out_err; + /* + * If we are using GICv5, then we need to allocate SPIs starting at 0, + * and not at the legacy offset of 32. This must happen before any + * interrupts are allocated. + */ + if ((type == IRQCHIP_GICV5)) { + err = irq__init_irq_offset(0); + if (err) + goto out_err; + + /* Track that we have a GICv5 - needed for FDT */ + vgic_is_v5 = true; + } + return 0; out_err: diff --git a/arm64/include/kvm/gic.h b/arm64/include/kvm/gic.h index 13742bd5..805f4247 100644 --- a/arm64/include/kvm/gic.h +++ b/arm64/include/kvm/gic.h @@ -40,6 +40,7 @@ enum irqchip_type { struct kvm; +bool gic__is_v5(void); int gic__alloc_irqnum(void); int gic__create(struct kvm *kvm, enum irqchip_type type); int gic__create_gicv2m_frame(struct kvm *kvm, u64 msi_frame_addr); -- 2.34.1 Add in the additional changes required to run GICv5 VMs with IRS and ITS support. For the IRS, this adds the ability to set the address of the IRS and do save/restore operations for the MMIO regs and ISTs. For the ITS, it allows the ITS device to be created, the base address to be set, and again for save/restore of the MMIO regions to take place. Signed-off-by: Sascha Bischoff --- arm64/include/asm/kvm.h | 9 +++++++++ include/linux/kvm.h | 4 +++- 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/arm64/include/asm/kvm.h b/arm64/include/asm/kvm.h index 1c13bfa2..6572514b 100644 --- a/arm64/include/asm/kvm.h +++ b/arm64/include/asm/kvm.h @@ -97,6 +97,13 @@ struct kvm_regs { #define KVM_VGIC_V3_REDIST_SIZE (2 * SZ_64K) #define KVM_VGIC_V3_ITS_SIZE (2 * SZ_64K) +/* Supported VGICv5 address types */ +#define KVM_VGIC_V5_ADDR_TYPE_IRS 6 +#define KVM_VGIC_V5_ADDR_TYPE_ITS 7 + +#define KVM_VGIC_V5_IRS_SIZE (2 * SZ_64K) +#define KVM_VGIC_V5_ITS_SIZE (2 * SZ_64K) + #define KVM_ARM_VCPU_POWER_OFF 0 /* CPU is started in OFF state */ #define KVM_ARM_VCPU_EL1_32BIT 1 /* CPU running a 32bit VM */ #define KVM_ARM_VCPU_PSCI_0_2 2 /* CPU uses PSCI v0.2 */ @@ -422,6 +429,8 @@ enum { (0x3fffffULL << KVM_DEV_ARM_VGIC_LINE_LEVEL_INFO_SHIFT) #define KVM_DEV_ARM_VGIC_LINE_LEVEL_INTID_MASK 0x3ff #define VGIC_LEVEL_INFO_LINE_LEVEL 0 +#define KVM_DEV_ARM_VGIC_GRP_IRS_REGS 10 +#define KVM_DEV_ARM_VGIC_GRP_IST 11 #define KVM_DEV_ARM_VGIC_CTRL_INIT 0 #define KVM_DEV_ARM_ITS_SAVE_TABLES 1 diff --git a/include/linux/kvm.h b/include/linux/kvm.h index f7dabbf1..14292051 100644 --- a/include/linux/kvm.h +++ b/include/linux/kvm.h @@ -1210,7 +1210,9 @@ enum kvm_device_type { KVM_DEV_TYPE_LOONGARCH_PCHPIC, #define KVM_DEV_TYPE_LOONGARCH_PCHPIC KVM_DEV_TYPE_LOONGARCH_PCHPIC KVM_DEV_TYPE_ARM_VGIC_V5, -#define KVM_DEV_TYPE_ARM_VGIC_V5 KVM_DEV_TYPE_ARM_VGIC_V5 +#define KVM_DEV_TYPE_ARM_VGIC_V5 KVM_DEV_TYPE_ARM_VGIC_V5 + KVM_DEV_TYPE_ARM_VGIC_V5_ITS, +#define KVM_DEV_TYPE_ARM_VGIC_V5_ITS KVM_DEV_TYPE_ARM_VGIC_V5_ITS KVM_DEV_TYPE_MAX, -- 2.34.1 Set the address of the IRS for the basic `gicv5` config, which is required to correctly initialise the GIC once the KVM IRS support has been introduced. This change enables SPIs and LPIs to be used with a GICv5 guest. MSIs are not supported. Note: the FDT changes to add the IRS node are still to come. Signed-off-by: Sascha Bischoff --- arm64/gic.c | 20 ++++++++++++++++++-- arm64/include/kvm/kvm-arch.h | 30 ++++++++++++++++++++++++++++++ 2 files changed, 48 insertions(+), 2 deletions(-) diff --git a/arm64/gic.c b/arm64/gic.c index a49bc9b9..5cb195ac 100644 --- a/arm64/gic.c +++ b/arm64/gic.c @@ -16,6 +16,8 @@ static int gic_fd = -1; static u64 gic_redists_base; static u64 gic_redists_size; +static u64 gicv5_irs_base; +static u64 gicv5_irs_size; static u64 gic_msi_base; static u64 gic_msi_size = 0; static bool vgic_is_init = false; @@ -178,6 +180,11 @@ static int gic__create_device(struct kvm *kvm, enum irqchip_type type) .attr = KVM_VGIC_V3_ADDR_TYPE_REDIST, .addr = (u64)(unsigned long)&gic_redists_base, }; + struct kvm_device_attr gicv5_irs_attr = { + .group = KVM_DEV_ARM_VGIC_GRP_ADDR, + .attr = KVM_VGIC_V5_ADDR_TYPE_IRS, + .addr = (u64)(unsigned long)&gicv5_irs_base, + }; switch (type) { case IRQCHIP_GICV2M: @@ -213,6 +220,7 @@ static int gic__create_device(struct kvm *kvm, enum irqchip_type type) err = ioctl(gic_fd, KVM_SET_DEVICE_ATTR, &redist_attr); break; case IRQCHIP_GICV5: + err = ioctl(gic_fd, KVM_SET_DEVICE_ATTR, &gicv5_irs_attr); break; case IRQCHIP_AUTO: return -ENODEV; @@ -314,6 +322,8 @@ int gic__create(struct kvm *kvm, enum irqchip_type type) gic_msi_base = gic_redists_base - gic_msi_size; break; case IRQCHIP_GICV5: + gicv5_irs_base = ARM_GICV5_IRS_BASE; + gicv5_irs_size = ARM_GICV5_IRS_SIZE; break; default: return -ENODEV; @@ -335,8 +345,14 @@ static int gic__init_gic(struct kvm *kvm) int ret; int lines = irq__get_nr_allocated_lines(); - u32 nr_irqs = ALIGN(lines, 32) + GIC_SPI_IRQ_BASE; u32 maint_irq = GIC_MAINT_IRQ + 16; /* PPI */ + u32 nr_irqs; + + if ((kvm->cfg.arch.irqchip != IRQCHIP_GICV5)) + nr_irqs = ALIGN(lines, 32) + GIC_SPI_IRQ_BASE; + else + nr_irqs = roundup_pow_of_two(lines); + struct kvm_device_attr nr_irqs_attr = { .group = KVM_DEV_ARM_VGIC_GRP_NR_IRQS, .addr = (u64)(unsigned long)&nr_irqs, @@ -495,7 +511,7 @@ void kvm__irq_line(struct kvm *kvm, int irq, int level) .level = !!level, }; - if (irq < GIC_SPI_IRQ_BASE || irq > GIC_MAX_IRQ) + if (!gic__is_v5() && (irq < GIC_SPI_IRQ_BASE || irq > GIC_MAX_IRQ)) pr_warning("Ignoring invalid GIC IRQ %d", irq); else if (ioctl(kvm->vm_fd, KVM_IRQ_LINE, &irq_level) < 0) pr_warning("Could not KVM_IRQ_LINE for irq %d", irq); diff --git a/arm64/include/kvm/kvm-arch.h b/arm64/include/kvm/kvm-arch.h index 8f508ef8..717a7360 100644 --- a/arm64/include/kvm/kvm-arch.h +++ b/arm64/include/kvm/kvm-arch.h @@ -57,6 +57,36 @@ #define ARM_GIC_CPUI_SIZE 0x20000 +/* + * GICv5-specific definitions for the various MMIO frames. + * + * Base for the IRS, ITS. These live at the end of the MMIO area. + * + * The IRS assumes back-to-back CONFIG and SETLPI frames. + * The ITS assumes back-to-back CONFIG and TRANSLATE frames. + * + * REST OF MMIO AREA + * ***************************************** + * ITS FRAMES (128K) + * ***************************************** + * IRS FRAMES (128K) + * ***************************************** + * ARM_AXI_AREA + */ +#define ARM_GICV5_IRS_BASE (ARM_AXI_AREA - ARM_GICV5_IRS_SIZE) +#define ARM_GICV5_IRS_SIZE (ARM_GICV5_IRS_CONFIG_SIZE + ARM_GICV5_IRS_SETLPI_SIZE) +#define ARM_GICV5_IRS_CONFIG_BASE ARM_GICV5_IRS_BASE +#define ARM_GICV5_IRS_CONFIG_SIZE 0x10000 +#define ARM_GICV5_IRS_SETLPI_BASE (ARM_GICV5_IRS_BASE + ARM_GICV5_IRS_SETLPI_SIZE) +#define ARM_GICV5_IRS_SETLPI_SIZE 0x10000 +#define ARM_GICV5_ITS_BASE (ARM_GICV5_IRS_BASE - ARM_GICV5_ITS_SIZE) +#define ARM_GICV5_ITS_SIZE (ARM_GICV5_ITS_CONFIG_SIZE + ARM_GICV5_ITS_TRANSL_SIZE) +#define ARM_GICV5_ITS_CONFIG_BASE ARM_GICV5_ITS_BASE +#define ARM_GICV5_ITS_CONFIG_SIZE 0x10000 +#define ARM_GICV5_ITS_TRANSL_BASE (ARM_GICV5_ITS_BASE + ARM_GICV5_ITS_TRANSL_SIZE) +#define ARM_GICV5_ITS_TRANSL_SIZE 0x10000 + + #define KVM_PCI_CFG_AREA ARM_AXI_AREA #define ARM_PCI_CFG_SIZE (1ULL << 28) #define KVM_PCI_MMIO_AREA (KVM_PCI_CFG_AREA + ARM_PCI_CFG_SIZE) -- 2.34.1 Extend the GICv5 FGT generation to include the IRS. For the `gicv5` irqchip config, generate nodes for the GICv5 CPU interface and the IRS. This means that the IAFFIDs are now configured, using the CPU phandles that were previously set up. Signed-off-by: Sascha Bischoff --- arm64/gic.c | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/arm64/gic.c b/arm64/gic.c index 5cb195ac..2152abf6 100644 --- a/arm64/gic.c +++ b/arm64/gic.c @@ -473,6 +473,16 @@ void gic__generate_fdt_nodes(void *fdt, enum irqchip_type type, bool nested, static void gic__generate_gicv5_fdt_nodes(void *fdt, enum irqchip_type type, bool nested, int nr_cpus) { + char node_at_addr[64]; + int cpus[nr_cpus]; + u16 iaffids[nr_cpus]; + u64 irs_reg_prop[] = { + cpu_to_fdt64(ARM_GICV5_IRS_CONFIG_BASE), + cpu_to_fdt64(ARM_GICV5_IRS_CONFIG_SIZE), + cpu_to_fdt64(ARM_GICV5_IRS_SETLPI_BASE), + cpu_to_fdt64(ARM_GICV5_IRS_SETLPI_SIZE) + }; + _FDT(fdt_begin_node(fdt, "gicv5-cpuif")); _FDT(fdt_property_string(fdt, "compatible", "arm,gic-v5")); _FDT(fdt_property_cell(fdt, "#interrupt-cells", GIC_FDT_IRQ_NUM_CELLS)); @@ -484,6 +494,29 @@ static void gic__generate_gicv5_fdt_nodes(void *fdt, enum irqchip_type type, /* Use a hard-coded phandle for the GIC to help wire things up */ _FDT(fdt_property_cell(fdt, "phandle", PHANDLE_GIC)); + /* + * GICv5 IRS node + */ + snprintf(node_at_addr, 64, "gicv5-irs@%lx", fdt64_to_cpu(irs_reg_prop[0])); + _FDT(fdt_begin_node(fdt, node_at_addr)); + _FDT(fdt_property_string(fdt, "compatible", "arm,gic-v5-irs")); + _FDT(fdt_property_cell(fdt, "#address-cells", 2)); + _FDT(fdt_property_cell(fdt, "#size-cells", 2)); + _FDT(fdt_property(fdt, "ranges", NULL, 0)); + + _FDT(fdt_property(fdt, "reg", irs_reg_prop, sizeof(irs_reg_prop))); + _FDT(fdt_property_string(fdt, "reg-names", "ns-config")); + + for (int cpu = 0; cpu < nr_cpus; ++cpu) { + cpus[cpu] = cpu_to_fdt32(PHANDLE_CPU_BASE + cpu); + iaffids[cpu] = cpu_to_fdt16(cpu); + } + + _FDT(fdt_property(fdt, "cpus", cpus, sizeof(u32) * nr_cpus)); + _FDT(fdt_property(fdt, "arm,iaffids", iaffids, sizeof(u16) * nr_cpus)); + + _FDT(fdt_end_node(fdt)); // End of IRS node + _FDT(fdt_end_node(fdt)); // End of GIC node } -- 2.34.1 Add support for generating SPI interupt defs that match either GICv5, or legacy GICs. This checks if the GIC is v5 or not, and generates either a GICv2/v3-compatible entry, or one that is GICv5-compatible. This ensures that devices using this interface (RTC, Virtio MMIO) don't need to be aware that a GICv5 irqchip is being used, and are able to directly generate the correct FDT entries. Signed-off-by: Sascha Bischoff --- arm64/fdt.c | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/arm64/fdt.c b/arm64/fdt.c index 44361e6b..07556dff 100644 --- a/arm64/fdt.c +++ b/arm64/fdt.c @@ -65,11 +65,20 @@ static void generate_cpu_nodes(void *fdt, struct kvm *kvm) static void generate_irq_prop(void *fdt, u8 irq, enum irq_type irq_type) { - u32 irq_prop[] = { - cpu_to_fdt32(GIC_FDT_IRQ_TYPE_SPI), - cpu_to_fdt32(irq - GIC_SPI_IRQ_BASE), - cpu_to_fdt32(irq_type) - }; + u32 irq_prop[3]; + u32 type, offset; + + if (gic__is_v5()) { + type = GICV5_FDT_IRQ_TYPE_SPI; + offset = 0; + } else { + type = GIC_FDT_IRQ_TYPE_SPI; + offset = -GIC_SPI_IRQ_BASE; + } + + irq_prop[0] = cpu_to_fdt32(type); + irq_prop[1] = cpu_to_fdt32(irq + offset); + irq_prop[2] = cpu_to_fdt32(irq_type); _FDT(fdt_property(fdt, "interrupts", irq_prop, sizeof(irq_prop))); } -- 2.34.1 Update the PCI FDT code to generate GICv5-compatible interrupt descriptors for the legacy INTx interrupts. These are used by a guest if MSIs are available. Signed-off-by: Sascha Bischoff --- arm64/pci.c | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/arm64/pci.c b/arm64/pci.c index 03667833..79ca34e8 100644 --- a/arm64/pci.c +++ b/arm64/pci.c @@ -82,6 +82,7 @@ void pci__generate_fdt_nodes(void *fdt, struct kvm *kvm) u8 pin = pci_hdr->irq_pin; u8 irq = pci_hdr->irq_line; u32 irq_flags = pci_hdr->irq_type; + u32 int_type, int_id; /* * Avoid adding entries in "interrupt-map" for devices that @@ -93,6 +94,14 @@ void pci__generate_fdt_nodes(void *fdt, struct kvm *kvm) continue; } + if (!gic__is_v5()) { + int_type = GIC_FDT_IRQ_TYPE_SPI; + int_id = irq - GIC_SPI_IRQ_BASE; + } else { + int_type = GICV5_FDT_IRQ_TYPE_SPI; + int_id = irq; + } + *entry = (struct of_interrupt_map_entry) { .pci_irq_mask = { .pci_addr = { @@ -106,8 +115,8 @@ void pci__generate_fdt_nodes(void *fdt, struct kvm *kvm) .gic_addr_hi = 0, .gic_addr_lo = 0, .gic_irq = { - .type = cpu_to_fdt32(GIC_FDT_IRQ_TYPE_SPI), - .num = cpu_to_fdt32(irq - GIC_SPI_IRQ_BASE), + .type = cpu_to_fdt32(int_type), + .num = cpu_to_fdt32(int_id), .flags = cpu_to_fdt32(irq_flags), }, }; -- 2.34.1 Extend the GIC code to be able to generate a configuration with an ITS (`gicv5-its`). With this change, the guest is able to support MSIs. Note that the FDT changes to add the ITS node are made in a separate commit. Signed-off-by: Sascha Bischoff --- arm64/gic.c | 35 ++++++++++++++++++++++++----------- arm64/include/kvm/gic.h | 1 + arm64/pmu.c | 3 ++- arm64/timer.c | 3 ++- 4 files changed, 29 insertions(+), 13 deletions(-) diff --git a/arm64/gic.c b/arm64/gic.c index 2152abf6..45dd2dab 100644 --- a/arm64/gic.c +++ b/arm64/gic.c @@ -51,6 +51,8 @@ int irqchip_parser(const struct option *opt, const char *arg, int unset) *type = IRQCHIP_GICV3_ITS; } else if (!strcmp(arg, "gicv5")) { *type = IRQCHIP_GICV5; + } else if (!strcmp(arg, "gicv5-its")) { + *type = IRQCHIP_GICV5_ITS; } else { pr_err("irqchip: unknown type \"%s\"\n", arg); return -1; @@ -107,15 +109,15 @@ static int irq__routing_init(struct kvm *kvm) return 0; } -static int gic__create_its_frame(struct kvm *kvm, u64 its_frame_addr) +static int gic__create_its_frame(struct kvm *kvm, u64 its_frame_addr, const bool v5) { struct kvm_create_device its_device = { - .type = KVM_DEV_TYPE_ARM_VGIC_ITS, + .type = v5 ? KVM_DEV_TYPE_ARM_VGIC_V5_ITS : KVM_DEV_TYPE_ARM_VGIC_ITS, .flags = 0, }; struct kvm_device_attr its_attr = { .group = KVM_DEV_ARM_VGIC_GRP_ADDR, - .attr = KVM_VGIC_ITS_ADDR_TYPE, + .attr = v5 ? KVM_VGIC_V5_ADDR_TYPE_ITS : KVM_VGIC_ITS_ADDR_TYPE, .addr = (u64)(unsigned long)&its_frame_addr, }; struct kvm_device_attr its_init_attr = { @@ -126,8 +128,9 @@ static int gic__create_its_frame(struct kvm *kvm, u64 its_frame_addr) err = ioctl(kvm->vm_fd, KVM_CREATE_DEVICE, &its_device); if (err) { - pr_err("GICv3 ITS requested, but kernel does not support it."); - pr_err("Try --irqchip=gicv3 instead"); + pr_err("GICv%c ITS requested, but kernel does not support it.", + v5 ? '5' : '3'); + pr_err("Try --irqchip=gicv%c instead", v5 ? '5' : '3'); return err; } @@ -152,7 +155,9 @@ static int gic__create_msi_frame(struct kvm *kvm, enum irqchip_type type, case IRQCHIP_GICV2M: return gic__create_gicv2m_frame(kvm, msi_frame_addr); case IRQCHIP_GICV3_ITS: - return gic__create_its_frame(kvm, msi_frame_addr); + return gic__create_its_frame(kvm, msi_frame_addr, false); + case IRQCHIP_GICV5_ITS: + return gic__create_its_frame(kvm, msi_frame_addr, true); default: /* No MSI frame needed */ return 0; } @@ -197,6 +202,7 @@ static int gic__create_device(struct kvm *kvm, enum irqchip_type type) gic_device.type = KVM_DEV_TYPE_ARM_VGIC_V3; dist_attr.attr = KVM_VGIC_V3_ADDR_TYPE_DIST; break; + case IRQCHIP_GICV5_ITS: case IRQCHIP_GICV5: gic_device.type = KVM_DEV_TYPE_ARM_VGIC_V5; break; @@ -220,6 +226,7 @@ static int gic__create_device(struct kvm *kvm, enum irqchip_type type) err = ioctl(gic_fd, KVM_SET_DEVICE_ATTR, &redist_attr); break; case IRQCHIP_GICV5: + case IRQCHIP_GICV5_ITS: err = ioctl(gic_fd, KVM_SET_DEVICE_ATTR, &gicv5_irs_attr); break; case IRQCHIP_AUTO: @@ -229,7 +236,7 @@ static int gic__create_device(struct kvm *kvm, enum irqchip_type type) goto out_err; /* Only set the dist_attr for non-GICv5 */ - if (type != IRQCHIP_GICV5) { + if ((type != IRQCHIP_GICV5) && (type != IRQCHIP_GICV5_ITS)) { err = ioctl(gic_fd, KVM_SET_DEVICE_ATTR, &dist_attr); if (err) goto out_err; @@ -244,7 +251,7 @@ static int gic__create_device(struct kvm *kvm, enum irqchip_type type) * and not at the legacy offset of 32. This must happen before any * interrupts are allocated. */ - if ((type == IRQCHIP_GICV5)) { + if ((type == IRQCHIP_GICV5) || (type == IRQCHIP_GICV5_ITS)) { err = irq__init_irq_offset(0); if (err) goto out_err; @@ -296,7 +303,7 @@ int gic__create(struct kvm *kvm, enum irqchip_type type) switch (type) { case IRQCHIP_AUTO: - for (try = IRQCHIP_GICV5; try >= IRQCHIP_GICV2; try--) { + for (try = IRQCHIP_GICV5_ITS; try >= IRQCHIP_GICV2; try--) { err = gic__create(kvm, try); if (!err) break; @@ -321,6 +328,10 @@ int gic__create(struct kvm *kvm, enum irqchip_type type) gic_redists_base = ARM_GIC_DIST_BASE - gic_redists_size; gic_msi_base = gic_redists_base - gic_msi_size; break; + case IRQCHIP_GICV5_ITS: + gic_msi_base = ARM_GICV5_ITS_BASE; + gic_msi_size = ARM_GICV5_ITS_SIZE; + /* fall through */ case IRQCHIP_GICV5: gicv5_irs_base = ARM_GICV5_IRS_BASE; gicv5_irs_size = ARM_GICV5_IRS_SIZE; @@ -348,7 +359,8 @@ static int gic__init_gic(struct kvm *kvm) u32 maint_irq = GIC_MAINT_IRQ + 16; /* PPI */ u32 nr_irqs; - if ((kvm->cfg.arch.irqchip != IRQCHIP_GICV5)) + if ((kvm->cfg.arch.irqchip != IRQCHIP_GICV5) && + (kvm->cfg.arch.irqchip != IRQCHIP_GICV5_ITS)) nr_irqs = ALIGN(lines, 32) + GIC_SPI_IRQ_BASE; else nr_irqs = roundup_pow_of_two(lines); @@ -525,7 +537,8 @@ u32 gic__get_fdt_irq_cpumask(struct kvm *kvm) /* Only for GICv2 */ if (kvm->cfg.arch.irqchip == IRQCHIP_GICV3 || kvm->cfg.arch.irqchip == IRQCHIP_GICV3_ITS || - kvm->cfg.arch.irqchip == IRQCHIP_GICV5) + kvm->cfg.arch.irqchip == IRQCHIP_GICV5 || + kvm->cfg.arch.irqchip == IRQCHIP_GICV5_ITS) return 0; if (kvm->nrcpus > 8) diff --git a/arm64/include/kvm/gic.h b/arm64/include/kvm/gic.h index 805f4247..0ef3aaa3 100644 --- a/arm64/include/kvm/gic.h +++ b/arm64/include/kvm/gic.h @@ -36,6 +36,7 @@ enum irqchip_type { IRQCHIP_GICV3, IRQCHIP_GICV3_ITS, IRQCHIP_GICV5, + IRQCHIP_GICV5_ITS, }; struct kvm; diff --git a/arm64/pmu.c b/arm64/pmu.c index 1720cc00..42cf81d3 100644 --- a/arm64/pmu.c +++ b/arm64/pmu.c @@ -200,7 +200,8 @@ void pmu__generate_fdt_nodes(void *fdt, struct kvm *kvm) u32 irq_prop[3]; u32 cpu_mask = gic__get_fdt_irq_cpumask(kvm); - if (kvm->cfg.arch.irqchip == IRQCHIP_GICV5) { + if (kvm->cfg.arch.irqchip == IRQCHIP_GICV5 || + kvm->cfg.arch.irqchip == IRQCHIP_GICV5_ITS) { irq_prop[0] = cpu_to_fdt32(GICV5_FDT_IRQ_TYPE_PPI); irq_prop[1] = cpu_to_fdt32(irq); /* For GICv5, encode the full intid by adding the type */ diff --git a/arm64/timer.c b/arm64/timer.c index 0945510d..20a4a808 100644 --- a/arm64/timer.c +++ b/arm64/timer.c @@ -17,7 +17,8 @@ void timer__generate_fdt_nodes(void *fdt, struct kvm *kvm) if (!kvm->cfg.arch.nested_virt) nr--; - if (kvm->cfg.arch.irqchip == IRQCHIP_GICV5) { + if (kvm->cfg.arch.irqchip == IRQCHIP_GICV5 || + kvm->cfg.arch.irqchip == IRQCHIP_GICV5_ITS) { type = GICV5_FDT_IRQ_TYPE_PPI; offset = 16; } else { -- 2.34.1 Now that KVM supports the GICv5 ITS, add in the FDT to describe it to the guest. This ITS node is the MSI controller node used for MSIs, and hence MSIs are now supported. It is marked with PHANDLE_MSI. Signed-off-by: Sascha Bischoff --- arm64/gic.c | 38 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/arm64/gic.c b/arm64/gic.c index 45dd2dab..b32462a8 100644 --- a/arm64/gic.c +++ b/arm64/gic.c @@ -448,6 +448,7 @@ void gic__generate_fdt_nodes(void *fdt, enum irqchip_type type, bool nested, reg_prop[2] = cpu_to_fdt64(gic_redists_base); reg_prop[3] = cpu_to_fdt64(gic_redists_size); break; + case IRQCHIP_GICV5_ITS: case IRQCHIP_GICV5: return gic__generate_gicv5_fdt_nodes(fdt, type, nested, nr_cpus); default: @@ -495,6 +496,16 @@ static void gic__generate_gicv5_fdt_nodes(void *fdt, enum irqchip_type type, cpu_to_fdt64(ARM_GICV5_IRS_SETLPI_SIZE) }; + u64 its_config_reg_prop[] = { + cpu_to_fdt64(ARM_GICV5_ITS_CONFIG_BASE), + cpu_to_fdt64(ARM_GICV5_ITS_CONFIG_SIZE), + }; + + u64 its_trans_reg_prop[] = { + cpu_to_fdt64(ARM_GICV5_ITS_TRANSL_BASE), + cpu_to_fdt64(ARM_GICV5_ITS_TRANSL_SIZE) + }; + _FDT(fdt_begin_node(fdt, "gicv5-cpuif")); _FDT(fdt_property_string(fdt, "compatible", "arm,gic-v5")); _FDT(fdt_property_cell(fdt, "#interrupt-cells", GIC_FDT_IRQ_NUM_CELLS)); @@ -527,6 +538,33 @@ static void gic__generate_gicv5_fdt_nodes(void *fdt, enum irqchip_type type, _FDT(fdt_property(fdt, "cpus", cpus, sizeof(u32) * nr_cpus)); _FDT(fdt_property(fdt, "arm,iaffids", iaffids, sizeof(u16) * nr_cpus)); + if (type == IRQCHIP_GICV5_ITS) { + /* + * GICv5 ITS node + */ + snprintf(node_at_addr, 64, "gicv5-its@%lx", fdt64_to_cpu(its_config_reg_prop[0])); + _FDT(fdt_begin_node(fdt, node_at_addr)); + _FDT(fdt_property_string(fdt, "compatible", "arm,gic-v5-its")); + _FDT(fdt_property_cell(fdt, "#address-cells", 2)); + _FDT(fdt_property_cell(fdt, "#size-cells", 2)); + _FDT(fdt_property(fdt, "ranges", NULL, 0)); + + _FDT(fdt_property(fdt, "reg", its_config_reg_prop, sizeof(its_config_reg_prop))); + _FDT(fdt_property_string(fdt, "reg-names", "ns-config")); + + snprintf(node_at_addr, 64, "msi-controller@%lx", fdt64_to_cpu(its_trans_reg_prop[0])); + _FDT(fdt_begin_node(fdt, node_at_addr)); + _FDT(fdt_property_cell(fdt, "phandle", PHANDLE_MSI)); + _FDT(fdt_property(fdt, "msi-controller", NULL, 0)); + + _FDT(fdt_property(fdt, "reg", its_trans_reg_prop, sizeof(its_trans_reg_prop))); + _FDT(fdt_property_string(fdt, "reg-names", "ns-translate")); + + _FDT(fdt_end_node(fdt)); // End of ITS msi-controller node + + _FDT(fdt_end_node(fdt)); // End of ITS node + } + _FDT(fdt_end_node(fdt)); // End of IRS node _FDT(fdt_end_node(fdt)); // End of GIC node -- 2.34.1 Now that GICv5's ITS is supported, point the MSI-generating devices to PHANDLE_MSI for the GICv5 ITS config. Signed-off-by: Sascha Bischoff --- arm64/pci.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/arm64/pci.c b/arm64/pci.c index 79ca34e8..aacaf6b6 100644 --- a/arm64/pci.c +++ b/arm64/pci.c @@ -70,7 +70,8 @@ void pci__generate_fdt_nodes(void *fdt, struct kvm *kvm) _FDT(fdt_property(fdt, "reg", &cfg_reg_prop, sizeof(cfg_reg_prop))); _FDT(fdt_property(fdt, "ranges", ranges, sizeof(ranges))); - if (irqchip == IRQCHIP_GICV2M || irqchip == IRQCHIP_GICV3_ITS) + if (irqchip == IRQCHIP_GICV2M || irqchip == IRQCHIP_GICV3_ITS || + irqchip == IRQCHIP_GICV5_ITS) _FDT(fdt_property_cell(fdt, "msi-parent", PHANDLE_MSI)); /* Generate the interrupt map ... */ -- 2.34.1