diff --git a/xen/arch/arm/include/asm/new_vgic.h b/xen/arch/arm/include/asm/new_vgic.h index 18ed3f754a..1e76213893 100644 --- a/xen/arch/arm/include/asm/new_vgic.h +++ b/xen/arch/arm/include/asm/new_vgic.h @@ -127,6 +127,8 @@ struct vgic_dist { paddr_t vgic_redist_free_offset; }; }; + paddr_t csize; /* CPU interface size */ + paddr_t vbase; /* virtual CPU interface base address */ /* distributor enabled */ bool enabled; diff --git a/xen/arch/arm/include/asm/vgic.h b/xen/arch/arm/include/asm/vgic.h index 3d44868039..328fd46d1b 100644 --- a/xen/arch/arm/include/asm/vgic.h +++ b/xen/arch/arm/include/asm/vgic.h @@ -153,6 +153,8 @@ struct vgic_dist { /* Base address for guest GIC */ paddr_t dbase; /* Distributor base address */ paddr_t cbase; /* CPU interface base address */ + paddr_t csize; /* CPU interface size */ + paddr_t vbase; /* Virtual CPU interface base address */ #ifdef CONFIG_GICV3 /* GIC V3 addressing */ /* List of contiguous occupied by the redistributors */ diff --git a/xen/arch/arm/traps.c b/xen/arch/arm/traps.c index 061c92acbd..d40c331a4e 100644 --- a/xen/arch/arm/traps.c +++ b/xen/arch/arm/traps.c @@ -1787,9 +1787,12 @@ static inline bool hpfar_is_valid(bool s1ptw, uint8_t fsc) } /* - * When using ACPI, most of the MMIO regions will be mapped on-demand - * in stage-2 page tables for the hardware domain because Xen is not - * able to know from the EFI memory map the MMIO regions. + * Try to map the MMIO regions for some special cases: + * 1. When using ACPI, most of the MMIO regions will be mapped on-demand + * in stage-2 page tables for the hardware domain because Xen is not + * able to know from the EFI memory map the MMIO regions. + * 2. For guests using GICv2, the GICv2 CPU interface mapping is created + * on the first access of the MMIO region. */ static bool try_map_mmio(gfn_t gfn) { @@ -1798,6 +1801,16 @@ static bool try_map_mmio(gfn_t gfn) /* For the hardware domain, all MMIOs are mapped with GFN == MFN */ mfn_t mfn = _mfn(gfn_x(gfn)); + /* + * Map the GICv2 virtual CPU interface in the GIC CPU interface + * region of the guest on the first access of the MMIO region. + */ + if ( d->arch.vgic.version == GIC_V2 && + gfn_to_gaddr(gfn) >= d->arch.vgic.cbase && + (gfn_to_gaddr(gfn) - d->arch.vgic.cbase) < d->arch.vgic.csize ) + return !map_mmio_regions(d, gfn, d->arch.vgic.csize / PAGE_SIZE, + maddr_to_mfn(d->arch.vgic.vbase)); + /* * Device-Tree should already have everything mapped when building * the hardware domain. diff --git a/xen/arch/arm/vgic-v2.c b/xen/arch/arm/vgic-v2.c index 0026cb4360..0b083c33e6 100644 --- a/xen/arch/arm/vgic-v2.c +++ b/xen/arch/arm/vgic-v2.c @@ -644,10 +644,6 @@ static int vgic_v2_vcpu_init(struct vcpu *v) static int vgic_v2_domain_init(struct domain *d) { - int ret; - paddr_t csize; - paddr_t vbase; - /* * The hardware domain and direct-mapped domain both get the hardware * address. @@ -667,8 +663,8 @@ static int vgic_v2_domain_init(struct domain *d) * aligned to PAGE_SIZE. */ d->arch.vgic.cbase = vgic_v2_hw.cbase; - csize = vgic_v2_hw.csize; - vbase = vgic_v2_hw.vbase; + d->arch.vgic.csize = vgic_v2_hw.csize; + d->arch.vgic.vbase = vgic_v2_hw.vbase; } else if ( is_domain_direct_mapped(d) ) { @@ -683,8 +679,8 @@ static int vgic_v2_domain_init(struct domain *d) */ d->arch.vgic.dbase = vgic_v2_hw.dbase; d->arch.vgic.cbase = vgic_v2_hw.cbase; - csize = GUEST_GICC_SIZE; - vbase = vgic_v2_hw.vbase + vgic_v2_hw.aliased_offset; + d->arch.vgic.csize = GUEST_GICC_SIZE; + d->arch.vgic.vbase = vgic_v2_hw.vbase + vgic_v2_hw.aliased_offset; } else { @@ -697,18 +693,11 @@ static int vgic_v2_domain_init(struct domain *d) */ BUILD_BUG_ON(GUEST_GICC_SIZE != SZ_8K); d->arch.vgic.cbase = GUEST_GICC_BASE; - csize = GUEST_GICC_SIZE; - vbase = vgic_v2_hw.vbase + vgic_v2_hw.aliased_offset; + d->arch.vgic.csize = GUEST_GICC_SIZE; + d->arch.vgic.vbase = vgic_v2_hw.vbase + vgic_v2_hw.aliased_offset; } - /* - * Map the gic virtual cpu interface in the gic cpu interface - * region of the guest. - */ - ret = map_mmio_regions(d, gaddr_to_gfn(d->arch.vgic.cbase), - csize / PAGE_SIZE, maddr_to_mfn(vbase)); - if ( ret ) - return ret; + /* Mapping of the virtual CPU interface is deferred until first access */ register_mmio_handler(d, &vgic_v2_distr_mmio_handler, d->arch.vgic.dbase, PAGE_SIZE, NULL); diff --git a/xen/arch/arm/vgic/vgic-v2.c b/xen/arch/arm/vgic/vgic-v2.c index bd51ef95c8..6a558089c5 100644 --- a/xen/arch/arm/vgic/vgic-v2.c +++ b/xen/arch/arm/vgic/vgic-v2.c @@ -258,8 +258,6 @@ void vgic_v2_enable(struct vcpu *vcpu) int vgic_v2_map_resources(struct domain *d) { struct vgic_dist *dist = &d->arch.vgic; - paddr_t csize; - paddr_t vbase; int ret; /* @@ -272,7 +270,7 @@ int vgic_v2_map_resources(struct domain *d) */ if ( is_hardware_domain(d) ) { - d->arch.vgic.dbase = gic_v2_hw_data.dbase; + dist->dbase = gic_v2_hw_data.dbase; /* * For the hardware domain, we always map the whole HW CPU * interface region in order to match the device tree (the "reg" @@ -280,13 +278,13 @@ int vgic_v2_map_resources(struct domain *d) * Note that we assume the size of the CPU interface is always * aligned to PAGE_SIZE. */ - d->arch.vgic.cbase = gic_v2_hw_data.cbase; - csize = gic_v2_hw_data.csize; - vbase = gic_v2_hw_data.vbase; + dist->cbase = gic_v2_hw_data.cbase; + dist->csize = gic_v2_hw_data.csize; + dist->vbase = gic_v2_hw_data.vbase; } else if ( is_domain_direct_mapped(d) ) { - d->arch.vgic.dbase = gic_v2_hw_data.dbase; + dist->dbase = gic_v2_hw_data.dbase; /* * For all the direct-mapped domain other than the hardware domain, * we only map a 8kB CPU interface but we make sure it is at a location @@ -296,13 +294,13 @@ int vgic_v2_map_resources(struct domain *d) * address when the GIC is aliased to get a 8kB contiguous * region. */ - d->arch.vgic.cbase = gic_v2_hw_data.cbase; - csize = GUEST_GICC_SIZE; - vbase = gic_v2_hw_data.vbase + gic_v2_hw_data.aliased_offset; + dist->cbase = gic_v2_hw_data.cbase; + dist->csize = GUEST_GICC_SIZE; + dist->vbase = gic_v2_hw_data.vbase + gic_v2_hw_data.aliased_offset; } else { - d->arch.vgic.dbase = GUEST_GICD_BASE; + dist->dbase = GUEST_GICD_BASE; /* * The CPU interface exposed to the guest is always 8kB. We may * need to add an offset to the virtual CPU interface base @@ -310,9 +308,9 @@ int vgic_v2_map_resources(struct domain *d) * region. */ BUILD_BUG_ON(GUEST_GICC_SIZE != SZ_8K); - d->arch.vgic.cbase = GUEST_GICC_BASE; - csize = GUEST_GICC_SIZE; - vbase = gic_v2_hw_data.vbase + gic_v2_hw_data.aliased_offset; + dist->cbase = GUEST_GICC_BASE; + dist->csize = GUEST_GICC_SIZE; + dist->vbase = gic_v2_hw_data.vbase + gic_v2_hw_data.aliased_offset; } @@ -323,17 +321,7 @@ int vgic_v2_map_resources(struct domain *d) return ret; } - /* - * Map the gic virtual cpu interface in the gic cpu interface - * region of the guest. - */ - ret = map_mmio_regions(d, gaddr_to_gfn(d->arch.vgic.cbase), - csize / PAGE_SIZE, maddr_to_mfn(vbase)); - if ( ret ) - { - gdprintk(XENLOG_ERR, "Unable to remap VGIC CPU to VCPU\n"); - return ret; - } + /* Mapping of the virtual CPU interface is deferred until first access */ dist->ready = true;