mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git
synced 2024-08-21 16:29:59 +00:00
arm/arm64: KVM: refactor MMIO accessors
The MMIO accessors for GICD_I[CS]ENABLER, GICD_I[CS]PENDR and GICD_ICFGR behave very similar for GICv2 and GICv3, although the way the affected VCPU is determined differs. Since we need them to access the registers from three different places in the future, we factor out a generic, backend-facing implementation and use small wrappers in the current GICv2 emulation. This will ease adding GICv3 accessors later. Signed-off-by: Andre Przywara <andre.przywara@arm.com> Reviewed-by: Christoffer Dall <christoffer.dall@linaro.org> Signed-off-by: Christoffer Dall <christoffer.dall@linaro.org>
This commit is contained in:
parent
2f5fa41a7a
commit
d97f683d0f
1 changed files with 74 additions and 52 deletions
|
@ -492,64 +492,66 @@ static bool handle_mmio_raz_wi(struct kvm_vcpu *vcpu,
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool handle_mmio_set_enable_reg(struct kvm_vcpu *vcpu,
|
static bool vgic_handle_enable_reg(struct kvm *kvm, struct kvm_exit_mmio *mmio,
|
||||||
struct kvm_exit_mmio *mmio,
|
phys_addr_t offset, int vcpu_id, int access)
|
||||||
phys_addr_t offset)
|
|
||||||
{
|
{
|
||||||
u32 *reg = vgic_bitmap_get_reg(&vcpu->kvm->arch.vgic.irq_enabled,
|
u32 *reg;
|
||||||
vcpu->vcpu_id, offset);
|
int mode = ACCESS_READ_VALUE | access;
|
||||||
vgic_reg_access(mmio, reg, offset,
|
struct kvm_vcpu *target_vcpu = kvm_get_vcpu(kvm, vcpu_id);
|
||||||
ACCESS_READ_VALUE | ACCESS_WRITE_SETBIT);
|
|
||||||
|
reg = vgic_bitmap_get_reg(&kvm->arch.vgic.irq_enabled, vcpu_id, offset);
|
||||||
|
vgic_reg_access(mmio, reg, offset, mode);
|
||||||
if (mmio->is_write) {
|
if (mmio->is_write) {
|
||||||
vgic_update_state(vcpu->kvm);
|
if (access & ACCESS_WRITE_CLEARBIT) {
|
||||||
|
if (offset < 4) /* Force SGI enabled */
|
||||||
|
*reg |= 0xffff;
|
||||||
|
vgic_retire_disabled_irqs(target_vcpu);
|
||||||
|
}
|
||||||
|
vgic_update_state(kvm);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static bool handle_mmio_set_enable_reg(struct kvm_vcpu *vcpu,
|
||||||
|
struct kvm_exit_mmio *mmio,
|
||||||
|
phys_addr_t offset)
|
||||||
|
{
|
||||||
|
return vgic_handle_enable_reg(vcpu->kvm, mmio, offset,
|
||||||
|
vcpu->vcpu_id, ACCESS_WRITE_SETBIT);
|
||||||
|
}
|
||||||
|
|
||||||
static bool handle_mmio_clear_enable_reg(struct kvm_vcpu *vcpu,
|
static bool handle_mmio_clear_enable_reg(struct kvm_vcpu *vcpu,
|
||||||
struct kvm_exit_mmio *mmio,
|
struct kvm_exit_mmio *mmio,
|
||||||
phys_addr_t offset)
|
phys_addr_t offset)
|
||||||
{
|
{
|
||||||
u32 *reg = vgic_bitmap_get_reg(&vcpu->kvm->arch.vgic.irq_enabled,
|
return vgic_handle_enable_reg(vcpu->kvm, mmio, offset,
|
||||||
vcpu->vcpu_id, offset);
|
vcpu->vcpu_id, ACCESS_WRITE_CLEARBIT);
|
||||||
vgic_reg_access(mmio, reg, offset,
|
|
||||||
ACCESS_READ_VALUE | ACCESS_WRITE_CLEARBIT);
|
|
||||||
if (mmio->is_write) {
|
|
||||||
if (offset < 4) /* Force SGI enabled */
|
|
||||||
*reg |= 0xffff;
|
|
||||||
vgic_retire_disabled_irqs(vcpu);
|
|
||||||
vgic_update_state(vcpu->kvm);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool handle_mmio_set_pending_reg(struct kvm_vcpu *vcpu,
|
static bool vgic_handle_set_pending_reg(struct kvm *kvm,
|
||||||
struct kvm_exit_mmio *mmio,
|
struct kvm_exit_mmio *mmio,
|
||||||
phys_addr_t offset)
|
phys_addr_t offset, int vcpu_id)
|
||||||
{
|
{
|
||||||
u32 *reg, orig;
|
u32 *reg, orig;
|
||||||
u32 level_mask;
|
u32 level_mask;
|
||||||
struct vgic_dist *dist = &vcpu->kvm->arch.vgic;
|
int mode = ACCESS_READ_VALUE | ACCESS_WRITE_SETBIT;
|
||||||
|
struct vgic_dist *dist = &kvm->arch.vgic;
|
||||||
|
|
||||||
reg = vgic_bitmap_get_reg(&dist->irq_cfg, vcpu->vcpu_id, offset);
|
reg = vgic_bitmap_get_reg(&dist->irq_cfg, vcpu_id, offset);
|
||||||
level_mask = (~(*reg));
|
level_mask = (~(*reg));
|
||||||
|
|
||||||
/* Mark both level and edge triggered irqs as pending */
|
/* Mark both level and edge triggered irqs as pending */
|
||||||
reg = vgic_bitmap_get_reg(&dist->irq_pending, vcpu->vcpu_id, offset);
|
reg = vgic_bitmap_get_reg(&dist->irq_pending, vcpu_id, offset);
|
||||||
orig = *reg;
|
orig = *reg;
|
||||||
vgic_reg_access(mmio, reg, offset,
|
vgic_reg_access(mmio, reg, offset, mode);
|
||||||
ACCESS_READ_VALUE | ACCESS_WRITE_SETBIT);
|
|
||||||
|
|
||||||
if (mmio->is_write) {
|
if (mmio->is_write) {
|
||||||
/* Set the soft-pending flag only for level-triggered irqs */
|
/* Set the soft-pending flag only for level-triggered irqs */
|
||||||
reg = vgic_bitmap_get_reg(&dist->irq_soft_pend,
|
reg = vgic_bitmap_get_reg(&dist->irq_soft_pend,
|
||||||
vcpu->vcpu_id, offset);
|
vcpu_id, offset);
|
||||||
vgic_reg_access(mmio, reg, offset,
|
vgic_reg_access(mmio, reg, offset, mode);
|
||||||
ACCESS_READ_VALUE | ACCESS_WRITE_SETBIT);
|
|
||||||
*reg &= level_mask;
|
*reg &= level_mask;
|
||||||
|
|
||||||
/* Ignore writes to SGIs */
|
/* Ignore writes to SGIs */
|
||||||
|
@ -558,31 +560,30 @@ static bool handle_mmio_set_pending_reg(struct kvm_vcpu *vcpu,
|
||||||
*reg |= orig & 0xffff;
|
*reg |= orig & 0xffff;
|
||||||
}
|
}
|
||||||
|
|
||||||
vgic_update_state(vcpu->kvm);
|
vgic_update_state(kvm);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool handle_mmio_clear_pending_reg(struct kvm_vcpu *vcpu,
|
static bool vgic_handle_clear_pending_reg(struct kvm *kvm,
|
||||||
struct kvm_exit_mmio *mmio,
|
struct kvm_exit_mmio *mmio,
|
||||||
phys_addr_t offset)
|
phys_addr_t offset, int vcpu_id)
|
||||||
{
|
{
|
||||||
u32 *level_active;
|
u32 *level_active;
|
||||||
u32 *reg, orig;
|
u32 *reg, orig;
|
||||||
struct vgic_dist *dist = &vcpu->kvm->arch.vgic;
|
int mode = ACCESS_READ_VALUE | ACCESS_WRITE_CLEARBIT;
|
||||||
|
struct vgic_dist *dist = &kvm->arch.vgic;
|
||||||
|
|
||||||
reg = vgic_bitmap_get_reg(&dist->irq_pending, vcpu->vcpu_id, offset);
|
reg = vgic_bitmap_get_reg(&dist->irq_pending, vcpu_id, offset);
|
||||||
orig = *reg;
|
orig = *reg;
|
||||||
vgic_reg_access(mmio, reg, offset,
|
vgic_reg_access(mmio, reg, offset, mode);
|
||||||
ACCESS_READ_VALUE | ACCESS_WRITE_CLEARBIT);
|
|
||||||
if (mmio->is_write) {
|
if (mmio->is_write) {
|
||||||
/* Re-set level triggered level-active interrupts */
|
/* Re-set level triggered level-active interrupts */
|
||||||
level_active = vgic_bitmap_get_reg(&dist->irq_level,
|
level_active = vgic_bitmap_get_reg(&dist->irq_level,
|
||||||
vcpu->vcpu_id, offset);
|
vcpu_id, offset);
|
||||||
reg = vgic_bitmap_get_reg(&dist->irq_pending,
|
reg = vgic_bitmap_get_reg(&dist->irq_pending, vcpu_id, offset);
|
||||||
vcpu->vcpu_id, offset);
|
|
||||||
*reg |= *level_active;
|
*reg |= *level_active;
|
||||||
|
|
||||||
/* Ignore writes to SGIs */
|
/* Ignore writes to SGIs */
|
||||||
|
@ -593,17 +594,31 @@ static bool handle_mmio_clear_pending_reg(struct kvm_vcpu *vcpu,
|
||||||
|
|
||||||
/* Clear soft-pending flags */
|
/* Clear soft-pending flags */
|
||||||
reg = vgic_bitmap_get_reg(&dist->irq_soft_pend,
|
reg = vgic_bitmap_get_reg(&dist->irq_soft_pend,
|
||||||
vcpu->vcpu_id, offset);
|
vcpu_id, offset);
|
||||||
vgic_reg_access(mmio, reg, offset,
|
vgic_reg_access(mmio, reg, offset, mode);
|
||||||
ACCESS_READ_VALUE | ACCESS_WRITE_CLEARBIT);
|
|
||||||
|
|
||||||
vgic_update_state(vcpu->kvm);
|
vgic_update_state(kvm);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static bool handle_mmio_set_pending_reg(struct kvm_vcpu *vcpu,
|
||||||
|
struct kvm_exit_mmio *mmio,
|
||||||
|
phys_addr_t offset)
|
||||||
|
{
|
||||||
|
return vgic_handle_set_pending_reg(vcpu->kvm, mmio, offset,
|
||||||
|
vcpu->vcpu_id);
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool handle_mmio_clear_pending_reg(struct kvm_vcpu *vcpu,
|
||||||
|
struct kvm_exit_mmio *mmio,
|
||||||
|
phys_addr_t offset)
|
||||||
|
{
|
||||||
|
return vgic_handle_clear_pending_reg(vcpu->kvm, mmio, offset,
|
||||||
|
vcpu->vcpu_id);
|
||||||
|
}
|
||||||
|
|
||||||
static bool handle_mmio_priority_reg(struct kvm_vcpu *vcpu,
|
static bool handle_mmio_priority_reg(struct kvm_vcpu *vcpu,
|
||||||
struct kvm_exit_mmio *mmio,
|
struct kvm_exit_mmio *mmio,
|
||||||
phys_addr_t offset)
|
phys_addr_t offset)
|
||||||
|
@ -726,14 +741,10 @@ static u16 vgic_cfg_compress(u32 val)
|
||||||
* LSB is always 0. As such, we only keep the upper bit, and use the
|
* LSB is always 0. As such, we only keep the upper bit, and use the
|
||||||
* two above functions to compress/expand the bits
|
* two above functions to compress/expand the bits
|
||||||
*/
|
*/
|
||||||
static bool handle_mmio_cfg_reg(struct kvm_vcpu *vcpu,
|
static bool vgic_handle_cfg_reg(u32 *reg, struct kvm_exit_mmio *mmio,
|
||||||
struct kvm_exit_mmio *mmio, phys_addr_t offset)
|
phys_addr_t offset)
|
||||||
{
|
{
|
||||||
u32 val;
|
u32 val;
|
||||||
u32 *reg;
|
|
||||||
|
|
||||||
reg = vgic_bitmap_get_reg(&vcpu->kvm->arch.vgic.irq_cfg,
|
|
||||||
vcpu->vcpu_id, offset >> 1);
|
|
||||||
|
|
||||||
if (offset & 4)
|
if (offset & 4)
|
||||||
val = *reg >> 16;
|
val = *reg >> 16;
|
||||||
|
@ -762,6 +773,17 @@ static bool handle_mmio_cfg_reg(struct kvm_vcpu *vcpu,
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static bool handle_mmio_cfg_reg(struct kvm_vcpu *vcpu,
|
||||||
|
struct kvm_exit_mmio *mmio, phys_addr_t offset)
|
||||||
|
{
|
||||||
|
u32 *reg;
|
||||||
|
|
||||||
|
reg = vgic_bitmap_get_reg(&vcpu->kvm->arch.vgic.irq_cfg,
|
||||||
|
vcpu->vcpu_id, offset >> 1);
|
||||||
|
|
||||||
|
return vgic_handle_cfg_reg(reg, mmio, offset);
|
||||||
|
}
|
||||||
|
|
||||||
static bool handle_mmio_sgi_reg(struct kvm_vcpu *vcpu,
|
static bool handle_mmio_sgi_reg(struct kvm_vcpu *vcpu,
|
||||||
struct kvm_exit_mmio *mmio, phys_addr_t offset)
|
struct kvm_exit_mmio *mmio, phys_addr_t offset)
|
||||||
{
|
{
|
||||||
|
|
Loading…
Reference in a new issue