Reviewed-by: Eric Auger <eric.auger@xxxxxxxxxx> On 05/29/2015 11:53 AM, Andre Przywara wrote: > Add emulation for some basic MMIO registers used in the ITS emulation. > This includes: > - GITS_{CTLR,TYPER,IIDR} > - ID registers > - GITS_{CBASER,CREAD,CWRITER} CREADR > 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; > } > -- 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