[Android-virt] [FYI PATCH] ARM: KVM: adjust_itstate on guest exceptions

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

 



On Thu, Jun 14, 2012 at 5:58 AM, Marc Zyngier <marc.zyngier at arm.com> wrote:
> On 14/06/12 05:48, Christoffer Dall wrote:
>> When an exception is taken from a guest running in Thumb-2 mode and
>> that instruction is emulated and skipped, the ITSTATE field stored
>> inside the CPSR must be updated by KVM, as the instruction is never
>> advanced by hardware.
>>
>> This can cause bugs like ending up in a state where you incorrectly don't or do
>> execute instructions, or even worse, as was the case in some GIC i/o code,
>> where one IT-block immediately follows another one, causes the IT-block to
>> generate an undefined exception on the IT instruction itself.
>
> Nice catch.
>
>> Note: This patch does touch a tiny-bit of unrelated code, but it's going to be
>> flattened for next KVM release anyhow.
>
> This patch breaks my ARM guest kernel though, see below.
>
>> Signed-off-by: Christoffer Dall <c.dall at virtualopensystems.com>
>> ---
>> ?arch/arm/include/asm/kvm_emulate.h | ? ?1 +
>> ?arch/arm/kvm/emulate.c ? ? ? ? ? ? | ? 49 ++++++++++++++++++++++++++++++++++--
>> ?arch/arm/kvm/mmu.c ? ? ? ? ? ? ? ? | ? ?1 +
>> ?3 files changed, 48 insertions(+), 3 deletions(-)
>>
>> diff --git a/arch/arm/include/asm/kvm_emulate.h b/arch/arm/include/asm/kvm_emulate.h
>> index 5b19196..1f93e71 100644
>> --- a/arch/arm/include/asm/kvm_emulate.h
>> +++ b/arch/arm/include/asm/kvm_emulate.h
>> @@ -49,6 +49,7 @@ int kvm_handle_cp15_64(struct kvm_vcpu *vcpu, struct kvm_run *run);
>> ?int kvm_handle_wfi(struct kvm_vcpu *vcpu, struct kvm_run *run);
>> ?int kvm_emulate_mmio_ls(struct kvm_vcpu *vcpu, phys_addr_t fault_ipa,
>> ? ? ? ? ? ? ? ? ? ? ? unsigned long instr);
>> +void kvm_adjust_itstate(struct kvm_vcpu *vcpu);
>>
>> ?/*
>> ? * Return the SPSR for the specified mode of the virtual CPU.
>> diff --git a/arch/arm/kvm/emulate.c b/arch/arm/kvm/emulate.c
>> index 36f591b..98ae05a 100644
>> --- a/arch/arm/kvm/emulate.c
>> +++ b/arch/arm/kvm/emulate.c
>> @@ -657,7 +657,7 @@ static int kvm_ls_length(struct kvm_vcpu *vcpu, u32 instr)
>> ?int kvm_emulate_mmio_ls(struct kvm_vcpu *vcpu, phys_addr_t fault_ipa,
>> ? ? ? ? ? ? ? ? ? ? ? unsigned long instr)
>> ?{
>> - ? ? unsigned long rd, rn, offset, len;
>> + ? ? unsigned long rd, rn, offset, len, instr_len;
>> ? ? ? int index;
>> ? ? ? bool is_write;
>>
>> @@ -694,8 +694,51 @@ int kvm_emulate_mmio_ls(struct kvm_vcpu *vcpu, phys_addr_t fault_ipa,
>>
>> ? ? ? /*
>> ? ? ? ?* The MMIO instruction is emulated and should not be re-executed
>> - ? ? ?* in the guest. (XXX We don't support Thumb instructions yet).
>> + ? ? ?* in the guest.
>> ? ? ? ?*/
>> - ? ? *vcpu_pc(vcpu) += 4;
>> + ? ? if (*vcpu_cpsr(vcpu) & PSR_T_BIT && !is_wide_instruction(instr))
>> + ? ? ? ? ? ? instr_len = 4;
>
> This bit looks wrong. Your instruction length is 4 if you're in ARM mode
> or the instruction is wide.
>

whoops, yeah. Blame it on the late night.

>> + ? ? else
>> + ? ? ? ? ? ? instr_len = 2;
>> +
>> + ? ? *vcpu_pc(vcpu) += instr_len;
>> + ? ? kvm_adjust_itstate(vcpu);
>> ? ? ? return 0;
>> ?}
>> +
>> +/**
>> + * adjust_itstate - adjust ITSTATE when emulating instructions in IT-block
>> + * @vcpu: ? ?The VCPU pointer
>> + *
>> + * When exceptions occur while instructions are executed in Thumb IF-THEN
>> + * blocks, the ITSTATE field of the CPSR is not advanved (updated), so we have
>> + * to do this little bit of work manually. The fields map like this:
>> + *
>> + * IT[7:0] -> CPSR[26:25],CPSR[15:10]
>> + */
>> +void kvm_adjust_itstate(struct kvm_vcpu *vcpu)
>> +{
>> + ? ? unsigned long itbits, cond;
>> + ? ? unsigned long cpsr = *vcpu_cpsr(vcpu);
>> +
>> + ? ? if (!(cpsr & PSR_IT_MASK))
>> + ? ? ? ? ? ? return;
>> +
>> + ? ? BUG_ON(!(cpsr & PSR_T_BIT));
>> +
>> + ? ? cond = (cpsr & 0xe000) >> 13;
>> + ? ? itbits = (cpsr & 0x1c00) >> (10 - 2);
>> + ? ? itbits |= (cpsr & (0x3 << 25)) >> 25;
>> +
>> + ? ? /* Perform ITAdvance (see page A-52 in ARM DDI 0406C) */
>> + ? ? if ((itbits & 0x7) == 0)
>> + ? ? ? ? ? ? itbits = cond = 0;
>> + ? ? else
>> + ? ? ? ? ? ? itbits = (itbits << 1) & 0x1f;
>> +
>> + ? ? cpsr &= ~PSR_IT_MASK;
>> + ? ? cpsr |= cond << 13;
>> + ? ? cpsr |= (itbits & 0x1c) << (10 - 2);
>> + ? ? cpsr |= (itbits & 0x3) << 25;
>> + ? ? *vcpu_cpsr(vcpu) = cpsr;
>> +}
>> diff --git a/arch/arm/kvm/mmu.c b/arch/arm/kvm/mmu.c
>> index 094035a..64e93c5 100644
>> --- a/arch/arm/kvm/mmu.c
>> +++ b/arch/arm/kvm/mmu.c
>> @@ -493,6 +493,7 @@ static int io_mem_abort(struct kvm_vcpu *vcpu, struct kvm_run *run,
>> ? ? ? ?* in the guest.
>> ? ? ? ?*/
>> ? ? ? *vcpu_pc(vcpu) += instr_len;
>> + ? ? kvm_adjust_itstate(vcpu);
>
> This should probably only be called if in Thumb mode.
>

if in ARM mode the ITSTATE block really should be zero and the adjust
itstate should just exit.

>> ? ? ? return 0;
>> ?}
>
> I've used the attached patch to get my ARM kernel back to a working state.
>

thanks!

-Christoffer



[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