From patchwork Fri Aug 18 14:23:46 2017 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Dongjiu Geng X-Patchwork-Id: 9909017 Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork.web.codeaurora.org (Postfix) with ESMTP id 5AB6760386 for ; Fri, 18 Aug 2017 14:02:33 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 2D81A28CD5 for ; Fri, 18 Aug 2017 14:02:33 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id 220FB28CEA; Fri, 18 Aug 2017 14:02:33 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on pdx-wl-mail.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-2.6 required=2.0 tests=BAYES_00,DKIM_SIGNED, DKIM_VALID,RCVD_IN_DNSWL_LOW autolearn=unavailable version=3.3.1 Received: from bombadil.infradead.org (bombadil.infradead.org [65.50.211.133]) (using TLSv1.2 with cipher AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by mail.wl.linuxfoundation.org (Postfix) with ESMTPS id 5031928CD5 for ; Fri, 18 Aug 2017 14:02:32 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=lists.infradead.org; s=bombadil.20170209; h=Sender: Content-Transfer-Encoding:Content-Type:Cc:List-Subscribe:List-Help:List-Post: List-Archive:List-Unsubscribe:List-Id:MIME-Version:References:In-Reply-To: Message-ID:Date:Subject:To:From:Reply-To:Content-ID:Content-Description: Resent-Date:Resent-From:Resent-Sender:Resent-To:Resent-Cc:Resent-Message-ID: List-Owner; bh=5jBTIroymipyKhWpebz4JvmdelJkk+tY/iXrCMZCoW4=; b=OuLnwO/mU3mQ95 rSAlNoazZzfSrl4IcJu65DCXZQUp5HkZhwTy1NcYFL+2YMadXo3MzxJuS2ufyD62qgKAeCOeo0EGl c8fQfclImuWa4thNMD+kKP+PWJIsuIheoU+QqwfssW1wdiKAp+Nh1D/Ug9Ufdv9O8v5vGp6JC8Nse JQktmZ+HSxOLi3TTnY0z/Lz0QWpBqOagbRm1kdQz4GEE41cUVA7h8m12YwA1RjM9orkkU7LeQS3wW A1Wa6fBEhm4cqHJ/yg3fQYw9vPA9rFvN/ammRozXQ2xOCSNReMJ4e366w4kmc/VJXn0iDcxqhwURl wHVy5ODIy4zI3NAiez/A==; Received: from localhost ([127.0.0.1] helo=bombadil.infradead.org) by bombadil.infradead.org with esmtp (Exim 4.87 #1 (Red Hat Linux)) id 1dihr1-0004ww-BP; Fri, 18 Aug 2017 14:02:27 +0000 Received: from szxga05-in.huawei.com ([45.249.212.191]) by bombadil.infradead.org with esmtps (Exim 4.87 #1 (Red Hat Linux)) id 1dihq8-00041W-IF for linux-arm-kernel@lists.infradead.org; Fri, 18 Aug 2017 14:01:41 +0000 Received: from 172.30.72.58 (EHLO DGGEMS411-HUB.china.huawei.com) ([172.30.72.58]) by dggrg05-dlp.huawei.com (MOS 4.4.6-GA FastPath queued) with ESMTP id DFM74406; Fri, 18 Aug 2017 22:00:47 +0800 (CST) Received: from linux.huawei.com (10.67.187.203) by DGGEMS411-HUB.china.huawei.com (10.3.19.211) with Microsoft SMTP Server id 14.3.301.0; Fri, 18 Aug 2017 22:00:13 +0800 From: Dongjiu Geng To: , , , , , , , , , , , , , , , , , , , , , , , , , , , , Subject: [PATCH v11 5/6] target-arm: kvm64: handle SIGBUS signal for synchronous External Abort Date: Fri, 18 Aug 2017 22:23:46 +0800 Message-ID: <1503066227-18251-6-git-send-email-gengdongjiu@huawei.com> X-Mailer: git-send-email 1.7.7 In-Reply-To: <1503066227-18251-1-git-send-email-gengdongjiu@huawei.com> References: <1503066227-18251-1-git-send-email-gengdongjiu@huawei.com> MIME-Version: 1.0 X-Originating-IP: [10.67.187.203] X-CFilter-Loop: Reflected X-Mirapoint-Virus-RAPID-Raw: score=unknown(0), refid=str=0001.0A020201.5996F310.00E0, ss=1, re=0.000, recu=0.000, reip=0.000, cl=1, cld=1, fgs=0, ip=0.0.0.0, so=2014-11-16 11:51:01, dmn=2013-03-21 17:37:32 X-Mirapoint-Loop-Id: dbca42ab5aec7b3a3582b2370f2434d9 X-CRM114-Version: 20100106-BlameMichelson ( TRE 0.8.0 (BSD) ) MR-646709E3 X-CRM114-CacheID: sfid-20170818_070133_263328_2ADCADFF X-CRM114-Status: GOOD ( 18.71 ) X-BeenThere: linux-arm-kernel@lists.infradead.org X-Mailman-Version: 2.1.21 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: zhengqiang10@huawei.com, wuquanming@huawei.com, huangshaoyu@huawei.com, linuxarm@huawei.com, gengdongjiu@huawei.com Sender: "linux-arm-kernel" Errors-To: linux-arm-kernel-bounces+patchwork-linux-arm=patchwork.kernel.org@lists.infradead.org X-Virus-Scanned: ClamAV using ClamSMTP Add SIGBUS signal handler. In this handler, it checks the exception type, translates the host VA which is delivered by host or KVM to guest PA, then fills this PA to CPER, finally injects a Error to guest OS through KVM. Add synchronous external abort injection logic, setup spsr_elx, esr_elx, PSTATE, far_elx, elr_elx etc, when switch to guest OS, it will jump to the synchronous external abort vector table entry. Signed-off-by: Dongjiu Geng Signed-off-by: Quanming Wu --- include/sysemu/kvm.h | 2 +- linux-headers/asm-arm64/kvm.h | 5 ++ target/arm/internals.h | 13 ++++ target/arm/kvm.c | 34 ++++++++++ target/arm/kvm64.c | 150 ++++++++++++++++++++++++++++++++++++++++++ target/arm/kvm_arm.h | 1 + 6 files changed, 204 insertions(+), 1 deletion(-) diff --git a/include/sysemu/kvm.h b/include/sysemu/kvm.h index 3a458f5..90c1605 100644 --- a/include/sysemu/kvm.h +++ b/include/sysemu/kvm.h @@ -361,7 +361,7 @@ bool kvm_vcpu_id_is_valid(int vcpu_id); /* Returns VCPU ID to be used on KVM_CREATE_VCPU ioctl() */ unsigned long kvm_arch_vcpu_id(CPUState *cpu); -#ifdef TARGET_I386 +#if defined(TARGET_I386) || defined(TARGET_AARCH64) #define KVM_HAVE_MCE_INJECTION 1 void kvm_arch_on_sigbus_vcpu(CPUState *cpu, int code, void *addr); #endif diff --git a/linux-headers/asm-arm64/kvm.h b/linux-headers/asm-arm64/kvm.h index d254700..5909c30 100644 --- a/linux-headers/asm-arm64/kvm.h +++ b/linux-headers/asm-arm64/kvm.h @@ -181,6 +181,11 @@ struct kvm_arch_memory_slot { #define KVM_REG_ARM64_SYSREG_OP2_MASK 0x0000000000000007 #define KVM_REG_ARM64_SYSREG_OP2_SHIFT 0 +/* AArch64 fault registers */ +#define KVM_REG_ARM64_FAULT (0x0014 << KVM_REG_ARM_COPROC_SHIFT) +#define KVM_REG_ARM64_FAULT_ESR_EC (0) +#define KVM_REG_ARM64_FAULT_FAR (1) + #define ARM64_SYS_REG_SHIFT_MASK(x,n) \ (((x) << KVM_REG_ARM64_SYSREG_ ## n ## _SHIFT) & \ KVM_REG_ARM64_SYSREG_ ## n ## _MASK) diff --git a/target/arm/internals.h b/target/arm/internals.h index 1f6efef..fc0ad6d 100644 --- a/target/arm/internals.h +++ b/target/arm/internals.h @@ -235,6 +235,19 @@ enum arm_exception_class { #define ARM_EL_ISV_SHIFT 24 #define ARM_EL_IL (1 << ARM_EL_IL_SHIFT) #define ARM_EL_ISV (1 << ARM_EL_ISV_SHIFT) +#define ARM_EL_EC_MASK ((0x3F) << ARM_EL_EC_SHIFT) +#define ARM_EL_FSC_TYPE (0x3C) + +#define FSC_SEA (0x10) +#define FSC_SEA_TTW0 (0x14) +#define FSC_SEA_TTW1 (0x15) +#define FSC_SEA_TTW2 (0x16) +#define FSC_SEA_TTW3 (0x17) +#define FSC_SECC (0x18) +#define FSC_SECC_TTW0 (0x1c) +#define FSC_SECC_TTW1 (0x1d) +#define FSC_SECC_TTW2 (0x1e) +#define FSC_SECC_TTW3 (0x1f) /* Utility functions for constructing various kinds of syndrome value. * Note that in general we follow the AArch64 syndrome values; in a diff --git a/target/arm/kvm.c b/target/arm/kvm.c index 7c17f0d..2e1716a 100644 --- a/target/arm/kvm.c +++ b/target/arm/kvm.c @@ -129,6 +129,39 @@ void kvm_arm_destroy_scratch_host_vcpu(int *fdarray) } } +typedef struct HWPoisonPage { + ram_addr_t ram_addr; + QLIST_ENTRY(HWPoisonPage) list; +} HWPoisonPage; + +static QLIST_HEAD(, HWPoisonPage) hwpoison_page_list = + QLIST_HEAD_INITIALIZER(hwpoison_page_list); + +static void kvm_unpoison_all(void *param) +{ + HWPoisonPage *page, *next_page; + + QLIST_FOREACH_SAFE(page, &hwpoison_page_list, list, next_page) { + QLIST_REMOVE(page, list); + qemu_ram_remap(page->ram_addr, TARGET_PAGE_SIZE); + g_free(page); + } +} + +void kvm_hwpoison_page_add(ram_addr_t ram_addr) +{ + HWPoisonPage *page; + + QLIST_FOREACH(page, &hwpoison_page_list, list) { + if (page->ram_addr == ram_addr) { + return; + } + } + page = g_new(HWPoisonPage, 1); + page->ram_addr = ram_addr; + QLIST_INSERT_HEAD(&hwpoison_page_list, page, list); +} + static void kvm_arm_host_cpu_class_init(ObjectClass *oc, void *data) { ARMHostCPUClass *ahcc = ARM_HOST_CPU_CLASS(oc); @@ -182,6 +215,7 @@ int kvm_arch_init(MachineState *ms, KVMState *s) cap_has_mp_state = kvm_check_extension(s, KVM_CAP_MP_STATE); + qemu_register_reset(kvm_unpoison_all, NULL); type_register_static(&host_arm_cpu_type_info); return 0; diff --git a/target/arm/kvm64.c b/target/arm/kvm64.c index 0781367..d3bdab2 100644 --- a/target/arm/kvm64.c +++ b/target/arm/kvm64.c @@ -27,6 +27,8 @@ #include "kvm_arm.h" #include "internals.h" #include "hw/arm/arm.h" +#include "hw/acpi/acpi-defs.h" +#include "hw/acpi/hest_ghes.h" static bool have_guest_debug; @@ -590,6 +592,79 @@ int kvm_arm_cpreg_level(uint64_t regidx) return KVM_PUT_RUNTIME_STATE; } +static int kvm_arm_cpreg_value(ARMCPU *cpu, ptrdiff_t fieldoffset) +{ + int i; + + for (i = 0; i < cpu->cpreg_array_len; i++) { + uint32_t regidx = kvm_to_cpreg_id(cpu->cpreg_indexes[i]); + const ARMCPRegInfo *ri; + ri = get_arm_cp_reginfo(cpu->cp_regs, regidx); + if (!ri) { + continue; + } + + if (ri->type & ARM_CP_NO_RAW) { + continue; + } + + if (ri->fieldoffset == fieldoffset) { + cpu->cpreg_values[i] = read_raw_cp_reg(&cpu->env, ri); + return 0; + } + } + return -EINVAL; +} + +/* Inject synchronous external abort */ +static int kvm_inject_arm_sea(CPUState *c) +{ + ARMCPU *cpu = ARM_CPU(c); + CPUARMState *env = &cpu->env; + unsigned long cpsr = pstate_read(env); + uint32_t esr = 0; + int ret; + + c->exception_index = EXCP_DATA_ABORT; + /* Inject the exception to El1 */ + env->exception.target_el = 1; + CPUClass *cc = CPU_GET_CLASS(c); + + esr |= (EC_DATAABORT << ARM_EL_EC_SHIFT); + /* This exception syndrome includes {I,D}FSC in the bits [5:0] + */ + esr |= (env->exception.syndrome & 0x3f); + + /* This exception is EL0 or EL1 fault. */ + if ((cpsr & 0xf) == PSTATE_MODE_EL0t) { + esr |= (EC_INSNABORT << ARM_EL_EC_SHIFT); + } else { + esr |= (EC_INSNABORT_SAME_EL << ARM_EL_EC_SHIFT); + } + + /* In the aarch64, there is only 32-bit instruction*/ + esr |= ARM_EL_IL; + env->exception.syndrome = esr; + cc->do_interrupt(c); + + /* set ESR_EL1 */ + ret = kvm_arm_cpreg_value(cpu, offsetof(CPUARMState, cp15.esr_el[1])); + + if (ret) { + fprintf(stderr, "<%s> failed to set esr_el1\n", __func__); + abort(); + } + + /* set FAR_EL1 */ + ret = kvm_arm_cpreg_value(cpu, offsetof(CPUARMState, cp15.far_el[1])); + if (ret) { + fprintf(stderr, "<%s> failed to set far_el1\n", __func__); + abort(); + } + + return 0; +} + #define AARCH64_CORE_REG(x) (KVM_REG_ARM64 | KVM_REG_SIZE_U64 | \ KVM_REG_ARM_CORE | KVM_REG_ARM_CORE_REG(x)) @@ -599,6 +674,9 @@ int kvm_arm_cpreg_level(uint64_t regidx) #define AARCH64_SIMD_CTRL_REG(x) (KVM_REG_ARM64 | KVM_REG_SIZE_U32 | \ KVM_REG_ARM_CORE | KVM_REG_ARM_CORE_REG(x)) +#define AARCH64_FAULT_REG(x) (KVM_REG_ARM64 | KVM_REG_SIZE_U64 | \ + KVM_REG_ARM64_FAULT | (x)) + int kvm_arch_put_registers(CPUState *cs, int level) { struct kvm_one_reg reg; @@ -873,6 +951,22 @@ int kvm_arch_get_registers(CPUState *cs) } vfp_set_fpcr(env, fpr); + if (is_a64(env)) { + reg.id = AARCH64_FAULT_REG(KVM_REG_ARM64_FAULT_ESR_EC); + reg.addr = (uintptr_t)(&env->exception.syndrome); + ret = kvm_vcpu_ioctl(cs, KVM_GET_ONE_REG, ®); + if (ret) { + return ret; + } + + reg.id = AARCH64_FAULT_REG(KVM_REG_ARM64_FAULT_FAR); + reg.addr = (uintptr_t)(&env->exception.vaddress); + ret = kvm_vcpu_ioctl(cs, KVM_GET_ONE_REG, ®); + if (ret) { + return ret; + } + } + if (!write_kvmstate_to_list(cpu)) { return EINVAL; } @@ -887,6 +981,62 @@ int kvm_arch_get_registers(CPUState *cs) return ret; } +static bool is_abort_sea(unsigned long syndrome) +{ + unsigned long fault_status; + uint8_t ec = ((syndrome & ARM_EL_EC_MASK) >> ARM_EL_EC_SHIFT); + if ((ec != EC_INSNABORT) && (ec != EC_DATAABORT)) { + return false; + } + + fault_status = syndrome & ARM_EL_FSC_TYPE; + switch (fault_status) { + case FSC_SEA: + case FSC_SEA_TTW0: + case FSC_SEA_TTW1: + case FSC_SEA_TTW2: + case FSC_SEA_TTW3: + case FSC_SECC: + case FSC_SECC_TTW0: + case FSC_SECC_TTW1: + case FSC_SECC_TTW2: + case FSC_SECC_TTW3: + return true; + default: + return false; + } +} + +void kvm_arch_on_sigbus_vcpu(CPUState *c, int code, void *addr) +{ + ram_addr_t ram_addr; + hwaddr paddr; + + ARMCPU *cpu = ARM_CPU(c); + CPUARMState *env = &cpu->env; + assert(code == BUS_MCEERR_AR || code == BUS_MCEERR_AO); + if (addr) { + ram_addr = qemu_ram_addr_from_host(addr); + if (ram_addr != RAM_ADDR_INVALID && + kvm_physical_memory_addr_from_host(c->kvm_state, addr, &paddr)) { + kvm_cpu_synchronize_state(c); + kvm_hwpoison_page_add(ram_addr); + if (is_abort_sea(env->exception.syndrome)) { + ghes_update_guest(ACPI_HEST_NOTIFY_SEA, paddr); + kvm_inject_arm_sea(c); + } + return; + } + fprintf(stderr, "Hardware memory error for memory used by " + "QEMU itself instead of guest system!\n"); + } + + if (code == BUS_MCEERR_AR) { + fprintf(stderr, "Hardware memory error!\n"); + exit(1); + } +} + /* C6.6.29 BRK instruction */ static const uint32_t brk_insn = 0xd4200000; diff --git a/target/arm/kvm_arm.h b/target/arm/kvm_arm.h index 633d088..7cdde97 100644 --- a/target/arm/kvm_arm.h +++ b/target/arm/kvm_arm.h @@ -288,4 +288,5 @@ static inline const char *its_class_name(void) } } +void kvm_hwpoison_page_add(ram_addr_t ram_addr); #endif