diff mbox

[06/29] arm64: KVM: fault injection into a guest

Message ID 1362455265-24165-7-git-send-email-marc.zyngier@arm.com (mailing list archive)
State New, archived
Headers show

Commit Message

Marc Zyngier March 5, 2013, 3:47 a.m. UTC
Implement the injection of a fault (undefined, data abort or
prefetch abort) into a 64bit guest.

Signed-off-by: Marc Zyngier <marc.zyngier@arm.com>
---
 arch/arm64/kvm/inject_fault.c | 117 ++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 117 insertions(+)
 create mode 100644 arch/arm64/kvm/inject_fault.c

Comments

Christopher Covington March 12, 2013, 1:20 p.m. UTC | #1
Hi Marc,

I noticed you went through the trouble of defining several constants in an
earlier patch. Perhaps you could put them to use here?

On 03/04/2013 10:47 PM, Marc Zyngier wrote:
> Implement the injection of a fault (undefined, data abort or
> prefetch abort) into a 64bit guest.
> 
> Signed-off-by: Marc Zyngier <marc.zyngier@arm.com>
> ---
>  arch/arm64/kvm/inject_fault.c | 117 ++++++++++++++++++++++++++++++++++++++++++
>  1 file changed, 117 insertions(+)
>  create mode 100644 arch/arm64/kvm/inject_fault.c

[...]

> +static void inject_abt64(struct kvm_vcpu *vcpu, bool is_iabt, unsigned long addr)
> +{
> +	unsigned long cpsr = *vcpu_cpsr(vcpu);
> +	int is_aarch32;
> +	u32 esr = 0;
> +
> +	is_aarch32 = vcpu_mode_is_32bit(vcpu);
> +
> +	*vcpu_spsr(vcpu) = cpsr;
> +	vcpu->arch.regs.elr_el1 = *vcpu_pc(vcpu);
> +
> +	*vcpu_cpsr(vcpu) = PSR_MODE_EL1h | PSR_A_BIT | PSR_F_BIT | PSR_I_BIT;
> +	*vcpu_pc(vcpu) = vcpu->arch.sys_regs[VBAR_EL1] + 0x200;
> +
> +	vcpu->arch.sys_regs[FAR_EL1] = addr;
> +
> +	/*
> +	 * Build an {i,d}abort, depending on the level and the
> +	 * instruction set. Report an external synchronous abort.
> +	 */
> +	if (kvm_vcpu_trap_il_is32bit(vcpu))
> +		esr |= (1 << 25);

ESR_EL2_IL

> +	if (is_aarch32 || (cpsr & PSR_MODE_MASK) == PSR_MODE_EL0t)
> +		esr |= (0x20 << 26);

ESR_EL2_EC_IABT << ESR_EL2_EC_SHIFT

> +	else
> +		esr |= (0x21 << 26);

ESR_EL2_EC_IABT_HYP << ESR_EL2_EC_SHIFT

> +
> +	if (!is_iabt)
> +		esr |= (1 << 28);

ESR_EL2_EC_DABT << ESR_EL2_EC_SHIFT

> +
> +	vcpu->arch.sys_regs[ESR_EL1] = esr | 0x10;
> +}
> +
> +static void inject_undef64(struct kvm_vcpu *vcpu)
> +{
> +	unsigned long cpsr = *vcpu_cpsr(vcpu);
> +	u32 esr = 0;
> +
> +	*vcpu_spsr(vcpu) = cpsr;
> +	vcpu->arch.regs.elr_el1 = *vcpu_pc(vcpu);
> +
> +	*vcpu_cpsr(vcpu) = PSR_MODE_EL1h | PSR_F_BIT | PSR_I_BIT;
> +	*vcpu_pc(vcpu) = vcpu->arch.sys_regs[VBAR_EL1] + 0x200;
> +
> +	/*
> +	 * Build an unknown exception, depending on the instruction
> +	 * set.
> +	 */
> +	if (kvm_vcpu_trap_il_is32bit(vcpu))
> +		esr |= (1 << 25);

ESR_EL2_IL

> +
> +	vcpu->arch.sys_regs[ESR_EL1] = esr;
> +}

[...]

Regards,
Christopher
Marc Zyngier March 12, 2013, 2:25 p.m. UTC | #2
On 12/03/13 13:20, Christopher Covington wrote:

Hi Christopher,

> I noticed you went through the trouble of defining several constants in an
> earlier patch. Perhaps you could put them to use here?
> 
> On 03/04/2013 10:47 PM, Marc Zyngier wrote:
>> Implement the injection of a fault (undefined, data abort or
>> prefetch abort) into a 64bit guest.
>>
>> Signed-off-by: Marc Zyngier <marc.zyngier@arm.com>
>> ---
>>  arch/arm64/kvm/inject_fault.c | 117 ++++++++++++++++++++++++++++++++++++++++++
>>  1 file changed, 117 insertions(+)
>>  create mode 100644 arch/arm64/kvm/inject_fault.c
> 
> [...]
> 
>> +static void inject_abt64(struct kvm_vcpu *vcpu, bool is_iabt, unsigned long addr)
>> +{
>> +	unsigned long cpsr = *vcpu_cpsr(vcpu);
>> +	int is_aarch32;
>> +	u32 esr = 0;
>> +
>> +	is_aarch32 = vcpu_mode_is_32bit(vcpu);
>> +
>> +	*vcpu_spsr(vcpu) = cpsr;
>> +	vcpu->arch.regs.elr_el1 = *vcpu_pc(vcpu);
>> +
>> +	*vcpu_cpsr(vcpu) = PSR_MODE_EL1h | PSR_A_BIT | PSR_F_BIT | PSR_I_BIT;
>> +	*vcpu_pc(vcpu) = vcpu->arch.sys_regs[VBAR_EL1] + 0x200;
>> +
>> +	vcpu->arch.sys_regs[FAR_EL1] = addr;
>> +
>> +	/*
>> +	 * Build an {i,d}abort, depending on the level and the
>> +	 * instruction set. Report an external synchronous abort.
>> +	 */
>> +	if (kvm_vcpu_trap_il_is32bit(vcpu))
>> +		esr |= (1 << 25);
> 
> ESR_EL2_IL

This is the illustration of what I was saying earlier about confusing
levels. Here, we're dealing with the guest's EL1. Using the _EL2 define
is semantically wrong, even if it has the same value.

> 
>> +	if (is_aarch32 || (cpsr & PSR_MODE_MASK) == PSR_MODE_EL0t)
>> +		esr |= (0x20 << 26);
> 
> ESR_EL2_EC_IABT << ESR_EL2_EC_SHIFT
> 
>> +	else
>> +		esr |= (0x21 << 26);
> 
> ESR_EL2_EC_IABT_HYP << ESR_EL2_EC_SHIFT

Even worse here. What this actually mean is "Exception at the current
level", which is EL1. Having _HYP here is completely misleading.

Now, maybe I should review all these defines and give them a more
general meaning. Then we'd be able to share the defines across levels
(arm/arch64/kernel/entry.S could use some defines too...). But overall,
I'm quite reluctant to start mixing ESR_EL1 and ESR_EL2.

>> +
>> +	if (!is_iabt)
>> +		esr |= (1 << 28);
> 
> ESR_EL2_EC_DABT << ESR_EL2_EC_SHIFT

Nasty. Works, but very nasty... ;-)

>> +
>> +	vcpu->arch.sys_regs[ESR_EL1] = esr | 0x10;
>> +}
>> +
>> +static void inject_undef64(struct kvm_vcpu *vcpu)
>> +{
>> +	unsigned long cpsr = *vcpu_cpsr(vcpu);
>> +	u32 esr = 0;
>> +
>> +	*vcpu_spsr(vcpu) = cpsr;
>> +	vcpu->arch.regs.elr_el1 = *vcpu_pc(vcpu);
>> +
>> +	*vcpu_cpsr(vcpu) = PSR_MODE_EL1h | PSR_F_BIT | PSR_I_BIT;
>> +	*vcpu_pc(vcpu) = vcpu->arch.sys_regs[VBAR_EL1] + 0x200;
>> +
>> +	/*
>> +	 * Build an unknown exception, depending on the instruction
>> +	 * set.
>> +	 */
>> +	if (kvm_vcpu_trap_il_is32bit(vcpu))
>> +		esr |= (1 << 25);
> 
> ESR_EL2_IL
> 
>> +
>> +	vcpu->arch.sys_regs[ESR_EL1] = esr;
>> +}

Thanks for reviewing,

	M.
Geoff Levand March 16, 2013, 1:03 a.m. UTC | #3
Hi Marc,

On Tue, 2013-03-05 at 03:47 +0000, Marc Zyngier wrote:
> --- /dev/null
> +++ b/arch/arm64/kvm/inject_fault.c
> @@ -0,0 +1,117 @@

...

> + * kvm_inject_undefined - inject a undefined instruction into the guest

s/ a undefined/ an undefined/

-Geoff

--
To unsubscribe from this list: send the line "unsubscribe kvm" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
diff mbox

Patch

diff --git a/arch/arm64/kvm/inject_fault.c b/arch/arm64/kvm/inject_fault.c
new file mode 100644
index 0000000..80b245f
--- /dev/null
+++ b/arch/arm64/kvm/inject_fault.c
@@ -0,0 +1,117 @@ 
+/*
+ * Fault injection for 64bit guests.
+ *
+ * Copyright (C) 2012 - ARM Ltd
+ * Author: Marc Zyngier <marc.zyngier@arm.com>
+ *
+ * Based on arch/arm/kvm/emulate.c
+ * Copyright (C) 2012 - Virtual Open Systems and Columbia University
+ * Author: Christoffer Dall <c.dall@virtualopensystems.com>
+ *
+ * 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <linux/kvm_host.h>
+#include <asm/kvm_emulate.h>
+
+static void inject_abt64(struct kvm_vcpu *vcpu, bool is_iabt, unsigned long addr)
+{
+	unsigned long cpsr = *vcpu_cpsr(vcpu);
+	int is_aarch32;
+	u32 esr = 0;
+
+	is_aarch32 = vcpu_mode_is_32bit(vcpu);
+
+	*vcpu_spsr(vcpu) = cpsr;
+	vcpu->arch.regs.elr_el1 = *vcpu_pc(vcpu);
+
+	*vcpu_cpsr(vcpu) = PSR_MODE_EL1h | PSR_A_BIT | PSR_F_BIT | PSR_I_BIT;
+	*vcpu_pc(vcpu) = vcpu->arch.sys_regs[VBAR_EL1] + 0x200;
+
+	vcpu->arch.sys_regs[FAR_EL1] = addr;
+
+	/*
+	 * Build an {i,d}abort, depending on the level and the
+	 * instruction set. Report an external synchronous abort.
+	 */
+	if (kvm_vcpu_trap_il_is32bit(vcpu))
+		esr |= (1 << 25);
+
+	if (is_aarch32 || (cpsr & PSR_MODE_MASK) == PSR_MODE_EL0t)
+		esr |= (0x20 << 26);
+	else
+		esr |= (0x21 << 26);
+
+	if (!is_iabt)
+		esr |= (1 << 28);
+
+	vcpu->arch.sys_regs[ESR_EL1] = esr | 0x10;
+}
+
+static void inject_undef64(struct kvm_vcpu *vcpu)
+{
+	unsigned long cpsr = *vcpu_cpsr(vcpu);
+	u32 esr = 0;
+
+	*vcpu_spsr(vcpu) = cpsr;
+	vcpu->arch.regs.elr_el1 = *vcpu_pc(vcpu);
+
+	*vcpu_cpsr(vcpu) = PSR_MODE_EL1h | PSR_F_BIT | PSR_I_BIT;
+	*vcpu_pc(vcpu) = vcpu->arch.sys_regs[VBAR_EL1] + 0x200;
+
+	/*
+	 * Build an unknown exception, depending on the instruction
+	 * set.
+	 */
+	if (kvm_vcpu_trap_il_is32bit(vcpu))
+		esr |= (1 << 25);
+
+	vcpu->arch.sys_regs[ESR_EL1] = esr;
+}
+
+/**
+ * kvm_inject_dabt - inject a data abort into the guest
+ * @vcpu: The VCPU to receive the undefined exception
+ * @addr: The address to report in the DFAR
+ *
+ * It is assumed that this code is called from the VCPU thread and that the
+ * VCPU therefore is not currently executing guest code.
+ */
+void kvm_inject_dabt(struct kvm_vcpu *vcpu, unsigned long addr)
+{
+	inject_abt64(vcpu, false, addr);
+}
+
+/**
+ * kvm_inject_pabt - inject a prefetch abort into the guest
+ * @vcpu: The VCPU to receive the undefined exception
+ * @addr: The address to report in the DFAR
+ *
+ * It is assumed that this code is called from the VCPU thread and that the
+ * VCPU therefore is not currently executing guest code.
+ */
+void kvm_inject_pabt(struct kvm_vcpu *vcpu, unsigned long addr)
+{
+	inject_abt64(vcpu, true, addr);
+}
+
+/**
+ * kvm_inject_undefined - inject a undefined instruction into the guest
+ *
+ * It is assumed that this code is called from the VCPU thread and that the
+ * VCPU therefore is not currently executing guest code.
+ */
+void kvm_inject_undefined(struct kvm_vcpu *vcpu)
+{
+	inject_undef64(vcpu);
+}