Skip to content

Commit

Permalink
KVM: arm/arm64: rework MMIO abort handling to use KVM MMIO bus
Browse files Browse the repository at this point in the history
Currently we have struct kvm_exit_mmio for encapsulating MMIO abort
data to be passed on from syndrome decoding all the way down to the
VGIC register handlers. Now as we switch the MMIO handling to be
routed through the KVM MMIO bus, it does not make sense anymore to
use that structure already from the beginning. So we keep the data in
local variables until we put them into the kvm_io_bus framework.
Then we fill kvm_exit_mmio in the VGIC only, making it a VGIC private
structure. On that way we replace the data buffer in that structure
with a pointer pointing to a single location in a local variable, so
we get rid of some copying on the way.
With all of the virtual GIC emulation code now being registered with
the kvm_io_bus, we can remove all of the old MMIO handling code and
its dispatching functionality.

I didn't bother to rename kvm_exit_mmio (to vgic_mmio or something),
because that touches a lot of code lines without any good reason.

This is based on an original patch by Nikolay.

Signed-off-by: Andre Przywara <[email protected]>
Cc: Nikolay Nikolaev <[email protected]>
Reviewed-by: Marc Zyngier <[email protected]>
Signed-off-by: Marc Zyngier <[email protected]>
  • Loading branch information
Andre-ARM authored and Marc Zyngier committed Mar 30, 2015
1 parent fb8f61a commit 950324a
Show file tree
Hide file tree
Showing 8 changed files with 55 additions and 221 deletions.
22 changes: 0 additions & 22 deletions arch/arm/include/asm/kvm_mmio.h
Original file line number Diff line number Diff line change
Expand Up @@ -28,28 +28,6 @@ struct kvm_decode {
bool sign_extend;
};

/*
* The in-kernel MMIO emulation code wants to use a copy of run->mmio,
* which is an anonymous type. Use our own type instead.
*/
struct kvm_exit_mmio {
phys_addr_t phys_addr;
u8 data[8];
u32 len;
bool is_write;
void *private;
};

static inline void kvm_prepare_mmio(struct kvm_run *run,
struct kvm_exit_mmio *mmio)
{
run->mmio.phys_addr = mmio->phys_addr;
run->mmio.len = mmio->len;
run->mmio.is_write = mmio->is_write;
memcpy(run->mmio.data, mmio->data, mmio->len);
run->exit_reason = KVM_EXIT_MMIO;
}

int kvm_handle_mmio_return(struct kvm_vcpu *vcpu, struct kvm_run *run);
int io_mem_abort(struct kvm_vcpu *vcpu, struct kvm_run *run,
phys_addr_t fault_ipa);
Expand Down
64 changes: 37 additions & 27 deletions arch/arm/kvm/mmio.c
Original file line number Diff line number Diff line change
Expand Up @@ -121,12 +121,11 @@ int kvm_handle_mmio_return(struct kvm_vcpu *vcpu, struct kvm_run *run)
return 0;
}

static int decode_hsr(struct kvm_vcpu *vcpu, phys_addr_t fault_ipa,
struct kvm_exit_mmio *mmio)
static int decode_hsr(struct kvm_vcpu *vcpu, bool *is_write, int *len)
{
unsigned long rt;
int len;
bool is_write, sign_extend;
int access_size;
bool sign_extend;

if (kvm_vcpu_dabt_isextabt(vcpu)) {
/* cache operation on I/O addr, tell guest unsupported */
Expand All @@ -140,17 +139,15 @@ static int decode_hsr(struct kvm_vcpu *vcpu, phys_addr_t fault_ipa,
return 1;
}

len = kvm_vcpu_dabt_get_as(vcpu);
if (unlikely(len < 0))
return len;
access_size = kvm_vcpu_dabt_get_as(vcpu);
if (unlikely(access_size < 0))
return access_size;

is_write = kvm_vcpu_dabt_iswrite(vcpu);
*is_write = kvm_vcpu_dabt_iswrite(vcpu);
sign_extend = kvm_vcpu_dabt_issext(vcpu);
rt = kvm_vcpu_dabt_get_rd(vcpu);

mmio->is_write = is_write;
mmio->phys_addr = fault_ipa;
mmio->len = len;
*len = access_size;
vcpu->arch.mmio_decode.sign_extend = sign_extend;
vcpu->arch.mmio_decode.rt = rt;

Expand All @@ -165,20 +162,20 @@ static int decode_hsr(struct kvm_vcpu *vcpu, phys_addr_t fault_ipa,
int io_mem_abort(struct kvm_vcpu *vcpu, struct kvm_run *run,
phys_addr_t fault_ipa)
{
struct kvm_exit_mmio mmio;
unsigned long data;
unsigned long rt;
int ret;
bool is_write;
int len;
u8 data_buf[8];

/*
* Prepare MMIO operation. First stash it in a private
* structure that we can use for in-kernel emulation. If the
* kernel can't handle it, copy it into run->mmio and let user
* space do its magic.
* Prepare MMIO operation. First decode the syndrome data we get
* from the CPU. Then try if some in-kernel emulation feels
* responsible, otherwise let user space do its magic.
*/

if (kvm_vcpu_dabt_isvalid(vcpu)) {
ret = decode_hsr(vcpu, fault_ipa, &mmio);
ret = decode_hsr(vcpu, &is_write, &len);
if (ret)
return ret;
} else {
Expand All @@ -188,21 +185,34 @@ int io_mem_abort(struct kvm_vcpu *vcpu, struct kvm_run *run,

rt = vcpu->arch.mmio_decode.rt;

if (mmio.is_write) {
data = vcpu_data_guest_to_host(vcpu, *vcpu_reg(vcpu, rt),
mmio.len);
if (is_write) {
data = vcpu_data_guest_to_host(vcpu, *vcpu_reg(vcpu, rt), len);

trace_kvm_mmio(KVM_TRACE_MMIO_WRITE, len, fault_ipa, data);
mmio_write_buf(data_buf, len, data);

trace_kvm_mmio(KVM_TRACE_MMIO_WRITE, mmio.len,
fault_ipa, data);
mmio_write_buf(mmio.data, mmio.len, data);
ret = kvm_io_bus_write(vcpu, KVM_MMIO_BUS, fault_ipa, len,
data_buf);
} else {
trace_kvm_mmio(KVM_TRACE_MMIO_READ_UNSATISFIED, mmio.len,
trace_kvm_mmio(KVM_TRACE_MMIO_READ_UNSATISFIED, len,
fault_ipa, 0);

ret = kvm_io_bus_read(vcpu, KVM_MMIO_BUS, fault_ipa, len,
data_buf);
}

if (vgic_handle_mmio(vcpu, run, &mmio))
/* Now prepare kvm_run for the potential return to userland. */
run->mmio.is_write = is_write;
run->mmio.phys_addr = fault_ipa;
run->mmio.len = len;
memcpy(run->mmio.data, data_buf, len);

if (!ret) {
/* We handled the access successfully in the kernel. */
kvm_handle_mmio_return(vcpu, run);
return 1;
}

kvm_prepare_mmio(run, &mmio);
run->exit_reason = KVM_EXIT_MMIO;
return 0;
}
22 changes: 0 additions & 22 deletions arch/arm64/include/asm/kvm_mmio.h
Original file line number Diff line number Diff line change
Expand Up @@ -31,28 +31,6 @@ struct kvm_decode {
bool sign_extend;
};

/*
* The in-kernel MMIO emulation code wants to use a copy of run->mmio,
* which is an anonymous type. Use our own type instead.
*/
struct kvm_exit_mmio {
phys_addr_t phys_addr;
u8 data[8];
u32 len;
bool is_write;
void *private;
};

static inline void kvm_prepare_mmio(struct kvm_run *run,
struct kvm_exit_mmio *mmio)
{
run->mmio.phys_addr = mmio->phys_addr;
run->mmio.len = mmio->len;
run->mmio.is_write = mmio->is_write;
memcpy(run->mmio.data, mmio->data, mmio->len);
run->exit_reason = KVM_EXIT_MMIO;
}

int kvm_handle_mmio_return(struct kvm_vcpu *vcpu, struct kvm_run *run);
int io_mem_abort(struct kvm_vcpu *vcpu, struct kvm_run *run,
phys_addr_t fault_ipa);
Expand Down
6 changes: 0 additions & 6 deletions include/kvm/arm_vgic.h
Original file line number Diff line number Diff line change
Expand Up @@ -140,8 +140,6 @@ struct vgic_params {
};

struct vgic_vm_ops {
bool (*handle_mmio)(struct kvm_vcpu *, struct kvm_run *,
struct kvm_exit_mmio *);
bool (*queue_sgi)(struct kvm_vcpu *, int irq);
void (*add_sgi_source)(struct kvm_vcpu *, int irq, int source);
int (*init_model)(struct kvm *);
Expand Down Expand Up @@ -313,8 +311,6 @@ struct vgic_cpu {

struct kvm;
struct kvm_vcpu;
struct kvm_run;
struct kvm_exit_mmio;

int kvm_vgic_addr(struct kvm *kvm, unsigned long type, u64 *addr, bool write);
int kvm_vgic_hyp_init(void);
Expand All @@ -330,8 +326,6 @@ int kvm_vgic_inject_irq(struct kvm *kvm, int cpuid, unsigned int irq_num,
void vgic_v3_dispatch_sgi(struct kvm_vcpu *vcpu, u64 reg);
int kvm_vgic_vcpu_pending_irq(struct kvm_vcpu *vcpu);
int kvm_vgic_vcpu_active_irq(struct kvm_vcpu *vcpu);
bool vgic_handle_mmio(struct kvm_vcpu *vcpu, struct kvm_run *run,
struct kvm_exit_mmio *mmio);

#define irqchip_in_kernel(k) (!!((k)->arch.vgic.in_kernel))
#define vgic_initialized(k) (!!((k)->arch.vgic.nr_cpus))
Expand Down
21 changes: 2 additions & 19 deletions virt/kvm/arm/vgic-v2-emul.c
Original file line number Diff line number Diff line change
Expand Up @@ -404,24 +404,6 @@ static const struct vgic_io_range vgic_dist_ranges[] = {
{}
};

static bool vgic_v2_handle_mmio(struct kvm_vcpu *vcpu, struct kvm_run *run,
struct kvm_exit_mmio *mmio)
{
unsigned long base = vcpu->kvm->arch.vgic.vgic_dist_base;

if (!is_in_range(mmio->phys_addr, mmio->len, base,
KVM_VGIC_V2_DIST_SIZE))
return false;

/* GICv2 does not support accesses wider than 32 bits */
if (mmio->len > 4) {
kvm_inject_dabt(vcpu, mmio->phys_addr);
return true;
}

return vgic_handle_mmio_range(vcpu, run, mmio, vgic_dist_ranges, base);
}

static void vgic_dispatch_sgi(struct kvm_vcpu *vcpu, u32 reg)
{
struct kvm *kvm = vcpu->kvm;
Expand Down Expand Up @@ -580,7 +562,6 @@ void vgic_v2_init_emulation(struct kvm *kvm)
{
struct vgic_dist *dist = &kvm->arch.vgic;

dist->vm_ops.handle_mmio = vgic_v2_handle_mmio;
dist->vm_ops.queue_sgi = vgic_v2_queue_sgi;
dist->vm_ops.add_sgi_source = vgic_v2_add_sgi_source;
dist->vm_ops.init_model = vgic_v2_init_model;
Expand Down Expand Up @@ -690,6 +671,7 @@ static int vgic_attr_regs_access(struct kvm_device *dev,
struct kvm_vcpu *vcpu, *tmp_vcpu;
struct vgic_dist *vgic;
struct kvm_exit_mmio mmio;
u32 data;

offset = attr->attr & KVM_DEV_ARM_VGIC_OFFSET_MASK;
cpuid = (attr->attr & KVM_DEV_ARM_VGIC_CPUID_MASK) >>
Expand All @@ -711,6 +693,7 @@ static int vgic_attr_regs_access(struct kvm_device *dev,

mmio.len = 4;
mmio.is_write = is_write;
mmio.data = &data;
if (is_write)
mmio_data_write(&mmio, ~0, *reg);
switch (attr->group) {
Expand Down
35 changes: 0 additions & 35 deletions virt/kvm/arm/vgic-v3-emul.c
Original file line number Diff line number Diff line change
Expand Up @@ -708,40 +708,6 @@ static const struct vgic_io_range vgic_redist_ranges[] = {
{},
};

/*
* This function splits accesses between the distributor and the two
* redistributor parts (private/SPI). As each redistributor is accessible
* from any CPU, we have to determine the affected VCPU by taking the faulting
* address into account. We then pass this VCPU to the handler function via
* the private parameter.
*/
#define SGI_BASE_OFFSET SZ_64K
static bool vgic_v3_handle_mmio(struct kvm_vcpu *vcpu, struct kvm_run *run,
struct kvm_exit_mmio *mmio)
{
struct vgic_dist *dist = &vcpu->kvm->arch.vgic;
unsigned long dbase = dist->vgic_dist_base;
unsigned long rdbase = dist->vgic_redist_base;
int nrcpus = atomic_read(&vcpu->kvm->online_vcpus);
int vcpu_id;

if (is_in_range(mmio->phys_addr, mmio->len, dbase, GIC_V3_DIST_SIZE)) {
return vgic_handle_mmio_range(vcpu, run, mmio,
vgic_v3_dist_ranges, dbase);
}

if (!is_in_range(mmio->phys_addr, mmio->len, rdbase,
GIC_V3_REDIST_SIZE * nrcpus))
return false;

vcpu_id = (mmio->phys_addr - rdbase) / GIC_V3_REDIST_SIZE;
rdbase += (vcpu_id * GIC_V3_REDIST_SIZE);
mmio->private = kvm_get_vcpu(vcpu->kvm, vcpu_id);

return vgic_handle_mmio_range(vcpu, run, mmio, vgic_redist_ranges,
rdbase);
}

static bool vgic_v3_queue_sgi(struct kvm_vcpu *vcpu, int irq)
{
if (vgic_queue_irq(vcpu, 0, irq)) {
Expand Down Expand Up @@ -861,7 +827,6 @@ void vgic_v3_init_emulation(struct kvm *kvm)
{
struct vgic_dist *dist = &kvm->arch.vgic;

dist->vm_ops.handle_mmio = vgic_v3_handle_mmio;
dist->vm_ops.queue_sgi = vgic_v3_queue_sgi;
dist->vm_ops.add_sgi_source = vgic_v3_add_sgi_source;
dist->vm_ops.init_model = vgic_v3_init_model;
Expand Down
Loading

0 comments on commit 950324a

Please sign in to comment.