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

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

 



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.

> +	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.

>  	return 0;
>  }

I've used the attached patch to get my ARM kernel back to a working state.

Cheers,

	M.
-- 
Jazz is not dead. It just smells funny...
-------------- next part --------------
A non-text attachment was scrubbed...
Name: thumb2-fix.patch
Type: text/x-diff
Size: 1919 bytes
Desc: not available
Url : https://lists.cs.columbia.edu/pipermail/android-virt/attachments/20120614/6a790354/thumb2-fix-0001.bin


[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