Re: [PATCH v2 03/11] ARM: KVM: Initial VGIC MMIO support code

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



On Mon, Aug 20, 2012 at 5:52 AM, Marc Zyngier <marc.zyngier@xxxxxxx> wrote:
> On 18/08/12 04:00, Christoffer Dall wrote:
>> On Thu, Jul 5, 2012 at 11:28 AM, Marc Zyngier <marc.zyngier@xxxxxxx> wrote:
>>> Wire the initial in-kernel MMIO support code for the VGIC, used
>>> for the distributor emulation.
>>>
>>> Signed-off-by: Marc Zyngier <marc.zyngier@xxxxxxx>
>>> ---
>>>  arch/arm/include/asm/kvm_vgic.h |    5 +-
>>>  arch/arm/kvm/Makefile           |    1 +
>>>  arch/arm/kvm/mmu.c              |   25 +++++---
>>>  arch/arm/kvm/vgic.c             |  127 +++++++++++++++++++++++++++++++++++++++
>>>  4 files changed, 150 insertions(+), 8 deletions(-)
>>>  create mode 100644 arch/arm/kvm/vgic.c
>>>
>>> diff --git a/arch/arm/include/asm/kvm_vgic.h b/arch/arm/include/asm/kvm_vgic.h
>>> index 8e8bd6d..59c2f60 100644
>>> --- a/arch/arm/include/asm/kvm_vgic.h
>>> +++ b/arch/arm/include/asm/kvm_vgic.h
>>> @@ -12,7 +12,10 @@ struct kvm_vcpu;
>>>  struct kvm_run;
>>>  struct kvm_exit_mmio;
>>>
>>> -#ifndef CONFIG_KVM_ARM_VGIC
>>> +#ifdef CONFIG_KVM_ARM_VGIC
>>> +int vgic_handle_mmio(struct kvm_vcpu *vcpu, struct kvm_run *run,
>>> +                    struct kvm_exit_mmio *mmio);
>>> +#else
>>>  static inline int kvm_vgic_hyp_init(void)
>>>  {
>>>         return 0;
>>> diff --git a/arch/arm/kvm/Makefile b/arch/arm/kvm/Makefile
>>> index 5f40c5a..c5c4a9f 100644
>>> --- a/arch/arm/kvm/Makefile
>>> +++ b/arch/arm/kvm/Makefile
>>> @@ -15,3 +15,4 @@ kvm-arm-y += $(addprefix ../../../virt/kvm/, kvm_main.o coalesced_mmio.o)
>>>  kvm-arm-y += arm.o guest.o mmu.o emulate.o reset.o
>>>
>>>  obj-$(CONFIG_KVM) += kvm-arm.o
>>> +obj-$(CONFIG_KVM_ARM_VGIC) += vgic.o
>>> diff --git a/arch/arm/kvm/mmu.c b/arch/arm/kvm/mmu.c
>>> index 4955a51..d63e05e 100644
>>> --- a/arch/arm/kvm/mmu.c
>>> +++ b/arch/arm/kvm/mmu.c
>>> @@ -641,10 +641,16 @@ static int io_mem_abort(struct kvm_vcpu *vcpu, struct kvm_run *run,
>>>         /* Get instruction length in bytes */
>>>         instr_len = (vcpu->arch.hsr & HSR_IL) ? 4 : 2;
>>>
>>> -       /* Export MMIO operations to user space */
>>> -       run->mmio.is_write = is_write;
>>> -       run->mmio.phys_addr = fault_ipa;
>>> -       run->mmio.len = len;
>>> +       /*
>>> +        * Prepare MMIO operation. First stash it in a private
>>> +        * structure that we can use for in-kernel emulation. If the
>>> +        * kernel can not handle it, copy it into run->mmio (as it is
>>> +        * garanteed to be the same type) and let user space do its
>>> +        * magic.
>>> +        */
>>> +       mmio.mmio.is_write = is_write;
>>> +       mmio.mmio.phys_addr = fault_ipa;
>>> +       mmio.mmio.len = len;
>>>         vcpu->arch.mmio_sign_extend = sign_extend;
>>>         vcpu->arch.mmio_rd = rd;
>>>
>>> @@ -653,9 +659,14 @@ static int io_mem_abort(struct kvm_vcpu *vcpu, struct kvm_run *run,
>>>                         len, fault_ipa, (is_write) ? *vcpu_reg(vcpu, rd) : 0);
>>>
>>>         if (is_write)
>>> -               memcpy(run->mmio.data, vcpu_reg(vcpu, rd), len);
>>> +               memcpy(mmio.mmio.data, vcpu_reg(vcpu, rd), len);
>>>
>>> -       run->exit_reason = vgic_handle_mmio(vcpu, run);
>>> +       run->exit_reason = vgic_handle_mmio(vcpu, run, &mmio);
>>> +       switch (run->exit_reason) {
>>> +       case KVM_EXIT_MMIO:
>>> +               run->mmio = mmio.mmio;
>>> +               break;
>>> +       }
>>
>> Is this switch going to expand or is it really an "if
>> (run->exit_reason == KVM_EXIT_MMIO)" ?
>
> The switch is gone in recent versions on this patch.
>
>> or, if memory serves me right, it's rather the opposite case, perhaps
>> even turn vgic_handle_mmio into a bool and do:
>>
>> kvm_skip_instr(...);
>>
>> if (vgic_handle_mmio(vcpu, run &mmio))
>>         return 1;
>
> I suppose kvm_skip_instr() is a recent addition. In which case, this
> would work.
>
>> /* Emulate in user space */
>> run->mmio.XXX = mmio.XXX;
>> run->mmio.YYY = mmio.YYY;
>> run->exit_reason = KVM_EXIT_MMIO;
>> return 0;
>>
>>
>> I think we should just define the mmio structure with whatever we
>> need, and then set the fields, field by field, if we need to copy to
>> vcpu->run, then avoid the weird type coherent construction that might
>> leave people pondering for hours why the need for the fancyness. Or
>> maybe that's just me?
>
> Whatever we need is exactly all the fields in the mmio structure. I'm
> trying to ensure consistency between userspace and in-kernel emulation.
> If you feel this is superfluous, I'll get rid of it.
>

I'm just suggesting a way to get rid of the construct you labeled
"beyond ugly" :)

>>
>>>
>>>         /*
>>>          * The MMIO instruction is emulated and should not be re-executed
>>> @@ -664,7 +675,7 @@ static int io_mem_abort(struct kvm_vcpu *vcpu, struct kvm_run *run,
>>>         *vcpu_pc(vcpu) += instr_len;
>>>         kvm_adjust_itstate(vcpu);
>>>         vcpu->stat.mmio_exits++;
>>> -       return KVM_EXIT_MMIO;
>>> +       return run->exit_reason;
>>
>> see above, but in any case this needs to be adjusted when rebased onto
>> kvm-a15-v10 (yes, I know it's my fault keeping this around for so
>> long)
>
> This has already been changed for v10/v11.
>
>>>  }
>>>
>>>  /**
>>> diff --git a/arch/arm/kvm/vgic.c b/arch/arm/kvm/vgic.c
>>> new file mode 100644
>>> index 0000000..0502212
>>> --- /dev/null
>>> +++ b/arch/arm/kvm/vgic.c
>>> @@ -0,0 +1,127 @@
>>> +/*
>>> + * Copyright (C) 2012 ARM Ltd.
>>> + * Author: Marc Zyngier <marc.zyngier@xxxxxxx>
>>> + *
>>> + * This program is free software; you can redistribute it and/or modify
>>> + * it under the terms of the GNU General Public License version 2 as
>>> + * published by the Free Software Foundation.
>>> + *
>>> + * This program is distributed in the hope that it will be useful,
>>> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
>>> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
>>> + * GNU General Public License for more details.
>>> + *
>>> + * You should have received a copy of the GNU General Public License
>>> + * along with this program; if not, write to the Free Software
>>> + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
>>> + */
>>> +
>>> +#include <linux/kvm.h>
>>> +#include <linux/kvm_host.h>
>>> +#include <linux/interrupt.h>
>>> +#include <linux/io.h>
>>> +#include <asm/kvm_emulate.h>
>>> +
>>> +#define ACCESS_READ_VALUE      (1 << 0)
>>> +#define ACCESS_READ_RAZ                (0 << 0)
>>> +#define ACCESS_READ_MASK(x)    ((x) & (1 << 0))
>>> +#define ACCESS_WRITE_IGNORED   (0 << 1)
>>> +#define ACCESS_WRITE_SETBIT    (1 << 1)
>>> +#define ACCESS_WRITE_CLEARBIT  (2 << 1)
>>> +#define ACCESS_WRITE_VALUE     (3 << 1)
>>> +#define ACCESS_WRITE_MASK(x)   ((x) & (3 << 1))
>>> +
>>
>> sorry, it might be me who's just damp, but this would really help me
>> next time I have to read it:
>>
>> /**
>>  * vgic_reg_access - access vgic register
>>  * @mmio:   pointer to the data describing the mmio access
>>  * @reg:    pointer to the virtual backing of the vgic distributor struct
>>  * @offset: least significant 2 bits used for word offset
>>  * @mode:   ACCESS_ mode (see defines above)
>>  *
>>  * Helper to make vgic register access easier using one of the access modes
>>  * defined for vgic register access
>> (read,raz,write-ignored,setbit,clearbit,write)
>>  */
>>
>>> +static void vgic_reg_access(struct kvm_exit_mmio *mmio, u32 *reg, u32 offset, int mode)
>>> +{
>>> +       int word_offset = offset & 3;
>>> +       int shift = word_offset * 8;
>>> +       u32 mask;
>>> +       u32 regval;
>>> +
>>> +       /*
>>> +        * Any alignment fault should have been delivered to the guest
>>> +        * directly (ARM ARM B3.12.7 "Prioritization of aborts").
>>> +        */
>>> +
>>> +       mask = (~0U) >> (word_offset * 8);
>>> +       if (reg)
>>> +               regval = *reg;
>>> +       else {
>>> +               BUG_ON(mode != (ACCESS_READ_RAZ | ACCESS_WRITE_IGNORED));
>>> +               regval = 0;
>>> +       }
>>> +
>>> +       if (mmio->mmio.is_write) {
>>> +               u32 data = (*((u32 *)mmio->mmio.data) & mask) << shift;
>>> +               switch (ACCESS_WRITE_MASK(mode)) {
>>> +               case ACCESS_WRITE_IGNORED:
>>> +                       return;
>>> +
>>> +               case ACCESS_WRITE_SETBIT:
>>> +                       regval |= data;
>>> +                       break;
>>> +
>>> +               case ACCESS_WRITE_CLEARBIT:
>>> +                       regval &= ~data;
>>> +                       break;
>>> +
>>> +               case ACCESS_WRITE_VALUE:
>>> +                       regval = (regval & ~(mask << shift)) | data;
>>> +                       break;
>>> +               }
>>> +               *reg = regval;
>>> +       } else {
>>> +               switch (ACCESS_READ_MASK(mode)) {
>>> +               case ACCESS_READ_RAZ:
>>> +                       regval = 0;
>>> +                       /* fall through */
>>> +
>>> +               case ACCESS_READ_VALUE:
>>> +                       *((u32 *)mmio->mmio.data) = (regval >> shift) & mask;
>>> +               }
>>> +       }
>>> +}
>>> +
>>> +/* All this should be handled by kvm_bus_io_*()... FIXME!!! */
>>> +struct mmio_range {
>>> +       unsigned long base;
>>> +       unsigned long len;
>>> +       void (*handle_mmio)(struct kvm_vcpu *vcpu, struct kvm_exit_mmio *mmio,
>>> +                           u32 offset);
>>> +};
>>> +
>>> +static const struct mmio_range vgic_ranges[] = {
>>> +       {}
>>> +};
>>> +
>>> +static const
>>> +struct mmio_range *find_matching_range(const struct mmio_range *ranges,
>>> +                                      struct kvm_exit_mmio *mmio,
>>> +                                      unsigned long base)
>>> +{
>>> +       const struct mmio_range *r = ranges;
>>> +       unsigned long addr = mmio->mmio.phys_addr - base;
>>> +
>>> +       while (r->len) {
>>> +               if (addr >= r->base &&
>>> +                   (addr + mmio->mmio.len) <= (r->base + r->len))
>>> +                       return r;
>>> +               r++;
>>> +       }
>>> +
>>> +       return NULL;
>>> +}
>>> +
>>> +/**
>>> + * vgic_handle_mmio - handle an in-kernel MMIO access
>>> + * @vcpu:      pointer to the vcpu performing the access
>>> + * @mmio:      pointer to the data describing the access
>>> + *
>>> + * returns KVM_EXIT_UNKNOWN if the MMIO access has been performed
>>> + * in kernel space, and KVM_EXIT_MMIO if it needs to be emulated
>>> + * in user space.
>>
>> as per previous comment, could be just "return true if MMIO access has
>> been handled here"
>>
>>> + */
>>> +int vgic_handle_mmio(struct kvm_vcpu *vcpu, struct kvm_run *run, struct kvm_exit_mmio *mmio)
>>> +{
>>> +       return KVM_EXIT_MMIO;
>>> +}
>>> --
>>> 1.7.10.3
>>>
>>>
>>
>> besides the cosmetic comments, looks good to me.
>>
>
>
> --
> Jazz is not dead. It just smells funny...
>
_______________________________________________
kvmarm mailing list
kvmarm@xxxxxxxxxxxxxxxxxxxxx
https://lists.cs.columbia.edu/cucslists/listinfo/kvmarm


[Index of Archives]     [Linux KVM]     [Spice Development]     [Libvirt]     [Libvirt Users]     [Linux USB Devel]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]

  Powered by Linux