Add emulation for some basic MMIO registers used in the ITS emulation. This includes: - GITS_{CTLR,TYPER,IIDR} - ID registers - GITS_{CBASER,CREAD,CWRITER} those implement the ITS command buffer handling Signed-off-by: Andre Przywara <andre.przywara@xxxxxxx> --- include/kvm/arm_vgic.h | 3 + include/linux/irqchip/arm-gic-v3.h | 8 ++ virt/kvm/arm/its-emul.c | 172 +++++++++++++++++++++++++++++++++++++ virt/kvm/arm/its-emul.h | 1 + virt/kvm/arm/vgic-v3-emul.c | 2 + 5 files changed, 186 insertions(+) diff --git a/include/kvm/arm_vgic.h b/include/kvm/arm_vgic.h index d76c2d9..3b8e3a1 100644 --- a/include/kvm/arm_vgic.h +++ b/include/kvm/arm_vgic.h @@ -159,6 +159,9 @@ struct vgic_io_device { struct vgic_its { bool enabled; spinlock_t lock; + u64 cbaser; + int creadr; + int cwriter; }; struct vgic_dist { diff --git a/include/linux/irqchip/arm-gic-v3.h b/include/linux/irqchip/arm-gic-v3.h index df4e527..0b450c7 100644 --- a/include/linux/irqchip/arm-gic-v3.h +++ b/include/linux/irqchip/arm-gic-v3.h @@ -179,15 +179,23 @@ #define GITS_BASER 0x0100 #define GITS_IDREGS_BASE 0xffd0 #define GITS_PIDR2 GICR_PIDR2 +#define GITS_PIDR4 0xffd0 +#define GITS_CIDR0 0xfff0 +#define GITS_CIDR1 0xfff4 +#define GITS_CIDR2 0xfff8 +#define GITS_CIDR3 0xfffc #define GITS_TRANSLATER 0x10040 #define GITS_CTLR_ENABLE (1U << 0) #define GITS_CTLR_QUIESCENT (1U << 31) +#define GITS_TYPER_PLPIS (1UL << 0) +#define GITS_TYPER_IDBITS_SHIFT 8 #define GITS_TYPER_DEVBITS_SHIFT 13 #define GITS_TYPER_DEVBITS(r) ((((r) >> GITS_TYPER_DEVBITS_SHIFT) & 0x1f) + 1) #define GITS_TYPER_PTA (1UL << 19) +#define GITS_TYPER_HWCOLLCNT_SHIFT 24 #define GITS_CBASER_VALID (1UL << 63) #define GITS_CBASER_nCnB (0UL << 59) diff --git a/virt/kvm/arm/its-emul.c b/virt/kvm/arm/its-emul.c index 7b283ce..82bc34a 100644 --- a/virt/kvm/arm/its-emul.c +++ b/virt/kvm/arm/its-emul.c @@ -32,10 +32,62 @@ #include "vgic.h" #include "its-emul.h" +#define BASER_BASE_ADDRESS(x) ((x) & 0xfffffffff000ULL) + +/* distributor lock is hold by the VGIC MMIO handler */ static bool handle_mmio_misc_gits(struct kvm_vcpu *vcpu, struct kvm_exit_mmio *mmio, phys_addr_t offset) { + struct vgic_its *its = &vcpu->kvm->arch.vgic.its; + u32 reg; + bool was_enabled; + + switch (offset & ~3) { + case 0x00: /* GITS_CTLR */ + /* We never defer any command execution. */ + reg = GITS_CTLR_QUIESCENT; + if (its->enabled) + reg |= GITS_CTLR_ENABLE; + was_enabled = its->enabled; + vgic_reg_access(mmio, ®, offset & 3, + ACCESS_READ_VALUE | ACCESS_WRITE_VALUE); + its->enabled = !!(reg & GITS_CTLR_ENABLE); + return !was_enabled && its->enabled; + case 0x04: /* GITS_IIDR */ + reg = (PRODUCT_ID_KVM << 24) | (IMPLEMENTER_ARM << 0); + vgic_reg_access(mmio, ®, offset & 3, + ACCESS_READ_VALUE | ACCESS_WRITE_IGNORED); + break; + case 0x08: /* GITS_TYPER */ + /* + * We use linear CPU numbers for redistributor addressing, + * so GITS_TYPER.PTA is 0. + * To avoid memory waste on the guest side, we keep the + * number of IDBits and DevBits low for the time being. + * This could later be made configurable by userland. + * Since we have all collections in linked list, we claim + * that we can hold all of the collection tables in our + * own memory and that the ITT entry size is 1 byte (the + * smallest possible one). + */ + reg = GITS_TYPER_PLPIS; + reg |= 0xff << GITS_TYPER_HWCOLLCNT_SHIFT; + reg |= 0x0f << GITS_TYPER_DEVBITS_SHIFT; + reg |= 0x0f << GITS_TYPER_IDBITS_SHIFT; + vgic_reg_access(mmio, ®, offset & 3, + ACCESS_READ_VALUE | ACCESS_WRITE_IGNORED); + break; + case 0x0c: + /* The upper 32bits of TYPER are all 0 for the time being. + * Should we need more than 256 collections, we can enable + * some bits in here. + */ + vgic_reg_access(mmio, NULL, offset & 3, + ACCESS_READ_RAZ | ACCESS_WRITE_IGNORED); + break; + } + return false; } @@ -43,20 +95,107 @@ static bool handle_mmio_gits_idregs(struct kvm_vcpu *vcpu, struct kvm_exit_mmio *mmio, phys_addr_t offset) { + u32 reg = 0; + int idreg = (offset & ~3) + GITS_IDREGS_BASE; + + switch (idreg) { + case GITS_PIDR2: + reg = GIC_PIDR2_ARCH_GICv3; + break; + case GITS_PIDR4: + /* This is a 64K software visible page */ + reg = 0x40; + break; + /* Those are the ID registers for (any) GIC. */ + case GITS_CIDR0: + reg = 0x0d; + break; + case GITS_CIDR1: + reg = 0xf0; + break; + case GITS_CIDR2: + reg = 0x05; + break; + case GITS_CIDR3: + reg = 0xb1; + break; + } + vgic_reg_access(mmio, ®, offset & 3, + ACCESS_READ_VALUE | ACCESS_WRITE_IGNORED); return false; } +static int vits_handle_command(struct kvm_vcpu *vcpu, u64 *its_cmd) +{ + return -ENODEV; +} + static bool handle_mmio_gits_cbaser(struct kvm_vcpu *vcpu, struct kvm_exit_mmio *mmio, phys_addr_t offset) { + struct vgic_its *its = &vcpu->kvm->arch.vgic.its; + int mode = ACCESS_READ_VALUE; + + mode |= its->enabled ? ACCESS_WRITE_IGNORED : ACCESS_WRITE_VALUE; + + vgic_handle_base_register(vcpu, mmio, offset, &its->cbaser, mode); + if (mmio->is_write) + its->creadr = 0; return false; } +static int its_cmd_buffer_size(struct kvm *kvm) +{ + struct vgic_its *its = &kvm->arch.vgic.its; + + return ((its->cbaser & 0xff) + 1) << 12; +} + +static gpa_t its_cmd_buffer_base(struct kvm *kvm) +{ + struct vgic_its *its = &kvm->arch.vgic.its; + + return BASER_BASE_ADDRESS(its->cbaser); +} + static bool handle_mmio_gits_cwriter(struct kvm_vcpu *vcpu, struct kvm_exit_mmio *mmio, phys_addr_t offset) { + struct vgic_its *its = &vcpu->kvm->arch.vgic.its; + u64 cmd_buf[4]; + gpa_t cbaser = its_cmd_buffer_base(vcpu->kvm); + u32 reg; + int ret; + + spin_lock(&its->lock); + switch (offset & ~3) { + case 0x00: + reg = its->cwriter & 0xfffe0; + vgic_reg_access(mmio, ®, offset & 3, + ACCESS_READ_VALUE | ACCESS_WRITE_VALUE); + reg &= 0xfffe0; + if (reg > its_cmd_buffer_size(vcpu->kvm)) + break; + while (its->creadr != reg) { + ret = kvm_read_guest(vcpu->kvm, cbaser + its->creadr, + cmd_buf, 32); + if (ret) + break; + vits_handle_command(vcpu, cmd_buf); + its->creadr += 32; + if (its->creadr == its_cmd_buffer_size(vcpu->kvm)) + its->creadr = 0; + } + its->cwriter = reg; + break; + case 0x04: + vgic_reg_access(mmio, ®, offset & 3, + ACCESS_READ_RAZ | ACCESS_WRITE_IGNORED); + break; + } + spin_unlock(&its->lock); return false; } @@ -64,6 +203,22 @@ static bool handle_mmio_gits_creadr(struct kvm_vcpu *vcpu, struct kvm_exit_mmio *mmio, phys_addr_t offset) { + struct vgic_its *its = &vcpu->kvm->arch.vgic.its; + u32 reg; + + spin_lock(&its->lock); + switch (offset & ~3) { + case 0x00: + reg = its->creadr & 0xfffe0; + vgic_reg_access(mmio, ®, offset & 3, + ACCESS_READ_VALUE | ACCESS_WRITE_IGNORED); + break; + case 0x04: + vgic_reg_access(mmio, ®, offset & 3, + ACCESS_READ_RAZ | ACCESS_WRITE_IGNORED); + break; + } + spin_unlock(&its->lock); return false; } @@ -119,9 +274,26 @@ int vits_init(struct kvm *kvm) if (IS_VGIC_ADDR_UNDEF(dist->vgic_its_base)) return -ENXIO; + dist->pendbaser = kmalloc(sizeof(u64) * dist->nr_cpus, GFP_KERNEL); + if (!dist->pendbaser) + return -ENOMEM; + spin_lock_init(&its->lock); its->enabled = false; return -ENXIO; } + +void vits_destroy(struct kvm *kvm) +{ + struct vgic_dist *dist = &kvm->arch.vgic; + struct vgic_its *its = &dist->its; + + if (!vgic_has_its(kvm)) + return; + + kfree(dist->pendbaser); + + its->enabled = false; +} diff --git a/virt/kvm/arm/its-emul.h b/virt/kvm/arm/its-emul.h index 5dc8e2f..472a6d0 100644 --- a/virt/kvm/arm/its-emul.h +++ b/virt/kvm/arm/its-emul.h @@ -31,5 +31,6 @@ void vgic_enable_lpis(struct kvm_vcpu *vcpu); int vits_init(struct kvm *kvm); +void vits_destroy(struct kvm *kvm); #endif diff --git a/virt/kvm/arm/vgic-v3-emul.c b/virt/kvm/arm/vgic-v3-emul.c index 4e40684..fa81c4b 100644 --- a/virt/kvm/arm/vgic-v3-emul.c +++ b/virt/kvm/arm/vgic-v3-emul.c @@ -881,6 +881,8 @@ static void vgic_v3_destroy_model(struct kvm *kvm) { struct vgic_dist *dist = &kvm->arch.vgic; + vits_destroy(kvm); + kfree(dist->irq_spi_mpidr); dist->irq_spi_mpidr = NULL; } -- 2.3.5 -- To unsubscribe from this list: send the line "unsubscribe kvm" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html