From patchwork Thu Jul 4 16:07:54 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: Peter Maydell X-Patchwork-Id: 11031707 Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id 4D05E13BD for ; Thu, 4 Jul 2019 16:16:00 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 2B41228787 for ; Thu, 4 Jul 2019 16:16:00 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id 1D4A928837; Thu, 4 Jul 2019 16:16:00 +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=-5.0 required=2.0 tests=BAYES_00,DKIM_INVALID, DKIM_SIGNED,MAILING_LIST_MULTI,RCVD_IN_DNSWL_MED autolearn=ham version=3.3.1 Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) (using TLSv1.2 with cipher AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by mail.wl.linuxfoundation.org (Postfix) with ESMTPS id D194C28787 for ; Thu, 4 Jul 2019 16:15:58 +0000 (UTC) Received: from localhost ([::1]:47488 helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.86_2) (envelope-from ) id 1hj4Or-0008QB-Tw for patchwork-qemu-devel@patchwork.kernel.org; Thu, 04 Jul 2019 12:15:57 -0400 Received: from eggs.gnu.org ([2001:470:142:3::10]:44134) by lists.gnu.org with esmtp (Exim 4.86_2) (envelope-from ) id 1hj4HK-0007sS-IY for qemu-devel@nongnu.org; Thu, 04 Jul 2019 12:08:14 -0400 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1hj4HI-0001zm-2f for qemu-devel@nongnu.org; Thu, 04 Jul 2019 12:08:10 -0400 Received: from mail-wm1-x329.google.com ([2a00:1450:4864:20::329]:51076) by eggs.gnu.org with esmtps (TLS1.0:RSA_AES_128_CBC_SHA1:16) (Exim 4.71) (envelope-from ) id 1hj4HH-0001yT-Pk for qemu-devel@nongnu.org; Thu, 04 Jul 2019 12:08:08 -0400 Received: by mail-wm1-x329.google.com with SMTP id n9so6259736wmi.0 for ; Thu, 04 Jul 2019 09:08:07 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linaro.org; s=google; h=from:to:subject:date:message-id:in-reply-to:references:mime-version :content-transfer-encoding; bh=fSFZ8Ir8Fwh9s0wZTfDaC/mvf3m/iW0XU/LH1HBLDR0=; b=NGcTqBRm2PynsN7hq/DXokeKxMcmvxGRIL5GVUOTbXMgJ+42s/oBXHB1X42ilUKRoK zo+yPN0hPJzIsMgy5784f4+fsyuGTukng97q3CVuq+1d1+LdVGKTC4XVBbI8PLEN8WVn CjV1XksWAGESasi3iGxEts3AZpmEhDgx8lK04+sDXcIpHv62NgZpPsezKuyNZb3wks4x VfZN85TA7ORvxIU6UnS7D4jteD04olzPOri+7AXdDwN5bUWUH8p95kTIhnTOdHO1yffy LBDI74CoHj47678hEgb7K/ajn/xa3P4eWDsvudOk94fTZmd2+5g0ggE/Gh2EjCqT83Tm Ah5g== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:subject:date:message-id:in-reply-to :references:mime-version:content-transfer-encoding; bh=fSFZ8Ir8Fwh9s0wZTfDaC/mvf3m/iW0XU/LH1HBLDR0=; b=FHyFTmaI9Y+Zr3kVZpTq+XGlQ2Q9EF2uNF10zWA1il+etEDGjn7ZhgcpZ5W2idfkMl PNL9FGx6AI7nDhwVz+xPgzGvpGq16D5BNSSpjTpeU7dsS5GDzWFBmGQTFnKL1hQ/koZk zm+O5WNMMYsoRl8kjpRC1FA2F2m3c4O4csTDWHMo+pCVnh4IQBVD1LTFAe5gGv4DqRFU a+v64EwqIAR9yb2a8Ncv+ZrDEaOE+i8TBk3R4M2HP3aCLJtac0GDZXvmdOPlHYx+bvVY hBWFC2qM4e/yqGpZ1GR7dnfAxFIFE0rs6VTZ8FqIyosr1Y6FJ6JxS3U9275RgGB8glD9 +pig== X-Gm-Message-State: APjAAAXNUQHMOjIWrgZF7OLnURodEHiV0sqg24EwVwmZ/BXgDfLogthm m/0oAWrtthAAy/ZyHFfOTnXqvpzImcbENQ== X-Google-Smtp-Source: APXvYqyJlDAcfRbiWzNIuaEcbZOljVo3CgcXLa6XpGvVUORxx/niCM3QqgyXvajyvoNw6R0+xsuGHg== X-Received: by 2002:a1c:a7c6:: with SMTP id q189mr211113wme.146.1562256486068; Thu, 04 Jul 2019 09:08:06 -0700 (PDT) Received: from orth.archaic.org.uk (orth.archaic.org.uk. [81.2.115.148]) by smtp.gmail.com with ESMTPSA id s2sm3849690wmj.33.2019.07.04.09.08.04 for (version=TLS1_3 cipher=AEAD-AES256-GCM-SHA384 bits=256/256); Thu, 04 Jul 2019 09:08:05 -0700 (PDT) From: Peter Maydell To: qemu-devel@nongnu.org Date: Thu, 4 Jul 2019 17:07:54 +0100 Message-Id: <20190704160802.12419-2-peter.maydell@linaro.org> X-Mailer: git-send-email 2.20.1 In-Reply-To: <20190704160802.12419-1-peter.maydell@linaro.org> References: <20190704160802.12419-1-peter.maydell@linaro.org> MIME-Version: 1.0 X-detected-operating-system: by eggs.gnu.org: Genre and OS details not recognized. X-Received-From: 2a00:1450:4864:20::329 Subject: [Qemu-devel] [PULL 1/9] target/arm: Move debug routines to debug_helper.c X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.23 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: qemu-devel-bounces+patchwork-qemu-devel=patchwork.kernel.org@nongnu.org Sender: "Qemu-devel" X-Virus-Scanned: ClamAV using ClamSMTP From: Philippe Mathieu-Daudé These routines are TCG specific. Signed-off-by: Philippe Mathieu-Daudé Message-id: 20190701194942.10092-2-philmd@redhat.com Reviewed-by: Peter Maydell Signed-off-by: Peter Maydell --- target/arm/Makefile.objs | 2 +- target/arm/cpu.c | 9 +- target/arm/debug_helper.c | 311 ++++++++++++++++++++++++++++++++++++++ target/arm/op_helper.c | 295 ------------------------------------ 4 files changed, 315 insertions(+), 302 deletions(-) create mode 100644 target/arm/debug_helper.c diff --git a/target/arm/Makefile.objs b/target/arm/Makefile.objs index 5c154f01c58..294433da880 100644 --- a/target/arm/Makefile.objs +++ b/target/arm/Makefile.objs @@ -32,7 +32,7 @@ target/arm/translate-sve.o: target/arm/decode-sve.inc.c target/arm/translate.o: target/arm/decode-vfp.inc.c target/arm/translate.o: target/arm/decode-vfp-uncond.inc.c -obj-y += tlb_helper.o +obj-y += tlb_helper.o debug_helper.o obj-y += translate.o op_helper.o obj-y += crypto_helper.o obj-y += iwmmxt_helper.o vec_helper.o neon_helper.o diff --git a/target/arm/cpu.c b/target/arm/cpu.c index f21261c8ff8..ca718fb38f4 100644 --- a/target/arm/cpu.c +++ b/target/arm/cpu.c @@ -2578,19 +2578,16 @@ static void arm_cpu_class_init(ObjectClass *oc, void *data) cc->gdb_arch_name = arm_gdb_arch_name; cc->gdb_get_dynamic_xml = arm_gdb_get_dynamic_xml; cc->gdb_stop_before_watchpoint = true; - cc->debug_excp_handler = arm_debug_excp_handler; - cc->debug_check_watchpoint = arm_debug_check_watchpoint; -#if !defined(CONFIG_USER_ONLY) - cc->adjust_watchpoint_address = arm_adjust_watchpoint_address; -#endif - cc->disas_set_info = arm_disas_set_info; #ifdef CONFIG_TCG cc->tcg_initialize = arm_translate_init; cc->tlb_fill = arm_cpu_tlb_fill; + cc->debug_excp_handler = arm_debug_excp_handler; + cc->debug_check_watchpoint = arm_debug_check_watchpoint; #if !defined(CONFIG_USER_ONLY) cc->do_unaligned_access = arm_cpu_do_unaligned_access; cc->do_transaction_failed = arm_cpu_do_transaction_failed; + cc->adjust_watchpoint_address = arm_adjust_watchpoint_address; #endif /* CONFIG_TCG && !CONFIG_USER_ONLY */ #endif } diff --git a/target/arm/debug_helper.c b/target/arm/debug_helper.c new file mode 100644 index 00000000000..dde80273ff1 --- /dev/null +++ b/target/arm/debug_helper.c @@ -0,0 +1,311 @@ +/* + * ARM debug helpers. + * + * This code is licensed under the GNU GPL v2 or later. + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ +#include "qemu/osdep.h" +#include "cpu.h" +#include "internals.h" +#include "exec/exec-all.h" +#include "exec/helper-proto.h" + +/* Return true if the linked breakpoint entry lbn passes its checks */ +static bool linked_bp_matches(ARMCPU *cpu, int lbn) +{ + CPUARMState *env = &cpu->env; + uint64_t bcr = env->cp15.dbgbcr[lbn]; + int brps = extract32(cpu->dbgdidr, 24, 4); + int ctx_cmps = extract32(cpu->dbgdidr, 20, 4); + int bt; + uint32_t contextidr; + + /* + * Links to unimplemented or non-context aware breakpoints are + * CONSTRAINED UNPREDICTABLE: either behave as if disabled, or + * as if linked to an UNKNOWN context-aware breakpoint (in which + * case DBGWCR_EL1.LBN must indicate that breakpoint). + * We choose the former. + */ + if (lbn > brps || lbn < (brps - ctx_cmps)) { + return false; + } + + bcr = env->cp15.dbgbcr[lbn]; + + if (extract64(bcr, 0, 1) == 0) { + /* Linked breakpoint disabled : generate no events */ + return false; + } + + bt = extract64(bcr, 20, 4); + + /* + * We match the whole register even if this is AArch32 using the + * short descriptor format (in which case it holds both PROCID and ASID), + * since we don't implement the optional v7 context ID masking. + */ + contextidr = extract64(env->cp15.contextidr_el[1], 0, 32); + + switch (bt) { + case 3: /* linked context ID match */ + if (arm_current_el(env) > 1) { + /* Context matches never fire in EL2 or (AArch64) EL3 */ + return false; + } + return (contextidr == extract64(env->cp15.dbgbvr[lbn], 0, 32)); + case 5: /* linked address mismatch (reserved in AArch64) */ + case 9: /* linked VMID match (reserved if no EL2) */ + case 11: /* linked context ID and VMID match (reserved if no EL2) */ + default: + /* + * Links to Unlinked context breakpoints must generate no + * events; we choose to do the same for reserved values too. + */ + return false; + } + + return false; +} + +static bool bp_wp_matches(ARMCPU *cpu, int n, bool is_wp) +{ + CPUARMState *env = &cpu->env; + uint64_t cr; + int pac, hmc, ssc, wt, lbn; + /* + * Note that for watchpoints the check is against the CPU security + * state, not the S/NS attribute on the offending data access. + */ + bool is_secure = arm_is_secure(env); + int access_el = arm_current_el(env); + + if (is_wp) { + CPUWatchpoint *wp = env->cpu_watchpoint[n]; + + if (!wp || !(wp->flags & BP_WATCHPOINT_HIT)) { + return false; + } + cr = env->cp15.dbgwcr[n]; + if (wp->hitattrs.user) { + /* + * The LDRT/STRT/LDT/STT "unprivileged access" instructions should + * match watchpoints as if they were accesses done at EL0, even if + * the CPU is at EL1 or higher. + */ + access_el = 0; + } + } else { + uint64_t pc = is_a64(env) ? env->pc : env->regs[15]; + + if (!env->cpu_breakpoint[n] || env->cpu_breakpoint[n]->pc != pc) { + return false; + } + cr = env->cp15.dbgbcr[n]; + } + /* + * The WATCHPOINT_HIT flag guarantees us that the watchpoint is + * enabled and that the address and access type match; for breakpoints + * we know the address matched; check the remaining fields, including + * linked breakpoints. We rely on WCR and BCR having the same layout + * for the LBN, SSC, HMC, PAC/PMC and is-linked fields. + * Note that some combinations of {PAC, HMC, SSC} are reserved and + * must act either like some valid combination or as if the watchpoint + * were disabled. We choose the former, and use this together with + * the fact that EL3 must always be Secure and EL2 must always be + * Non-Secure to simplify the code slightly compared to the full + * table in the ARM ARM. + */ + pac = extract64(cr, 1, 2); + hmc = extract64(cr, 13, 1); + ssc = extract64(cr, 14, 2); + + switch (ssc) { + case 0: + break; + case 1: + case 3: + if (is_secure) { + return false; + } + break; + case 2: + if (!is_secure) { + return false; + } + break; + } + + switch (access_el) { + case 3: + case 2: + if (!hmc) { + return false; + } + break; + case 1: + if (extract32(pac, 0, 1) == 0) { + return false; + } + break; + case 0: + if (extract32(pac, 1, 1) == 0) { + return false; + } + break; + default: + g_assert_not_reached(); + } + + wt = extract64(cr, 20, 1); + lbn = extract64(cr, 16, 4); + + if (wt && !linked_bp_matches(cpu, lbn)) { + return false; + } + + return true; +} + +static bool check_watchpoints(ARMCPU *cpu) +{ + CPUARMState *env = &cpu->env; + int n; + + /* + * If watchpoints are disabled globally or we can't take debug + * exceptions here then watchpoint firings are ignored. + */ + if (extract32(env->cp15.mdscr_el1, 15, 1) == 0 + || !arm_generate_debug_exceptions(env)) { + return false; + } + + for (n = 0; n < ARRAY_SIZE(env->cpu_watchpoint); n++) { + if (bp_wp_matches(cpu, n, true)) { + return true; + } + } + return false; +} + +static bool check_breakpoints(ARMCPU *cpu) +{ + CPUARMState *env = &cpu->env; + int n; + + /* + * If breakpoints are disabled globally or we can't take debug + * exceptions here then breakpoint firings are ignored. + */ + if (extract32(env->cp15.mdscr_el1, 15, 1) == 0 + || !arm_generate_debug_exceptions(env)) { + return false; + } + + for (n = 0; n < ARRAY_SIZE(env->cpu_breakpoint); n++) { + if (bp_wp_matches(cpu, n, false)) { + return true; + } + } + return false; +} + +void HELPER(check_breakpoints)(CPUARMState *env) +{ + ARMCPU *cpu = env_archcpu(env); + + if (check_breakpoints(cpu)) { + HELPER(exception_internal(env, EXCP_DEBUG)); + } +} + +bool arm_debug_check_watchpoint(CPUState *cs, CPUWatchpoint *wp) +{ + /* + * Called by core code when a CPU watchpoint fires; need to check if this + * is also an architectural watchpoint match. + */ + ARMCPU *cpu = ARM_CPU(cs); + + return check_watchpoints(cpu); +} + +void arm_debug_excp_handler(CPUState *cs) +{ + /* + * Called by core code when a watchpoint or breakpoint fires; + * need to check which one and raise the appropriate exception. + */ + ARMCPU *cpu = ARM_CPU(cs); + CPUARMState *env = &cpu->env; + CPUWatchpoint *wp_hit = cs->watchpoint_hit; + + if (wp_hit) { + if (wp_hit->flags & BP_CPU) { + bool wnr = (wp_hit->flags & BP_WATCHPOINT_HIT_WRITE) != 0; + bool same_el = arm_debug_target_el(env) == arm_current_el(env); + + cs->watchpoint_hit = NULL; + + env->exception.fsr = arm_debug_exception_fsr(env); + env->exception.vaddress = wp_hit->hitaddr; + raise_exception(env, EXCP_DATA_ABORT, + syn_watchpoint(same_el, 0, wnr), + arm_debug_target_el(env)); + } + } else { + uint64_t pc = is_a64(env) ? env->pc : env->regs[15]; + bool same_el = (arm_debug_target_el(env) == arm_current_el(env)); + + /* + * (1) GDB breakpoints should be handled first. + * (2) Do not raise a CPU exception if no CPU breakpoint has fired, + * since singlestep is also done by generating a debug internal + * exception. + */ + if (cpu_breakpoint_test(cs, pc, BP_GDB) + || !cpu_breakpoint_test(cs, pc, BP_CPU)) { + return; + } + + env->exception.fsr = arm_debug_exception_fsr(env); + /* + * FAR is UNKNOWN: clear vaddress to avoid potentially exposing + * values to the guest that it shouldn't be able to see at its + * exception/security level. + */ + env->exception.vaddress = 0; + raise_exception(env, EXCP_PREFETCH_ABORT, + syn_breakpoint(same_el), + arm_debug_target_el(env)); + } +} + +#if !defined(CONFIG_USER_ONLY) + +vaddr arm_adjust_watchpoint_address(CPUState *cs, vaddr addr, int len) +{ + ARMCPU *cpu = ARM_CPU(cs); + CPUARMState *env = &cpu->env; + + /* + * In BE32 system mode, target memory is stored byteswapped (on a + * little-endian host system), and by the time we reach here (via an + * opcode helper) the addresses of subword accesses have been adjusted + * to account for that, which means that watchpoints will not match. + * Undo the adjustment here. + */ + if (arm_sctlr_b(env)) { + if (len == 1) { + addr ^= 3; + } else if (len == 2) { + addr ^= 2; + } + } + + return addr; +} + +#endif diff --git a/target/arm/op_helper.c b/target/arm/op_helper.c index 9850993c114..1ab91f915e4 100644 --- a/target/arm/op_helper.c +++ b/target/arm/op_helper.c @@ -831,301 +831,6 @@ void HELPER(pre_smc)(CPUARMState *env, uint32_t syndrome) } } -/* Return true if the linked breakpoint entry lbn passes its checks */ -static bool linked_bp_matches(ARMCPU *cpu, int lbn) -{ - CPUARMState *env = &cpu->env; - uint64_t bcr = env->cp15.dbgbcr[lbn]; - int brps = extract32(cpu->dbgdidr, 24, 4); - int ctx_cmps = extract32(cpu->dbgdidr, 20, 4); - int bt; - uint32_t contextidr; - - /* - * Links to unimplemented or non-context aware breakpoints are - * CONSTRAINED UNPREDICTABLE: either behave as if disabled, or - * as if linked to an UNKNOWN context-aware breakpoint (in which - * case DBGWCR_EL1.LBN must indicate that breakpoint). - * We choose the former. - */ - if (lbn > brps || lbn < (brps - ctx_cmps)) { - return false; - } - - bcr = env->cp15.dbgbcr[lbn]; - - if (extract64(bcr, 0, 1) == 0) { - /* Linked breakpoint disabled : generate no events */ - return false; - } - - bt = extract64(bcr, 20, 4); - - /* - * We match the whole register even if this is AArch32 using the - * short descriptor format (in which case it holds both PROCID and ASID), - * since we don't implement the optional v7 context ID masking. - */ - contextidr = extract64(env->cp15.contextidr_el[1], 0, 32); - - switch (bt) { - case 3: /* linked context ID match */ - if (arm_current_el(env) > 1) { - /* Context matches never fire in EL2 or (AArch64) EL3 */ - return false; - } - return (contextidr == extract64(env->cp15.dbgbvr[lbn], 0, 32)); - case 5: /* linked address mismatch (reserved in AArch64) */ - case 9: /* linked VMID match (reserved if no EL2) */ - case 11: /* linked context ID and VMID match (reserved if no EL2) */ - default: - /* - * Links to Unlinked context breakpoints must generate no - * events; we choose to do the same for reserved values too. - */ - return false; - } - - return false; -} - -static bool bp_wp_matches(ARMCPU *cpu, int n, bool is_wp) -{ - CPUARMState *env = &cpu->env; - uint64_t cr; - int pac, hmc, ssc, wt, lbn; - /* - * Note that for watchpoints the check is against the CPU security - * state, not the S/NS attribute on the offending data access. - */ - bool is_secure = arm_is_secure(env); - int access_el = arm_current_el(env); - - if (is_wp) { - CPUWatchpoint *wp = env->cpu_watchpoint[n]; - - if (!wp || !(wp->flags & BP_WATCHPOINT_HIT)) { - return false; - } - cr = env->cp15.dbgwcr[n]; - if (wp->hitattrs.user) { - /* - * The LDRT/STRT/LDT/STT "unprivileged access" instructions should - * match watchpoints as if they were accesses done at EL0, even if - * the CPU is at EL1 or higher. - */ - access_el = 0; - } - } else { - uint64_t pc = is_a64(env) ? env->pc : env->regs[15]; - - if (!env->cpu_breakpoint[n] || env->cpu_breakpoint[n]->pc != pc) { - return false; - } - cr = env->cp15.dbgbcr[n]; - } - /* - * The WATCHPOINT_HIT flag guarantees us that the watchpoint is - * enabled and that the address and access type match; for breakpoints - * we know the address matched; check the remaining fields, including - * linked breakpoints. We rely on WCR and BCR having the same layout - * for the LBN, SSC, HMC, PAC/PMC and is-linked fields. - * Note that some combinations of {PAC, HMC, SSC} are reserved and - * must act either like some valid combination or as if the watchpoint - * were disabled. We choose the former, and use this together with - * the fact that EL3 must always be Secure and EL2 must always be - * Non-Secure to simplify the code slightly compared to the full - * table in the ARM ARM. - */ - pac = extract64(cr, 1, 2); - hmc = extract64(cr, 13, 1); - ssc = extract64(cr, 14, 2); - - switch (ssc) { - case 0: - break; - case 1: - case 3: - if (is_secure) { - return false; - } - break; - case 2: - if (!is_secure) { - return false; - } - break; - } - - switch (access_el) { - case 3: - case 2: - if (!hmc) { - return false; - } - break; - case 1: - if (extract32(pac, 0, 1) == 0) { - return false; - } - break; - case 0: - if (extract32(pac, 1, 1) == 0) { - return false; - } - break; - default: - g_assert_not_reached(); - } - - wt = extract64(cr, 20, 1); - lbn = extract64(cr, 16, 4); - - if (wt && !linked_bp_matches(cpu, lbn)) { - return false; - } - - return true; -} - -static bool check_watchpoints(ARMCPU *cpu) -{ - CPUARMState *env = &cpu->env; - int n; - - /* - * If watchpoints are disabled globally or we can't take debug - * exceptions here then watchpoint firings are ignored. - */ - if (extract32(env->cp15.mdscr_el1, 15, 1) == 0 - || !arm_generate_debug_exceptions(env)) { - return false; - } - - for (n = 0; n < ARRAY_SIZE(env->cpu_watchpoint); n++) { - if (bp_wp_matches(cpu, n, true)) { - return true; - } - } - return false; -} - -static bool check_breakpoints(ARMCPU *cpu) -{ - CPUARMState *env = &cpu->env; - int n; - - /* - * If breakpoints are disabled globally or we can't take debug - * exceptions here then breakpoint firings are ignored. - */ - if (extract32(env->cp15.mdscr_el1, 15, 1) == 0 - || !arm_generate_debug_exceptions(env)) { - return false; - } - - for (n = 0; n < ARRAY_SIZE(env->cpu_breakpoint); n++) { - if (bp_wp_matches(cpu, n, false)) { - return true; - } - } - return false; -} - -void HELPER(check_breakpoints)(CPUARMState *env) -{ - ARMCPU *cpu = env_archcpu(env); - - if (check_breakpoints(cpu)) { - HELPER(exception_internal(env, EXCP_DEBUG)); - } -} - -bool arm_debug_check_watchpoint(CPUState *cs, CPUWatchpoint *wp) -{ - /* - * Called by core code when a CPU watchpoint fires; need to check if this - * is also an architectural watchpoint match. - */ - ARMCPU *cpu = ARM_CPU(cs); - - return check_watchpoints(cpu); -} - -vaddr arm_adjust_watchpoint_address(CPUState *cs, vaddr addr, int len) -{ - ARMCPU *cpu = ARM_CPU(cs); - CPUARMState *env = &cpu->env; - - /* - * In BE32 system mode, target memory is stored byteswapped (on a - * little-endian host system), and by the time we reach here (via an - * opcode helper) the addresses of subword accesses have been adjusted - * to account for that, which means that watchpoints will not match. - * Undo the adjustment here. - */ - if (arm_sctlr_b(env)) { - if (len == 1) { - addr ^= 3; - } else if (len == 2) { - addr ^= 2; - } - } - - return addr; -} - -void arm_debug_excp_handler(CPUState *cs) -{ - /* - * Called by core code when a watchpoint or breakpoint fires; - * need to check which one and raise the appropriate exception. - */ - ARMCPU *cpu = ARM_CPU(cs); - CPUARMState *env = &cpu->env; - CPUWatchpoint *wp_hit = cs->watchpoint_hit; - - if (wp_hit) { - if (wp_hit->flags & BP_CPU) { - bool wnr = (wp_hit->flags & BP_WATCHPOINT_HIT_WRITE) != 0; - bool same_el = arm_debug_target_el(env) == arm_current_el(env); - - cs->watchpoint_hit = NULL; - - env->exception.fsr = arm_debug_exception_fsr(env); - env->exception.vaddress = wp_hit->hitaddr; - raise_exception(env, EXCP_DATA_ABORT, - syn_watchpoint(same_el, 0, wnr), - arm_debug_target_el(env)); - } - } else { - uint64_t pc = is_a64(env) ? env->pc : env->regs[15]; - bool same_el = (arm_debug_target_el(env) == arm_current_el(env)); - - /* - * (1) GDB breakpoints should be handled first. - * (2) Do not raise a CPU exception if no CPU breakpoint has fired, - * since singlestep is also done by generating a debug internal - * exception. - */ - if (cpu_breakpoint_test(cs, pc, BP_GDB) - || !cpu_breakpoint_test(cs, pc, BP_CPU)) { - return; - } - - env->exception.fsr = arm_debug_exception_fsr(env); - /* - * FAR is UNKNOWN: clear vaddress to avoid potentially exposing - * values to the guest that it shouldn't be able to see at its - * exception/security level. - */ - env->exception.vaddress = 0; - raise_exception(env, EXCP_PREFETCH_ABORT, - syn_breakpoint(same_el), - arm_debug_target_el(env)); - } -} - /* ??? Flag setting arithmetic is awkward because we need to do comparisons. The only way to do that in TCG is a conditional branch, which clobbers all our temporaries. For now implement these as helper functions. */ From patchwork Thu Jul 4 16:07:55 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: Peter Maydell X-Patchwork-Id: 11031701 Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id D39831398 for ; Thu, 4 Jul 2019 16:12:06 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id C3A3428AB1 for ; Thu, 4 Jul 2019 16:12:06 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id B78E228AB6; Thu, 4 Jul 2019 16:12:06 +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=-5.0 required=2.0 tests=BAYES_00,DKIM_INVALID, DKIM_SIGNED,MAILING_LIST_MULTI,RCVD_IN_DNSWL_MED autolearn=ham version=3.3.1 Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) (using TLSv1.2 with cipher AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by mail.wl.linuxfoundation.org (Postfix) with ESMTPS id C238D28AB1 for ; Thu, 4 Jul 2019 16:12:04 +0000 (UTC) Received: from localhost ([::1]:47424 helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.86_2) (envelope-from ) id 1hj4L6-0004AD-5G for patchwork-qemu-devel@patchwork.kernel.org; Thu, 04 Jul 2019 12:12:04 -0400 Received: from eggs.gnu.org ([2001:470:142:3::10]:44132) by lists.gnu.org with esmtp (Exim 4.86_2) (envelope-from ) id 1hj4HK-0007s6-ED for qemu-devel@nongnu.org; Thu, 04 Jul 2019 12:08:12 -0400 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1hj4HJ-00021B-BW for qemu-devel@nongnu.org; Thu, 04 Jul 2019 12:08:10 -0400 Received: from mail-wm1-x336.google.com ([2a00:1450:4864:20::336]:37707) by eggs.gnu.org with esmtps (TLS1.0:RSA_AES_128_CBC_SHA1:16) (Exim 4.71) (envelope-from ) id 1hj4HJ-00020F-5C for qemu-devel@nongnu.org; Thu, 04 Jul 2019 12:08:09 -0400 Received: by mail-wm1-x336.google.com with SMTP id f17so6686070wme.2 for ; Thu, 04 Jul 2019 09:08:09 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linaro.org; s=google; h=from:to:subject:date:message-id:in-reply-to:references:mime-version :content-transfer-encoding; bh=VE11EFgntOX7Py1z1pIxszbPG3fn/oQDUqV4xv6+5lo=; b=PLz/uHrqH7NhLrA0ptL+BMwzByMhnXEx1PEocfmIeYN6gVyxX+Pk7gClle0ATmDKSF XAbI8XuyRCRr1NoF0ZFYnQdlpTjXDt9onHxAVRTiX40DJf1cg58tnRzg8i3CWirBj/wm yEGWdp3d3EWkftLFg+V3j+qvSg+FRTSP3JkUtWyn9fL+Y/U5zsfyIG8pH394LuWcawMd qFLD1sjx63/XZFnED5YcbzMIi3mAoblahSqr2kH4jQNDtqbTYYEImAJD7HWmOR/CFNoh /xpa3NhqIhX8WurMtOU5LL7Xbrkz1jYHrxFbdN3OYU2hQlzjSvztjWb7bCq+9x2CDuew 1g1Q== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:subject:date:message-id:in-reply-to :references:mime-version:content-transfer-encoding; bh=VE11EFgntOX7Py1z1pIxszbPG3fn/oQDUqV4xv6+5lo=; b=L3Y5RSjavaiYkwcU065UME+0eXEvU0j29BZpFSj88yDrm7yVEVSenF/vVCUNmzhy+/ 5RecwIarUK3gNhiVDy6KvvC609oy2N9dZxdK3sHqV/H5m5KC/0e0HcA/UeRNk4mNsynD VbY5VRQU3pREPv/tjssIWc13JXQ0hLUiOT/oeGm3NCAmfcnPQun9SOlv+FsH9HfQOnhT rpuHwjzvY7MLPY7d3dogpsJ1xw/HwUbv9LYzT5T/lfo992INkdWhhSb2vusfaFaTbk9D oHXeRw+wfrzI/qVlokdOSVWW01A/PX0iDS40m8PfFY4fwgb5wMcFl0N++97+JiLxOpbz yRyQ== X-Gm-Message-State: APjAAAX6FvIJF7uy0nnH5agvVjUNQfovM93OC6Kzg8JJ+vU4CvTojtjS r6ix+AtTnsoeOnq82nFFhsUOHJNQpf18nA== X-Google-Smtp-Source: APXvYqzQGXk1mEgOjrpVlEjyrQnD6ITeu4+Be+QwHmow2AxPrYo5B75OC/EL827veeOtvfOwcoeJDA== X-Received: by 2002:a05:600c:206:: with SMTP id 6mr184033wmi.73.1562256487826; Thu, 04 Jul 2019 09:08:07 -0700 (PDT) Received: from orth.archaic.org.uk (orth.archaic.org.uk. [81.2.115.148]) by smtp.gmail.com with ESMTPSA id s2sm3849690wmj.33.2019.07.04.09.08.06 for (version=TLS1_3 cipher=AEAD-AES256-GCM-SHA384 bits=256/256); Thu, 04 Jul 2019 09:08:06 -0700 (PDT) From: Peter Maydell To: qemu-devel@nongnu.org Date: Thu, 4 Jul 2019 17:07:55 +0100 Message-Id: <20190704160802.12419-3-peter.maydell@linaro.org> X-Mailer: git-send-email 2.20.1 In-Reply-To: <20190704160802.12419-1-peter.maydell@linaro.org> References: <20190704160802.12419-1-peter.maydell@linaro.org> MIME-Version: 1.0 X-detected-operating-system: by eggs.gnu.org: Genre and OS details not recognized. X-Received-From: 2a00:1450:4864:20::336 Subject: [Qemu-devel] [PULL 2/9] target/arm: Restrict semi-hosting to TCG X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.23 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: qemu-devel-bounces+patchwork-qemu-devel=patchwork.kernel.org@nongnu.org Sender: "Qemu-devel" X-Virus-Scanned: ClamAV using ClamSMTP From: Philippe Mathieu-Daudé Per Peter Maydell: Semihosting hooks either SVC or HLT instructions, and inside KVM both of those go to EL1, ie to the guest, and can't be trapped to KVM. Let check_for_semihosting() return False when not running on TCG. Signed-off-by: Philippe Mathieu-Daudé Message-id: 20190701194942.10092-3-philmd@redhat.com Reviewed-by: Peter Maydell Signed-off-by: Peter Maydell --- target/arm/Makefile.objs | 2 +- target/arm/cpu.h | 7 +++++++ target/arm/helper.c | 8 +++++++- 3 files changed, 15 insertions(+), 2 deletions(-) diff --git a/target/arm/Makefile.objs b/target/arm/Makefile.objs index 294433da880..82bedefc3d0 100644 --- a/target/arm/Makefile.objs +++ b/target/arm/Makefile.objs @@ -1,4 +1,4 @@ -obj-y += arm-semi.o +obj-$(CONFIG_TCG) += arm-semi.o obj-y += helper.o vfp_helper.o obj-y += cpu.o gdbstub.o obj-$(TARGET_AARCH64) += cpu64.o gdbstub64.o diff --git a/target/arm/cpu.h b/target/arm/cpu.h index a9be18660fd..94c990cddbd 100644 --- a/target/arm/cpu.h +++ b/target/arm/cpu.h @@ -964,7 +964,14 @@ static inline void aarch64_sve_change_el(CPUARMState *env, int o, { } #endif +#if !defined(CONFIG_TCG) +static inline target_ulong do_arm_semihosting(CPUARMState *env) +{ + g_assert_not_reached(); +} +#else target_ulong do_arm_semihosting(CPUARMState *env); +#endif void aarch64_sync_32_to_64(CPUARMState *env); void aarch64_sync_64_to_32(CPUARMState *env); diff --git a/target/arm/helper.c b/target/arm/helper.c index 9a1fe3b72ed..055bf831a61 100644 --- a/target/arm/helper.c +++ b/target/arm/helper.c @@ -20,7 +20,6 @@ #include "qemu/qemu-print.h" #include "exec/exec-all.h" #include "exec/cpu_ldst.h" -#include "arm_ldst.h" #include /* For crc32 */ #include "hw/semihosting/semihost.h" #include "sysemu/cpus.h" @@ -29,6 +28,9 @@ #include "qapi/qapi-commands-machine-target.h" #include "qapi/error.h" #include "qemu/guest-random.h" +#ifdef CONFIG_TCG +#include "arm_ldst.h" +#endif #define ARM_CPU_FREQ 1000000000 /* FIXME: 1 GHz, should be configurable */ @@ -10399,6 +10401,7 @@ static void arm_cpu_do_interrupt_aarch64(CPUState *cs) static inline bool check_for_semihosting(CPUState *cs) { +#ifdef CONFIG_TCG /* Check whether this exception is a semihosting call; if so * then handle it and return true; otherwise return false. */ @@ -10474,6 +10477,9 @@ static inline bool check_for_semihosting(CPUState *cs) env->regs[0] = do_arm_semihosting(env); return true; } +#else + return false; +#endif } /* Handle a CPU exception for A and R profile CPUs. From patchwork Thu Jul 4 16:07:56 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: Peter Maydell X-Patchwork-Id: 11031717 Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id A245C1510 for ; Thu, 4 Jul 2019 16:19:28 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 6F41328786 for ; Thu, 4 Jul 2019 16:19:28 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id 616C228AB7; Thu, 4 Jul 2019 16:19:28 +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=-5.0 required=2.0 tests=BAYES_00,DKIM_INVALID, DKIM_SIGNED,MAILING_LIST_MULTI,RCVD_IN_DNSWL_MED autolearn=ham version=3.3.1 Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) (using TLSv1.2 with cipher AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by mail.wl.linuxfoundation.org (Postfix) with ESMTPS id 4D26C28786 for ; Thu, 4 Jul 2019 16:19:20 +0000 (UTC) Received: from localhost ([::1]:47518 helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.86_2) (envelope-from ) id 1hj4S7-0002jm-LO for patchwork-qemu-devel@patchwork.kernel.org; Thu, 04 Jul 2019 12:19:19 -0400 Received: from eggs.gnu.org ([2001:470:142:3::10]:44248) by lists.gnu.org with esmtp (Exim 4.86_2) (envelope-from ) id 1hj4Hf-00085L-V6 for qemu-devel@nongnu.org; Thu, 04 Jul 2019 12:08:43 -0400 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1hj4HU-0002CC-7x for qemu-devel@nongnu.org; Thu, 04 Jul 2019 12:08:31 -0400 Received: from mail-wr1-x42a.google.com ([2a00:1450:4864:20::42a]:39639) by eggs.gnu.org with esmtps (TLS1.0:RSA_AES_128_CBC_SHA1:16) (Exim 4.71) (envelope-from ) id 1hj4HQ-00027C-Dg for qemu-devel@nongnu.org; Thu, 04 Jul 2019 12:08:18 -0400 Received: by mail-wr1-x42a.google.com with SMTP id x4so7155540wrt.6 for ; Thu, 04 Jul 2019 09:08:15 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linaro.org; s=google; h=from:to:subject:date:message-id:in-reply-to:references:mime-version :content-transfer-encoding; bh=Q0sAfYtwNRYCH1QcxnZHyABxYtrggmzJ3Jt/NVor5to=; b=tDlv0K/fe23BOLDhhCaUVwzs0r519q3ptD/y+Cz9V7HRtp6b4V6IXUMFicmtvpP+tH BXnn2AIW1c42Plj5tpFjFpItOVSIBGas/Nxc9ChIWIGXz0KMXkv2EwaN6lYJ1yDvpung 15s3Q72o6Sxb4dmXyZcQcqjS/GJqQ4oZ1++FZoNCvswW+R4RNfmPPptv4NmLt3M+a7j3 /QuABuBx0u0cDEI1nBd2cEObhESKAWVTe69AGozX5wxFmYAIggZzFYZCqQbYl7K27Mvc 2TNaXBzW+uNxQL6O2dXzRAuraUYS+rLobiohe0JseuAkXkKSL6T2J0shid3h9/xS6aeL kCAw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:subject:date:message-id:in-reply-to :references:mime-version:content-transfer-encoding; bh=Q0sAfYtwNRYCH1QcxnZHyABxYtrggmzJ3Jt/NVor5to=; b=eOWZbrJZhx/sK9Rw8ZZKWaYJ57WHL/MR13Ar0V8qjrLC0oXWYRTiAXY1SH/YlCBg/K zUjvNkeF38+RGRyIrtrgFT7D8nlfuLDDwea/0GWYNWufZZrZykwQO6MFnRxrJutJso5b 2XHMt/DUn73Jm1noXtxOJaAbNbadoe6jZd09ykSB6BDOfKOfDqdjv9gVPf2eJEhj5SVM TZl79fhBHdKREjVrBtyaw3cKCM4eerI8e1b3LyqSvo0ABuYqcyPMnJyIYL9/MhUbBMav 9JL5Fm2O0R4Pc4CIGrPX7D7sUrSQuNmGWo+pglZT1e+QRdjuh6TmTw3/QXaz1W8qqr9U 2Sgw== X-Gm-Message-State: APjAAAUFXNizxTwO8ryVD5tsi6L0sbZQC7uEGRuzNJPYlOGamCxNgifk dObDXJ0YexdiKzhpTfnXXrO0dnWH7K65sg== X-Google-Smtp-Source: APXvYqyZo56wLmUd1avewTl+ygmzRCMS0LDXhtO7SGUF3CT4q9U+6O32jg6yOG6LpvR0QULQDlmaMw== X-Received: by 2002:adf:eb89:: with SMTP id t9mr33301836wrn.120.1562256492284; Thu, 04 Jul 2019 09:08:12 -0700 (PDT) Received: from orth.archaic.org.uk (orth.archaic.org.uk. [81.2.115.148]) by smtp.gmail.com with ESMTPSA id s2sm3849690wmj.33.2019.07.04.09.08.07 for (version=TLS1_3 cipher=AEAD-AES256-GCM-SHA384 bits=256/256); Thu, 04 Jul 2019 09:08:11 -0700 (PDT) From: Peter Maydell To: qemu-devel@nongnu.org Date: Thu, 4 Jul 2019 17:07:56 +0100 Message-Id: <20190704160802.12419-4-peter.maydell@linaro.org> X-Mailer: git-send-email 2.20.1 In-Reply-To: <20190704160802.12419-1-peter.maydell@linaro.org> References: <20190704160802.12419-1-peter.maydell@linaro.org> MIME-Version: 1.0 X-detected-operating-system: by eggs.gnu.org: Genre and OS details not recognized. X-Received-From: 2a00:1450:4864:20::42a Subject: [Qemu-devel] [PULL 3/9] target/arm/helper: Move M profile routines to m_helper.c X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.23 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: qemu-devel-bounces+patchwork-qemu-devel=patchwork.kernel.org@nongnu.org Sender: "Qemu-devel" X-Virus-Scanned: ClamAV using ClamSMTP From: Philippe Mathieu-Daudé In preparation for supporting TCG disablement on ARM, we move most of TCG related v7m/v8m helpers and APIs into their own file. Note: It is easier to review this commit using the 'histogram' diff algorithm: $ git diff --diff-algorithm=histogram ... or $ git diff --histogram ... Suggested-by: Samuel Ortiz Signed-off-by: Philippe Mathieu-Daudé Message-id: 20190702144335.10717-2-philmd@redhat.com Reviewed-by: Peter Maydell Signed-off-by: Peter Maydell --- target/arm/Makefile.objs | 1 + target/arm/helper.c | 2638 +------------------------------------ target/arm/m_helper.c | 2676 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 2681 insertions(+), 2634 deletions(-) create mode 100644 target/arm/m_helper.c diff --git a/target/arm/Makefile.objs b/target/arm/Makefile.objs index 82bedefc3d0..5cafc1eb6c8 100644 --- a/target/arm/Makefile.objs +++ b/target/arm/Makefile.objs @@ -36,6 +36,7 @@ obj-y += tlb_helper.o debug_helper.o obj-y += translate.o op_helper.o obj-y += crypto_helper.o obj-y += iwmmxt_helper.o vec_helper.o neon_helper.o +obj-y += m_helper.o obj-$(CONFIG_SOFTMMU) += psci.o diff --git a/target/arm/helper.c b/target/arm/helper.c index 055bf831a61..2df7152a9cd 100644 --- a/target/arm/helper.c +++ b/target/arm/helper.c @@ -19,7 +19,6 @@ #include "qemu/crc32c.h" #include "qemu/qemu-print.h" #include "exec/exec-all.h" -#include "exec/cpu_ldst.h" #include /* For crc32 */ #include "hw/semihosting/semihost.h" #include "sysemu/cpus.h" @@ -30,6 +29,7 @@ #include "qemu/guest-random.h" #ifdef CONFIG_TCG #include "arm_ldst.h" +#include "exec/cpu_ldst.h" #endif #define ARM_CPU_FREQ 1000000000 /* FIXME: 1 GHz, should be configurable */ @@ -7457,75 +7457,6 @@ uint32_t HELPER(rbit)(uint32_t x) #ifdef CONFIG_USER_ONLY -/* These should probably raise undefined insn exceptions. */ -void HELPER(v7m_msr)(CPUARMState *env, uint32_t reg, uint32_t val) -{ - ARMCPU *cpu = env_archcpu(env); - - cpu_abort(CPU(cpu), "v7m_msr %d\n", reg); -} - -uint32_t HELPER(v7m_mrs)(CPUARMState *env, uint32_t reg) -{ - ARMCPU *cpu = env_archcpu(env); - - cpu_abort(CPU(cpu), "v7m_mrs %d\n", reg); - return 0; -} - -void HELPER(v7m_bxns)(CPUARMState *env, uint32_t dest) -{ - /* translate.c should never generate calls here in user-only mode */ - g_assert_not_reached(); -} - -void HELPER(v7m_blxns)(CPUARMState *env, uint32_t dest) -{ - /* translate.c should never generate calls here in user-only mode */ - g_assert_not_reached(); -} - -void HELPER(v7m_preserve_fp_state)(CPUARMState *env) -{ - /* translate.c should never generate calls here in user-only mode */ - g_assert_not_reached(); -} - -void HELPER(v7m_vlstm)(CPUARMState *env, uint32_t fptr) -{ - /* translate.c should never generate calls here in user-only mode */ - g_assert_not_reached(); -} - -void HELPER(v7m_vlldm)(CPUARMState *env, uint32_t fptr) -{ - /* translate.c should never generate calls here in user-only mode */ - g_assert_not_reached(); -} - -uint32_t HELPER(v7m_tt)(CPUARMState *env, uint32_t addr, uint32_t op) -{ - /* - * The TT instructions can be used by unprivileged code, but in - * user-only emulation we don't have the MPU. - * Luckily since we know we are NonSecure unprivileged (and that in - * turn means that the A flag wasn't specified), all the bits in the - * register must be zero: - * IREGION: 0 because IRVALID is 0 - * IRVALID: 0 because NS - * S: 0 because NS - * NSRW: 0 because NS - * NSR: 0 because NS - * RW: 0 because unpriv and A flag not set - * R: 0 because unpriv and A flag not set - * SRVALID: 0 because NS - * MRVALID: 0 because unpriv and A flag not set - * SREGION: 0 becaus SRVALID is 0 - * MREGION: 0 because MRVALID is 0 - */ - return 0; -} - static void switch_mode(CPUARMState *env, int mode) { ARMCPU *cpu = env_archcpu(env); @@ -7720,2078 +7651,6 @@ void arm_log_exception(int idx) } } -/* - * What kind of stack write are we doing? This affects how exceptions - * generated during the stacking are treated. - */ -typedef enum StackingMode { - STACK_NORMAL, - STACK_IGNFAULTS, - STACK_LAZYFP, -} StackingMode; - -static bool v7m_stack_write(ARMCPU *cpu, uint32_t addr, uint32_t value, - ARMMMUIdx mmu_idx, StackingMode mode) -{ - CPUState *cs = CPU(cpu); - CPUARMState *env = &cpu->env; - MemTxAttrs attrs = {}; - MemTxResult txres; - target_ulong page_size; - hwaddr physaddr; - int prot; - ARMMMUFaultInfo fi = {}; - bool secure = mmu_idx & ARM_MMU_IDX_M_S; - int exc; - bool exc_secure; - - if (get_phys_addr(env, addr, MMU_DATA_STORE, mmu_idx, &physaddr, - &attrs, &prot, &page_size, &fi, NULL)) { - /* MPU/SAU lookup failed */ - if (fi.type == ARMFault_QEMU_SFault) { - if (mode == STACK_LAZYFP) { - qemu_log_mask(CPU_LOG_INT, - "...SecureFault with SFSR.LSPERR " - "during lazy stacking\n"); - env->v7m.sfsr |= R_V7M_SFSR_LSPERR_MASK; - } else { - qemu_log_mask(CPU_LOG_INT, - "...SecureFault with SFSR.AUVIOL " - "during stacking\n"); - env->v7m.sfsr |= R_V7M_SFSR_AUVIOL_MASK; - } - env->v7m.sfsr |= R_V7M_SFSR_SFARVALID_MASK; - env->v7m.sfar = addr; - exc = ARMV7M_EXCP_SECURE; - exc_secure = false; - } else { - if (mode == STACK_LAZYFP) { - qemu_log_mask(CPU_LOG_INT, - "...MemManageFault with CFSR.MLSPERR\n"); - env->v7m.cfsr[secure] |= R_V7M_CFSR_MLSPERR_MASK; - } else { - qemu_log_mask(CPU_LOG_INT, - "...MemManageFault with CFSR.MSTKERR\n"); - env->v7m.cfsr[secure] |= R_V7M_CFSR_MSTKERR_MASK; - } - exc = ARMV7M_EXCP_MEM; - exc_secure = secure; - } - goto pend_fault; - } - address_space_stl_le(arm_addressspace(cs, attrs), physaddr, value, - attrs, &txres); - if (txres != MEMTX_OK) { - /* BusFault trying to write the data */ - if (mode == STACK_LAZYFP) { - qemu_log_mask(CPU_LOG_INT, "...BusFault with BFSR.LSPERR\n"); - env->v7m.cfsr[M_REG_NS] |= R_V7M_CFSR_LSPERR_MASK; - } else { - qemu_log_mask(CPU_LOG_INT, "...BusFault with BFSR.STKERR\n"); - env->v7m.cfsr[M_REG_NS] |= R_V7M_CFSR_STKERR_MASK; - } - exc = ARMV7M_EXCP_BUS; - exc_secure = false; - goto pend_fault; - } - return true; - -pend_fault: - /* - * By pending the exception at this point we are making - * the IMPDEF choice "overridden exceptions pended" (see the - * MergeExcInfo() pseudocode). The other choice would be to not - * pend them now and then make a choice about which to throw away - * later if we have two derived exceptions. - * The only case when we must not pend the exception but instead - * throw it away is if we are doing the push of the callee registers - * and we've already generated a derived exception (this is indicated - * by the caller passing STACK_IGNFAULTS). Even in this case we will - * still update the fault status registers. - */ - switch (mode) { - case STACK_NORMAL: - armv7m_nvic_set_pending_derived(env->nvic, exc, exc_secure); - break; - case STACK_LAZYFP: - armv7m_nvic_set_pending_lazyfp(env->nvic, exc, exc_secure); - break; - case STACK_IGNFAULTS: - break; - } - return false; -} - -static bool v7m_stack_read(ARMCPU *cpu, uint32_t *dest, uint32_t addr, - ARMMMUIdx mmu_idx) -{ - CPUState *cs = CPU(cpu); - CPUARMState *env = &cpu->env; - MemTxAttrs attrs = {}; - MemTxResult txres; - target_ulong page_size; - hwaddr physaddr; - int prot; - ARMMMUFaultInfo fi = {}; - bool secure = mmu_idx & ARM_MMU_IDX_M_S; - int exc; - bool exc_secure; - uint32_t value; - - if (get_phys_addr(env, addr, MMU_DATA_LOAD, mmu_idx, &physaddr, - &attrs, &prot, &page_size, &fi, NULL)) { - /* MPU/SAU lookup failed */ - if (fi.type == ARMFault_QEMU_SFault) { - qemu_log_mask(CPU_LOG_INT, - "...SecureFault with SFSR.AUVIOL during unstack\n"); - env->v7m.sfsr |= R_V7M_SFSR_AUVIOL_MASK | R_V7M_SFSR_SFARVALID_MASK; - env->v7m.sfar = addr; - exc = ARMV7M_EXCP_SECURE; - exc_secure = false; - } else { - qemu_log_mask(CPU_LOG_INT, - "...MemManageFault with CFSR.MUNSTKERR\n"); - env->v7m.cfsr[secure] |= R_V7M_CFSR_MUNSTKERR_MASK; - exc = ARMV7M_EXCP_MEM; - exc_secure = secure; - } - goto pend_fault; - } - - value = address_space_ldl(arm_addressspace(cs, attrs), physaddr, - attrs, &txres); - if (txres != MEMTX_OK) { - /* BusFault trying to read the data */ - qemu_log_mask(CPU_LOG_INT, "...BusFault with BFSR.UNSTKERR\n"); - env->v7m.cfsr[M_REG_NS] |= R_V7M_CFSR_UNSTKERR_MASK; - exc = ARMV7M_EXCP_BUS; - exc_secure = false; - goto pend_fault; - } - - *dest = value; - return true; - -pend_fault: - /* - * By pending the exception at this point we are making - * the IMPDEF choice "overridden exceptions pended" (see the - * MergeExcInfo() pseudocode). The other choice would be to not - * pend them now and then make a choice about which to throw away - * later if we have two derived exceptions. - */ - armv7m_nvic_set_pending(env->nvic, exc, exc_secure); - return false; -} - -void HELPER(v7m_preserve_fp_state)(CPUARMState *env) -{ - /* - * Preserve FP state (because LSPACT was set and we are about - * to execute an FP instruction). This corresponds to the - * PreserveFPState() pseudocode. - * We may throw an exception if the stacking fails. - */ - ARMCPU *cpu = env_archcpu(env); - bool is_secure = env->v7m.fpccr[M_REG_S] & R_V7M_FPCCR_S_MASK; - bool negpri = !(env->v7m.fpccr[M_REG_S] & R_V7M_FPCCR_HFRDY_MASK); - bool is_priv = !(env->v7m.fpccr[is_secure] & R_V7M_FPCCR_USER_MASK); - bool splimviol = env->v7m.fpccr[is_secure] & R_V7M_FPCCR_SPLIMVIOL_MASK; - uint32_t fpcar = env->v7m.fpcar[is_secure]; - bool stacked_ok = true; - bool ts = is_secure && (env->v7m.fpccr[M_REG_S] & R_V7M_FPCCR_TS_MASK); - bool take_exception; - - /* Take the iothread lock as we are going to touch the NVIC */ - qemu_mutex_lock_iothread(); - - /* Check the background context had access to the FPU */ - if (!v7m_cpacr_pass(env, is_secure, is_priv)) { - armv7m_nvic_set_pending_lazyfp(env->nvic, ARMV7M_EXCP_USAGE, is_secure); - env->v7m.cfsr[is_secure] |= R_V7M_CFSR_NOCP_MASK; - stacked_ok = false; - } else if (!is_secure && !extract32(env->v7m.nsacr, 10, 1)) { - armv7m_nvic_set_pending_lazyfp(env->nvic, ARMV7M_EXCP_USAGE, M_REG_S); - env->v7m.cfsr[M_REG_S] |= R_V7M_CFSR_NOCP_MASK; - stacked_ok = false; - } - - if (!splimviol && stacked_ok) { - /* We only stack if the stack limit wasn't violated */ - int i; - ARMMMUIdx mmu_idx; - - mmu_idx = arm_v7m_mmu_idx_all(env, is_secure, is_priv, negpri); - for (i = 0; i < (ts ? 32 : 16); i += 2) { - uint64_t dn = *aa32_vfp_dreg(env, i / 2); - uint32_t faddr = fpcar + 4 * i; - uint32_t slo = extract64(dn, 0, 32); - uint32_t shi = extract64(dn, 32, 32); - - if (i >= 16) { - faddr += 8; /* skip the slot for the FPSCR */ - } - stacked_ok = stacked_ok && - v7m_stack_write(cpu, faddr, slo, mmu_idx, STACK_LAZYFP) && - v7m_stack_write(cpu, faddr + 4, shi, mmu_idx, STACK_LAZYFP); - } - - stacked_ok = stacked_ok && - v7m_stack_write(cpu, fpcar + 0x40, - vfp_get_fpscr(env), mmu_idx, STACK_LAZYFP); - } - - /* - * We definitely pended an exception, but it's possible that it - * might not be able to be taken now. If its priority permits us - * to take it now, then we must not update the LSPACT or FP regs, - * but instead jump out to take the exception immediately. - * If it's just pending and won't be taken until the current - * handler exits, then we do update LSPACT and the FP regs. - */ - take_exception = !stacked_ok && - armv7m_nvic_can_take_pending_exception(env->nvic); - - qemu_mutex_unlock_iothread(); - - if (take_exception) { - raise_exception_ra(env, EXCP_LAZYFP, 0, 1, GETPC()); - } - - env->v7m.fpccr[is_secure] &= ~R_V7M_FPCCR_LSPACT_MASK; - - if (ts) { - /* Clear s0 to s31 and the FPSCR */ - int i; - - for (i = 0; i < 32; i += 2) { - *aa32_vfp_dreg(env, i / 2) = 0; - } - vfp_set_fpscr(env, 0); - } - /* - * Otherwise s0 to s15 and FPSCR are UNKNOWN; we choose to leave them - * unchanged. - */ -} - -/* - * Write to v7M CONTROL.SPSEL bit for the specified security bank. - * This may change the current stack pointer between Main and Process - * stack pointers if it is done for the CONTROL register for the current - * security state. - */ -static void write_v7m_control_spsel_for_secstate(CPUARMState *env, - bool new_spsel, - bool secstate) -{ - bool old_is_psp = v7m_using_psp(env); - - env->v7m.control[secstate] = - deposit32(env->v7m.control[secstate], - R_V7M_CONTROL_SPSEL_SHIFT, - R_V7M_CONTROL_SPSEL_LENGTH, new_spsel); - - if (secstate == env->v7m.secure) { - bool new_is_psp = v7m_using_psp(env); - uint32_t tmp; - - if (old_is_psp != new_is_psp) { - tmp = env->v7m.other_sp; - env->v7m.other_sp = env->regs[13]; - env->regs[13] = tmp; - } - } -} - -/* - * Write to v7M CONTROL.SPSEL bit. This may change the current - * stack pointer between Main and Process stack pointers. - */ -static void write_v7m_control_spsel(CPUARMState *env, bool new_spsel) -{ - write_v7m_control_spsel_for_secstate(env, new_spsel, env->v7m.secure); -} - -void write_v7m_exception(CPUARMState *env, uint32_t new_exc) -{ - /* - * Write a new value to v7m.exception, thus transitioning into or out - * of Handler mode; this may result in a change of active stack pointer. - */ - bool new_is_psp, old_is_psp = v7m_using_psp(env); - uint32_t tmp; - - env->v7m.exception = new_exc; - - new_is_psp = v7m_using_psp(env); - - if (old_is_psp != new_is_psp) { - tmp = env->v7m.other_sp; - env->v7m.other_sp = env->regs[13]; - env->regs[13] = tmp; - } -} - -/* Switch M profile security state between NS and S */ -static void switch_v7m_security_state(CPUARMState *env, bool new_secstate) -{ - uint32_t new_ss_msp, new_ss_psp; - - if (env->v7m.secure == new_secstate) { - return; - } - - /* - * All the banked state is accessed by looking at env->v7m.secure - * except for the stack pointer; rearrange the SP appropriately. - */ - new_ss_msp = env->v7m.other_ss_msp; - new_ss_psp = env->v7m.other_ss_psp; - - if (v7m_using_psp(env)) { - env->v7m.other_ss_psp = env->regs[13]; - env->v7m.other_ss_msp = env->v7m.other_sp; - } else { - env->v7m.other_ss_msp = env->regs[13]; - env->v7m.other_ss_psp = env->v7m.other_sp; - } - - env->v7m.secure = new_secstate; - - if (v7m_using_psp(env)) { - env->regs[13] = new_ss_psp; - env->v7m.other_sp = new_ss_msp; - } else { - env->regs[13] = new_ss_msp; - env->v7m.other_sp = new_ss_psp; - } -} - -void HELPER(v7m_bxns)(CPUARMState *env, uint32_t dest) -{ - /* - * Handle v7M BXNS: - * - if the return value is a magic value, do exception return (like BX) - * - otherwise bit 0 of the return value is the target security state - */ - uint32_t min_magic; - - if (arm_feature(env, ARM_FEATURE_M_SECURITY)) { - /* Covers FNC_RETURN and EXC_RETURN magic */ - min_magic = FNC_RETURN_MIN_MAGIC; - } else { - /* EXC_RETURN magic only */ - min_magic = EXC_RETURN_MIN_MAGIC; - } - - if (dest >= min_magic) { - /* - * This is an exception return magic value; put it where - * do_v7m_exception_exit() expects and raise EXCEPTION_EXIT. - * Note that if we ever add gen_ss_advance() singlestep support to - * M profile this should count as an "instruction execution complete" - * event (compare gen_bx_excret_final_code()). - */ - env->regs[15] = dest & ~1; - env->thumb = dest & 1; - HELPER(exception_internal)(env, EXCP_EXCEPTION_EXIT); - /* notreached */ - } - - /* translate.c should have made BXNS UNDEF unless we're secure */ - assert(env->v7m.secure); - - if (!(dest & 1)) { - env->v7m.control[M_REG_S] &= ~R_V7M_CONTROL_SFPA_MASK; - } - switch_v7m_security_state(env, dest & 1); - env->thumb = 1; - env->regs[15] = dest & ~1; -} - -void HELPER(v7m_blxns)(CPUARMState *env, uint32_t dest) -{ - /* - * Handle v7M BLXNS: - * - bit 0 of the destination address is the target security state - */ - - /* At this point regs[15] is the address just after the BLXNS */ - uint32_t nextinst = env->regs[15] | 1; - uint32_t sp = env->regs[13] - 8; - uint32_t saved_psr; - - /* translate.c will have made BLXNS UNDEF unless we're secure */ - assert(env->v7m.secure); - - if (dest & 1) { - /* - * Target is Secure, so this is just a normal BLX, - * except that the low bit doesn't indicate Thumb/not. - */ - env->regs[14] = nextinst; - env->thumb = 1; - env->regs[15] = dest & ~1; - return; - } - - /* Target is non-secure: first push a stack frame */ - if (!QEMU_IS_ALIGNED(sp, 8)) { - qemu_log_mask(LOG_GUEST_ERROR, - "BLXNS with misaligned SP is UNPREDICTABLE\n"); - } - - if (sp < v7m_sp_limit(env)) { - raise_exception(env, EXCP_STKOF, 0, 1); - } - - saved_psr = env->v7m.exception; - if (env->v7m.control[M_REG_S] & R_V7M_CONTROL_SFPA_MASK) { - saved_psr |= XPSR_SFPA; - } - - /* Note that these stores can throw exceptions on MPU faults */ - cpu_stl_data(env, sp, nextinst); - cpu_stl_data(env, sp + 4, saved_psr); - - env->regs[13] = sp; - env->regs[14] = 0xfeffffff; - if (arm_v7m_is_handler_mode(env)) { - /* - * Write a dummy value to IPSR, to avoid leaking the current secure - * exception number to non-secure code. This is guaranteed not - * to cause write_v7m_exception() to actually change stacks. - */ - write_v7m_exception(env, 1); - } - env->v7m.control[M_REG_S] &= ~R_V7M_CONTROL_SFPA_MASK; - switch_v7m_security_state(env, 0); - env->thumb = 1; - env->regs[15] = dest; -} - -static uint32_t *get_v7m_sp_ptr(CPUARMState *env, bool secure, bool threadmode, - bool spsel) -{ - /* - * Return a pointer to the location where we currently store the - * stack pointer for the requested security state and thread mode. - * This pointer will become invalid if the CPU state is updated - * such that the stack pointers are switched around (eg changing - * the SPSEL control bit). - * Compare the v8M ARM ARM pseudocode LookUpSP_with_security_mode(). - * Unlike that pseudocode, we require the caller to pass us in the - * SPSEL control bit value; this is because we also use this - * function in handling of pushing of the callee-saves registers - * part of the v8M stack frame (pseudocode PushCalleeStack()), - * and in the tailchain codepath the SPSEL bit comes from the exception - * return magic LR value from the previous exception. The pseudocode - * opencodes the stack-selection in PushCalleeStack(), but we prefer - * to make this utility function generic enough to do the job. - */ - bool want_psp = threadmode && spsel; - - if (secure == env->v7m.secure) { - if (want_psp == v7m_using_psp(env)) { - return &env->regs[13]; - } else { - return &env->v7m.other_sp; - } - } else { - if (want_psp) { - return &env->v7m.other_ss_psp; - } else { - return &env->v7m.other_ss_msp; - } - } -} - -static bool arm_v7m_load_vector(ARMCPU *cpu, int exc, bool targets_secure, - uint32_t *pvec) -{ - CPUState *cs = CPU(cpu); - CPUARMState *env = &cpu->env; - MemTxResult result; - uint32_t addr = env->v7m.vecbase[targets_secure] + exc * 4; - uint32_t vector_entry; - MemTxAttrs attrs = {}; - ARMMMUIdx mmu_idx; - bool exc_secure; - - mmu_idx = arm_v7m_mmu_idx_for_secstate_and_priv(env, targets_secure, true); - - /* - * We don't do a get_phys_addr() here because the rules for vector - * loads are special: they always use the default memory map, and - * the default memory map permits reads from all addresses. - * Since there's no easy way to pass through to pmsav8_mpu_lookup() - * that we want this special case which would always say "yes", - * we just do the SAU lookup here followed by a direct physical load. - */ - attrs.secure = targets_secure; - attrs.user = false; - - if (arm_feature(env, ARM_FEATURE_M_SECURITY)) { - V8M_SAttributes sattrs = {}; - - v8m_security_lookup(env, addr, MMU_DATA_LOAD, mmu_idx, &sattrs); - if (sattrs.ns) { - attrs.secure = false; - } else if (!targets_secure) { - /* NS access to S memory */ - goto load_fail; - } - } - - vector_entry = address_space_ldl(arm_addressspace(cs, attrs), addr, - attrs, &result); - if (result != MEMTX_OK) { - goto load_fail; - } - *pvec = vector_entry; - return true; - -load_fail: - /* - * All vector table fetch fails are reported as HardFault, with - * HFSR.VECTTBL and .FORCED set. (FORCED is set because - * technically the underlying exception is a MemManage or BusFault - * that is escalated to HardFault.) This is a terminal exception, - * so we will either take the HardFault immediately or else enter - * lockup (the latter case is handled in armv7m_nvic_set_pending_derived()). - */ - exc_secure = targets_secure || - !(cpu->env.v7m.aircr & R_V7M_AIRCR_BFHFNMINS_MASK); - env->v7m.hfsr |= R_V7M_HFSR_VECTTBL_MASK | R_V7M_HFSR_FORCED_MASK; - armv7m_nvic_set_pending_derived(env->nvic, ARMV7M_EXCP_HARD, exc_secure); - return false; -} - -static uint32_t v7m_integrity_sig(CPUARMState *env, uint32_t lr) -{ - /* - * Return the integrity signature value for the callee-saves - * stack frame section. @lr is the exception return payload/LR value - * whose FType bit forms bit 0 of the signature if FP is present. - */ - uint32_t sig = 0xfefa125a; - - if (!arm_feature(env, ARM_FEATURE_VFP) || (lr & R_V7M_EXCRET_FTYPE_MASK)) { - sig |= 1; - } - return sig; -} - -static bool v7m_push_callee_stack(ARMCPU *cpu, uint32_t lr, bool dotailchain, - bool ignore_faults) -{ - /* - * For v8M, push the callee-saves register part of the stack frame. - * Compare the v8M pseudocode PushCalleeStack(). - * In the tailchaining case this may not be the current stack. - */ - CPUARMState *env = &cpu->env; - uint32_t *frame_sp_p; - uint32_t frameptr; - ARMMMUIdx mmu_idx; - bool stacked_ok; - uint32_t limit; - bool want_psp; - uint32_t sig; - StackingMode smode = ignore_faults ? STACK_IGNFAULTS : STACK_NORMAL; - - if (dotailchain) { - bool mode = lr & R_V7M_EXCRET_MODE_MASK; - bool priv = !(env->v7m.control[M_REG_S] & R_V7M_CONTROL_NPRIV_MASK) || - !mode; - - mmu_idx = arm_v7m_mmu_idx_for_secstate_and_priv(env, M_REG_S, priv); - frame_sp_p = get_v7m_sp_ptr(env, M_REG_S, mode, - lr & R_V7M_EXCRET_SPSEL_MASK); - want_psp = mode && (lr & R_V7M_EXCRET_SPSEL_MASK); - if (want_psp) { - limit = env->v7m.psplim[M_REG_S]; - } else { - limit = env->v7m.msplim[M_REG_S]; - } - } else { - mmu_idx = arm_mmu_idx(env); - frame_sp_p = &env->regs[13]; - limit = v7m_sp_limit(env); - } - - frameptr = *frame_sp_p - 0x28; - if (frameptr < limit) { - /* - * Stack limit failure: set SP to the limit value, and generate - * STKOF UsageFault. Stack pushes below the limit must not be - * performed. It is IMPDEF whether pushes above the limit are - * performed; we choose not to. - */ - qemu_log_mask(CPU_LOG_INT, - "...STKOF during callee-saves register stacking\n"); - env->v7m.cfsr[env->v7m.secure] |= R_V7M_CFSR_STKOF_MASK; - armv7m_nvic_set_pending(env->nvic, ARMV7M_EXCP_USAGE, - env->v7m.secure); - *frame_sp_p = limit; - return true; - } - - /* - * Write as much of the stack frame as we can. A write failure may - * cause us to pend a derived exception. - */ - sig = v7m_integrity_sig(env, lr); - stacked_ok = - v7m_stack_write(cpu, frameptr, sig, mmu_idx, smode) && - v7m_stack_write(cpu, frameptr + 0x8, env->regs[4], mmu_idx, smode) && - v7m_stack_write(cpu, frameptr + 0xc, env->regs[5], mmu_idx, smode) && - v7m_stack_write(cpu, frameptr + 0x10, env->regs[6], mmu_idx, smode) && - v7m_stack_write(cpu, frameptr + 0x14, env->regs[7], mmu_idx, smode) && - v7m_stack_write(cpu, frameptr + 0x18, env->regs[8], mmu_idx, smode) && - v7m_stack_write(cpu, frameptr + 0x1c, env->regs[9], mmu_idx, smode) && - v7m_stack_write(cpu, frameptr + 0x20, env->regs[10], mmu_idx, smode) && - v7m_stack_write(cpu, frameptr + 0x24, env->regs[11], mmu_idx, smode); - - /* Update SP regardless of whether any of the stack accesses failed. */ - *frame_sp_p = frameptr; - - return !stacked_ok; -} - -static void v7m_exception_taken(ARMCPU *cpu, uint32_t lr, bool dotailchain, - bool ignore_stackfaults) -{ - /* - * Do the "take the exception" parts of exception entry, - * but not the pushing of state to the stack. This is - * similar to the pseudocode ExceptionTaken() function. - */ - CPUARMState *env = &cpu->env; - uint32_t addr; - bool targets_secure; - int exc; - bool push_failed = false; - - armv7m_nvic_get_pending_irq_info(env->nvic, &exc, &targets_secure); - qemu_log_mask(CPU_LOG_INT, "...taking pending %s exception %d\n", - targets_secure ? "secure" : "nonsecure", exc); - - if (dotailchain) { - /* Sanitize LR FType and PREFIX bits */ - if (!arm_feature(env, ARM_FEATURE_VFP)) { - lr |= R_V7M_EXCRET_FTYPE_MASK; - } - lr = deposit32(lr, 24, 8, 0xff); - } - - if (arm_feature(env, ARM_FEATURE_V8)) { - if (arm_feature(env, ARM_FEATURE_M_SECURITY) && - (lr & R_V7M_EXCRET_S_MASK)) { - /* - * The background code (the owner of the registers in the - * exception frame) is Secure. This means it may either already - * have or now needs to push callee-saves registers. - */ - if (targets_secure) { - if (dotailchain && !(lr & R_V7M_EXCRET_ES_MASK)) { - /* - * We took an exception from Secure to NonSecure - * (which means the callee-saved registers got stacked) - * and are now tailchaining to a Secure exception. - * Clear DCRS so eventual return from this Secure - * exception unstacks the callee-saved registers. - */ - lr &= ~R_V7M_EXCRET_DCRS_MASK; - } - } else { - /* - * We're going to a non-secure exception; push the - * callee-saves registers to the stack now, if they're - * not already saved. - */ - if (lr & R_V7M_EXCRET_DCRS_MASK && - !(dotailchain && !(lr & R_V7M_EXCRET_ES_MASK))) { - push_failed = v7m_push_callee_stack(cpu, lr, dotailchain, - ignore_stackfaults); - } - lr |= R_V7M_EXCRET_DCRS_MASK; - } - } - - lr &= ~R_V7M_EXCRET_ES_MASK; - if (targets_secure || !arm_feature(env, ARM_FEATURE_M_SECURITY)) { - lr |= R_V7M_EXCRET_ES_MASK; - } - lr &= ~R_V7M_EXCRET_SPSEL_MASK; - if (env->v7m.control[targets_secure] & R_V7M_CONTROL_SPSEL_MASK) { - lr |= R_V7M_EXCRET_SPSEL_MASK; - } - - /* - * Clear registers if necessary to prevent non-secure exception - * code being able to see register values from secure code. - * Where register values become architecturally UNKNOWN we leave - * them with their previous values. - */ - if (arm_feature(env, ARM_FEATURE_M_SECURITY)) { - if (!targets_secure) { - /* - * Always clear the caller-saved registers (they have been - * pushed to the stack earlier in v7m_push_stack()). - * Clear callee-saved registers if the background code is - * Secure (in which case these regs were saved in - * v7m_push_callee_stack()). - */ - int i; - - for (i = 0; i < 13; i++) { - /* r4..r11 are callee-saves, zero only if EXCRET.S == 1 */ - if (i < 4 || i > 11 || (lr & R_V7M_EXCRET_S_MASK)) { - env->regs[i] = 0; - } - } - /* Clear EAPSR */ - xpsr_write(env, 0, XPSR_NZCV | XPSR_Q | XPSR_GE | XPSR_IT); - } - } - } - - if (push_failed && !ignore_stackfaults) { - /* - * Derived exception on callee-saves register stacking: - * we might now want to take a different exception which - * targets a different security state, so try again from the top. - */ - qemu_log_mask(CPU_LOG_INT, - "...derived exception on callee-saves register stacking"); - v7m_exception_taken(cpu, lr, true, true); - return; - } - - if (!arm_v7m_load_vector(cpu, exc, targets_secure, &addr)) { - /* Vector load failed: derived exception */ - qemu_log_mask(CPU_LOG_INT, "...derived exception on vector table load"); - v7m_exception_taken(cpu, lr, true, true); - return; - } - - /* - * Now we've done everything that might cause a derived exception - * we can go ahead and activate whichever exception we're going to - * take (which might now be the derived exception). - */ - armv7m_nvic_acknowledge_irq(env->nvic); - - /* Switch to target security state -- must do this before writing SPSEL */ - switch_v7m_security_state(env, targets_secure); - write_v7m_control_spsel(env, 0); - arm_clear_exclusive(env); - /* Clear SFPA and FPCA (has no effect if no FPU) */ - env->v7m.control[M_REG_S] &= - ~(R_V7M_CONTROL_FPCA_MASK | R_V7M_CONTROL_SFPA_MASK); - /* Clear IT bits */ - env->condexec_bits = 0; - env->regs[14] = lr; - env->regs[15] = addr & 0xfffffffe; - env->thumb = addr & 1; -} - -static void v7m_update_fpccr(CPUARMState *env, uint32_t frameptr, - bool apply_splim) -{ - /* - * Like the pseudocode UpdateFPCCR: save state in FPCAR and FPCCR - * that we will need later in order to do lazy FP reg stacking. - */ - bool is_secure = env->v7m.secure; - void *nvic = env->nvic; - /* - * Some bits are unbanked and live always in fpccr[M_REG_S]; some bits - * are banked and we want to update the bit in the bank for the - * current security state; and in one case we want to specifically - * update the NS banked version of a bit even if we are secure. - */ - uint32_t *fpccr_s = &env->v7m.fpccr[M_REG_S]; - uint32_t *fpccr_ns = &env->v7m.fpccr[M_REG_NS]; - uint32_t *fpccr = &env->v7m.fpccr[is_secure]; - bool hfrdy, bfrdy, mmrdy, ns_ufrdy, s_ufrdy, sfrdy, monrdy; - - env->v7m.fpcar[is_secure] = frameptr & ~0x7; - - if (apply_splim && arm_feature(env, ARM_FEATURE_V8)) { - bool splimviol; - uint32_t splim = v7m_sp_limit(env); - bool ign = armv7m_nvic_neg_prio_requested(nvic, is_secure) && - (env->v7m.ccr[is_secure] & R_V7M_CCR_STKOFHFNMIGN_MASK); - - splimviol = !ign && frameptr < splim; - *fpccr = FIELD_DP32(*fpccr, V7M_FPCCR, SPLIMVIOL, splimviol); - } - - *fpccr = FIELD_DP32(*fpccr, V7M_FPCCR, LSPACT, 1); - - *fpccr_s = FIELD_DP32(*fpccr_s, V7M_FPCCR, S, is_secure); - - *fpccr = FIELD_DP32(*fpccr, V7M_FPCCR, USER, arm_current_el(env) == 0); - - *fpccr = FIELD_DP32(*fpccr, V7M_FPCCR, THREAD, - !arm_v7m_is_handler_mode(env)); - - hfrdy = armv7m_nvic_get_ready_status(nvic, ARMV7M_EXCP_HARD, false); - *fpccr_s = FIELD_DP32(*fpccr_s, V7M_FPCCR, HFRDY, hfrdy); - - bfrdy = armv7m_nvic_get_ready_status(nvic, ARMV7M_EXCP_BUS, false); - *fpccr_s = FIELD_DP32(*fpccr_s, V7M_FPCCR, BFRDY, bfrdy); - - mmrdy = armv7m_nvic_get_ready_status(nvic, ARMV7M_EXCP_MEM, is_secure); - *fpccr = FIELD_DP32(*fpccr, V7M_FPCCR, MMRDY, mmrdy); - - ns_ufrdy = armv7m_nvic_get_ready_status(nvic, ARMV7M_EXCP_USAGE, false); - *fpccr_ns = FIELD_DP32(*fpccr_ns, V7M_FPCCR, UFRDY, ns_ufrdy); - - monrdy = armv7m_nvic_get_ready_status(nvic, ARMV7M_EXCP_DEBUG, false); - *fpccr_s = FIELD_DP32(*fpccr_s, V7M_FPCCR, MONRDY, monrdy); - - if (arm_feature(env, ARM_FEATURE_M_SECURITY)) { - s_ufrdy = armv7m_nvic_get_ready_status(nvic, ARMV7M_EXCP_USAGE, true); - *fpccr_s = FIELD_DP32(*fpccr_s, V7M_FPCCR, UFRDY, s_ufrdy); - - sfrdy = armv7m_nvic_get_ready_status(nvic, ARMV7M_EXCP_SECURE, false); - *fpccr_s = FIELD_DP32(*fpccr_s, V7M_FPCCR, SFRDY, sfrdy); - } -} - -void HELPER(v7m_vlstm)(CPUARMState *env, uint32_t fptr) -{ - /* fptr is the value of Rn, the frame pointer we store the FP regs to */ - bool s = env->v7m.fpccr[M_REG_S] & R_V7M_FPCCR_S_MASK; - bool lspact = env->v7m.fpccr[s] & R_V7M_FPCCR_LSPACT_MASK; - - assert(env->v7m.secure); - - if (!(env->v7m.control[M_REG_S] & R_V7M_CONTROL_SFPA_MASK)) { - return; - } - - /* Check access to the coprocessor is permitted */ - if (!v7m_cpacr_pass(env, true, arm_current_el(env) != 0)) { - raise_exception_ra(env, EXCP_NOCP, 0, 1, GETPC()); - } - - if (lspact) { - /* LSPACT should not be active when there is active FP state */ - raise_exception_ra(env, EXCP_LSERR, 0, 1, GETPC()); - } - - if (fptr & 7) { - raise_exception_ra(env, EXCP_UNALIGNED, 0, 1, GETPC()); - } - - /* - * Note that we do not use v7m_stack_write() here, because the - * accesses should not set the FSR bits for stacking errors if they - * fail. (In pseudocode terms, they are AccType_NORMAL, not AccType_STACK - * or AccType_LAZYFP). Faults in cpu_stl_data() will throw exceptions - * and longjmp out. - */ - if (!(env->v7m.fpccr[M_REG_S] & R_V7M_FPCCR_LSPEN_MASK)) { - bool ts = env->v7m.fpccr[M_REG_S] & R_V7M_FPCCR_TS_MASK; - int i; - - for (i = 0; i < (ts ? 32 : 16); i += 2) { - uint64_t dn = *aa32_vfp_dreg(env, i / 2); - uint32_t faddr = fptr + 4 * i; - uint32_t slo = extract64(dn, 0, 32); - uint32_t shi = extract64(dn, 32, 32); - - if (i >= 16) { - faddr += 8; /* skip the slot for the FPSCR */ - } - cpu_stl_data(env, faddr, slo); - cpu_stl_data(env, faddr + 4, shi); - } - cpu_stl_data(env, fptr + 0x40, vfp_get_fpscr(env)); - - /* - * If TS is 0 then s0 to s15 and FPSCR are UNKNOWN; we choose to - * leave them unchanged, matching our choice in v7m_preserve_fp_state. - */ - if (ts) { - for (i = 0; i < 32; i += 2) { - *aa32_vfp_dreg(env, i / 2) = 0; - } - vfp_set_fpscr(env, 0); - } - } else { - v7m_update_fpccr(env, fptr, false); - } - - env->v7m.control[M_REG_S] &= ~R_V7M_CONTROL_FPCA_MASK; -} - -void HELPER(v7m_vlldm)(CPUARMState *env, uint32_t fptr) -{ - /* fptr is the value of Rn, the frame pointer we load the FP regs from */ - assert(env->v7m.secure); - - if (!(env->v7m.control[M_REG_S] & R_V7M_CONTROL_SFPA_MASK)) { - return; - } - - /* Check access to the coprocessor is permitted */ - if (!v7m_cpacr_pass(env, true, arm_current_el(env) != 0)) { - raise_exception_ra(env, EXCP_NOCP, 0, 1, GETPC()); - } - - if (env->v7m.fpccr[M_REG_S] & R_V7M_FPCCR_LSPACT_MASK) { - /* State in FP is still valid */ - env->v7m.fpccr[M_REG_S] &= ~R_V7M_FPCCR_LSPACT_MASK; - } else { - bool ts = env->v7m.fpccr[M_REG_S] & R_V7M_FPCCR_TS_MASK; - int i; - uint32_t fpscr; - - if (fptr & 7) { - raise_exception_ra(env, EXCP_UNALIGNED, 0, 1, GETPC()); - } - - for (i = 0; i < (ts ? 32 : 16); i += 2) { - uint32_t slo, shi; - uint64_t dn; - uint32_t faddr = fptr + 4 * i; - - if (i >= 16) { - faddr += 8; /* skip the slot for the FPSCR */ - } - - slo = cpu_ldl_data(env, faddr); - shi = cpu_ldl_data(env, faddr + 4); - - dn = (uint64_t) shi << 32 | slo; - *aa32_vfp_dreg(env, i / 2) = dn; - } - fpscr = cpu_ldl_data(env, fptr + 0x40); - vfp_set_fpscr(env, fpscr); - } - - env->v7m.control[M_REG_S] |= R_V7M_CONTROL_FPCA_MASK; -} - -static bool v7m_push_stack(ARMCPU *cpu) -{ - /* - * Do the "set up stack frame" part of exception entry, - * similar to pseudocode PushStack(). - * Return true if we generate a derived exception (and so - * should ignore further stack faults trying to process - * that derived exception.) - */ - bool stacked_ok = true, limitviol = false; - CPUARMState *env = &cpu->env; - uint32_t xpsr = xpsr_read(env); - uint32_t frameptr = env->regs[13]; - ARMMMUIdx mmu_idx = arm_mmu_idx(env); - uint32_t framesize; - bool nsacr_cp10 = extract32(env->v7m.nsacr, 10, 1); - - if ((env->v7m.control[M_REG_S] & R_V7M_CONTROL_FPCA_MASK) && - (env->v7m.secure || nsacr_cp10)) { - if (env->v7m.secure && - env->v7m.fpccr[M_REG_S] & R_V7M_FPCCR_TS_MASK) { - framesize = 0xa8; - } else { - framesize = 0x68; - } - } else { - framesize = 0x20; - } - - /* Align stack pointer if the guest wants that */ - if ((frameptr & 4) && - (env->v7m.ccr[env->v7m.secure] & R_V7M_CCR_STKALIGN_MASK)) { - frameptr -= 4; - xpsr |= XPSR_SPREALIGN; - } - - xpsr &= ~XPSR_SFPA; - if (env->v7m.secure && - (env->v7m.control[M_REG_S] & R_V7M_CONTROL_SFPA_MASK)) { - xpsr |= XPSR_SFPA; - } - - frameptr -= framesize; - - if (arm_feature(env, ARM_FEATURE_V8)) { - uint32_t limit = v7m_sp_limit(env); - - if (frameptr < limit) { - /* - * Stack limit failure: set SP to the limit value, and generate - * STKOF UsageFault. Stack pushes below the limit must not be - * performed. It is IMPDEF whether pushes above the limit are - * performed; we choose not to. - */ - qemu_log_mask(CPU_LOG_INT, - "...STKOF during stacking\n"); - env->v7m.cfsr[env->v7m.secure] |= R_V7M_CFSR_STKOF_MASK; - armv7m_nvic_set_pending(env->nvic, ARMV7M_EXCP_USAGE, - env->v7m.secure); - env->regs[13] = limit; - /* - * We won't try to perform any further memory accesses but - * we must continue through the following code to check for - * permission faults during FPU state preservation, and we - * must update FPCCR if lazy stacking is enabled. - */ - limitviol = true; - stacked_ok = false; - } - } - - /* - * Write as much of the stack frame as we can. If we fail a stack - * write this will result in a derived exception being pended - * (which may be taken in preference to the one we started with - * if it has higher priority). - */ - stacked_ok = stacked_ok && - v7m_stack_write(cpu, frameptr, env->regs[0], mmu_idx, STACK_NORMAL) && - v7m_stack_write(cpu, frameptr + 4, env->regs[1], - mmu_idx, STACK_NORMAL) && - v7m_stack_write(cpu, frameptr + 8, env->regs[2], - mmu_idx, STACK_NORMAL) && - v7m_stack_write(cpu, frameptr + 12, env->regs[3], - mmu_idx, STACK_NORMAL) && - v7m_stack_write(cpu, frameptr + 16, env->regs[12], - mmu_idx, STACK_NORMAL) && - v7m_stack_write(cpu, frameptr + 20, env->regs[14], - mmu_idx, STACK_NORMAL) && - v7m_stack_write(cpu, frameptr + 24, env->regs[15], - mmu_idx, STACK_NORMAL) && - v7m_stack_write(cpu, frameptr + 28, xpsr, mmu_idx, STACK_NORMAL); - - if (env->v7m.control[M_REG_S] & R_V7M_CONTROL_FPCA_MASK) { - /* FPU is active, try to save its registers */ - bool fpccr_s = env->v7m.fpccr[M_REG_S] & R_V7M_FPCCR_S_MASK; - bool lspact = env->v7m.fpccr[fpccr_s] & R_V7M_FPCCR_LSPACT_MASK; - - if (lspact && arm_feature(env, ARM_FEATURE_M_SECURITY)) { - qemu_log_mask(CPU_LOG_INT, - "...SecureFault because LSPACT and FPCA both set\n"); - env->v7m.sfsr |= R_V7M_SFSR_LSERR_MASK; - armv7m_nvic_set_pending(env->nvic, ARMV7M_EXCP_SECURE, false); - } else if (!env->v7m.secure && !nsacr_cp10) { - qemu_log_mask(CPU_LOG_INT, - "...Secure UsageFault with CFSR.NOCP because " - "NSACR.CP10 prevents stacking FP regs\n"); - armv7m_nvic_set_pending(env->nvic, ARMV7M_EXCP_USAGE, M_REG_S); - env->v7m.cfsr[M_REG_S] |= R_V7M_CFSR_NOCP_MASK; - } else { - if (!(env->v7m.fpccr[M_REG_S] & R_V7M_FPCCR_LSPEN_MASK)) { - /* Lazy stacking disabled, save registers now */ - int i; - bool cpacr_pass = v7m_cpacr_pass(env, env->v7m.secure, - arm_current_el(env) != 0); - - if (stacked_ok && !cpacr_pass) { - /* - * Take UsageFault if CPACR forbids access. The pseudocode - * here does a full CheckCPEnabled() but we know the NSACR - * check can never fail as we have already handled that. - */ - qemu_log_mask(CPU_LOG_INT, - "...UsageFault with CFSR.NOCP because " - "CPACR.CP10 prevents stacking FP regs\n"); - armv7m_nvic_set_pending(env->nvic, ARMV7M_EXCP_USAGE, - env->v7m.secure); - env->v7m.cfsr[env->v7m.secure] |= R_V7M_CFSR_NOCP_MASK; - stacked_ok = false; - } - - for (i = 0; i < ((framesize == 0xa8) ? 32 : 16); i += 2) { - uint64_t dn = *aa32_vfp_dreg(env, i / 2); - uint32_t faddr = frameptr + 0x20 + 4 * i; - uint32_t slo = extract64(dn, 0, 32); - uint32_t shi = extract64(dn, 32, 32); - - if (i >= 16) { - faddr += 8; /* skip the slot for the FPSCR */ - } - stacked_ok = stacked_ok && - v7m_stack_write(cpu, faddr, slo, - mmu_idx, STACK_NORMAL) && - v7m_stack_write(cpu, faddr + 4, shi, - mmu_idx, STACK_NORMAL); - } - stacked_ok = stacked_ok && - v7m_stack_write(cpu, frameptr + 0x60, - vfp_get_fpscr(env), mmu_idx, STACK_NORMAL); - if (cpacr_pass) { - for (i = 0; i < ((framesize == 0xa8) ? 32 : 16); i += 2) { - *aa32_vfp_dreg(env, i / 2) = 0; - } - vfp_set_fpscr(env, 0); - } - } else { - /* Lazy stacking enabled, save necessary info to stack later */ - v7m_update_fpccr(env, frameptr + 0x20, true); - } - } - } - - /* - * If we broke a stack limit then SP was already updated earlier; - * otherwise we update SP regardless of whether any of the stack - * accesses failed or we took some other kind of fault. - */ - if (!limitviol) { - env->regs[13] = frameptr; - } - - return !stacked_ok; -} - -static void do_v7m_exception_exit(ARMCPU *cpu) -{ - CPUARMState *env = &cpu->env; - uint32_t excret; - uint32_t xpsr, xpsr_mask; - bool ufault = false; - bool sfault = false; - bool return_to_sp_process; - bool return_to_handler; - bool rettobase = false; - bool exc_secure = false; - bool return_to_secure; - bool ftype; - bool restore_s16_s31; - - /* - * If we're not in Handler mode then jumps to magic exception-exit - * addresses don't have magic behaviour. However for the v8M - * security extensions the magic secure-function-return has to - * work in thread mode too, so to avoid doing an extra check in - * the generated code we allow exception-exit magic to also cause the - * internal exception and bring us here in thread mode. Correct code - * will never try to do this (the following insn fetch will always - * fault) so we the overhead of having taken an unnecessary exception - * doesn't matter. - */ - if (!arm_v7m_is_handler_mode(env)) { - return; - } - - /* - * In the spec pseudocode ExceptionReturn() is called directly - * from BXWritePC() and gets the full target PC value including - * bit zero. In QEMU's implementation we treat it as a normal - * jump-to-register (which is then caught later on), and so split - * the target value up between env->regs[15] and env->thumb in - * gen_bx(). Reconstitute it. - */ - excret = env->regs[15]; - if (env->thumb) { - excret |= 1; - } - - qemu_log_mask(CPU_LOG_INT, "Exception return: magic PC %" PRIx32 - " previous exception %d\n", - excret, env->v7m.exception); - - if ((excret & R_V7M_EXCRET_RES1_MASK) != R_V7M_EXCRET_RES1_MASK) { - qemu_log_mask(LOG_GUEST_ERROR, "M profile: zero high bits in exception " - "exit PC value 0x%" PRIx32 " are UNPREDICTABLE\n", - excret); - } - - ftype = excret & R_V7M_EXCRET_FTYPE_MASK; - - if (!arm_feature(env, ARM_FEATURE_VFP) && !ftype) { - qemu_log_mask(LOG_GUEST_ERROR, "M profile: zero FTYPE in exception " - "exit PC value 0x%" PRIx32 " is UNPREDICTABLE " - "if FPU not present\n", - excret); - ftype = true; - } - - if (arm_feature(env, ARM_FEATURE_M_SECURITY)) { - /* - * EXC_RETURN.ES validation check (R_SMFL). We must do this before - * we pick which FAULTMASK to clear. - */ - if (!env->v7m.secure && - ((excret & R_V7M_EXCRET_ES_MASK) || - !(excret & R_V7M_EXCRET_DCRS_MASK))) { - sfault = 1; - /* For all other purposes, treat ES as 0 (R_HXSR) */ - excret &= ~R_V7M_EXCRET_ES_MASK; - } - exc_secure = excret & R_V7M_EXCRET_ES_MASK; - } - - if (env->v7m.exception != ARMV7M_EXCP_NMI) { - /* - * Auto-clear FAULTMASK on return from other than NMI. - * If the security extension is implemented then this only - * happens if the raw execution priority is >= 0; the - * value of the ES bit in the exception return value indicates - * which security state's faultmask to clear. (v8M ARM ARM R_KBNF.) - */ - if (arm_feature(env, ARM_FEATURE_M_SECURITY)) { - if (armv7m_nvic_raw_execution_priority(env->nvic) >= 0) { - env->v7m.faultmask[exc_secure] = 0; - } - } else { - env->v7m.faultmask[M_REG_NS] = 0; - } - } - - switch (armv7m_nvic_complete_irq(env->nvic, env->v7m.exception, - exc_secure)) { - case -1: - /* attempt to exit an exception that isn't active */ - ufault = true; - break; - case 0: - /* still an irq active now */ - break; - case 1: - /* - * We returned to base exception level, no nesting. - * (In the pseudocode this is written using "NestedActivation != 1" - * where we have 'rettobase == false'.) - */ - rettobase = true; - break; - default: - g_assert_not_reached(); - } - - return_to_handler = !(excret & R_V7M_EXCRET_MODE_MASK); - return_to_sp_process = excret & R_V7M_EXCRET_SPSEL_MASK; - return_to_secure = arm_feature(env, ARM_FEATURE_M_SECURITY) && - (excret & R_V7M_EXCRET_S_MASK); - - if (arm_feature(env, ARM_FEATURE_V8)) { - if (!arm_feature(env, ARM_FEATURE_M_SECURITY)) { - /* - * UNPREDICTABLE if S == 1 or DCRS == 0 or ES == 1 (R_XLCP); - * we choose to take the UsageFault. - */ - if ((excret & R_V7M_EXCRET_S_MASK) || - (excret & R_V7M_EXCRET_ES_MASK) || - !(excret & R_V7M_EXCRET_DCRS_MASK)) { - ufault = true; - } - } - if (excret & R_V7M_EXCRET_RES0_MASK) { - ufault = true; - } - } else { - /* For v7M we only recognize certain combinations of the low bits */ - switch (excret & 0xf) { - case 1: /* Return to Handler */ - break; - case 13: /* Return to Thread using Process stack */ - case 9: /* Return to Thread using Main stack */ - /* - * We only need to check NONBASETHRDENA for v7M, because in - * v8M this bit does not exist (it is RES1). - */ - if (!rettobase && - !(env->v7m.ccr[env->v7m.secure] & - R_V7M_CCR_NONBASETHRDENA_MASK)) { - ufault = true; - } - break; - default: - ufault = true; - } - } - - /* - * Set CONTROL.SPSEL from excret.SPSEL. Since we're still in - * Handler mode (and will be until we write the new XPSR.Interrupt - * field) this does not switch around the current stack pointer. - * We must do this before we do any kind of tailchaining, including - * for the derived exceptions on integrity check failures, or we will - * give the guest an incorrect EXCRET.SPSEL value on exception entry. - */ - write_v7m_control_spsel_for_secstate(env, return_to_sp_process, exc_secure); - - /* - * Clear scratch FP values left in caller saved registers; this - * must happen before any kind of tail chaining. - */ - if ((env->v7m.fpccr[M_REG_S] & R_V7M_FPCCR_CLRONRET_MASK) && - (env->v7m.control[M_REG_S] & R_V7M_CONTROL_FPCA_MASK)) { - if (env->v7m.fpccr[M_REG_S] & R_V7M_FPCCR_LSPACT_MASK) { - env->v7m.sfsr |= R_V7M_SFSR_LSERR_MASK; - armv7m_nvic_set_pending(env->nvic, ARMV7M_EXCP_SECURE, false); - qemu_log_mask(CPU_LOG_INT, "...taking SecureFault on existing " - "stackframe: error during lazy state deactivation\n"); - v7m_exception_taken(cpu, excret, true, false); - return; - } else { - /* Clear s0..s15 and FPSCR */ - int i; - - for (i = 0; i < 16; i += 2) { - *aa32_vfp_dreg(env, i / 2) = 0; - } - vfp_set_fpscr(env, 0); - } - } - - if (sfault) { - env->v7m.sfsr |= R_V7M_SFSR_INVER_MASK; - armv7m_nvic_set_pending(env->nvic, ARMV7M_EXCP_SECURE, false); - qemu_log_mask(CPU_LOG_INT, "...taking SecureFault on existing " - "stackframe: failed EXC_RETURN.ES validity check\n"); - v7m_exception_taken(cpu, excret, true, false); - return; - } - - if (ufault) { - /* - * Bad exception return: instead of popping the exception - * stack, directly take a usage fault on the current stack. - */ - env->v7m.cfsr[env->v7m.secure] |= R_V7M_CFSR_INVPC_MASK; - armv7m_nvic_set_pending(env->nvic, ARMV7M_EXCP_USAGE, env->v7m.secure); - qemu_log_mask(CPU_LOG_INT, "...taking UsageFault on existing " - "stackframe: failed exception return integrity check\n"); - v7m_exception_taken(cpu, excret, true, false); - return; - } - - /* - * Tailchaining: if there is currently a pending exception that - * is high enough priority to preempt execution at the level we're - * about to return to, then just directly take that exception now, - * avoiding an unstack-and-then-stack. Note that now we have - * deactivated the previous exception by calling armv7m_nvic_complete_irq() - * our current execution priority is already the execution priority we are - * returning to -- none of the state we would unstack or set based on - * the EXCRET value affects it. - */ - if (armv7m_nvic_can_take_pending_exception(env->nvic)) { - qemu_log_mask(CPU_LOG_INT, "...tailchaining to pending exception\n"); - v7m_exception_taken(cpu, excret, true, false); - return; - } - - switch_v7m_security_state(env, return_to_secure); - - { - /* - * The stack pointer we should be reading the exception frame from - * depends on bits in the magic exception return type value (and - * for v8M isn't necessarily the stack pointer we will eventually - * end up resuming execution with). Get a pointer to the location - * in the CPU state struct where the SP we need is currently being - * stored; we will use and modify it in place. - * We use this limited C variable scope so we don't accidentally - * use 'frame_sp_p' after we do something that makes it invalid. - */ - uint32_t *frame_sp_p = get_v7m_sp_ptr(env, - return_to_secure, - !return_to_handler, - return_to_sp_process); - uint32_t frameptr = *frame_sp_p; - bool pop_ok = true; - ARMMMUIdx mmu_idx; - bool return_to_priv = return_to_handler || - !(env->v7m.control[return_to_secure] & R_V7M_CONTROL_NPRIV_MASK); - - mmu_idx = arm_v7m_mmu_idx_for_secstate_and_priv(env, return_to_secure, - return_to_priv); - - if (!QEMU_IS_ALIGNED(frameptr, 8) && - arm_feature(env, ARM_FEATURE_V8)) { - qemu_log_mask(LOG_GUEST_ERROR, - "M profile exception return with non-8-aligned SP " - "for destination state is UNPREDICTABLE\n"); - } - - /* Do we need to pop callee-saved registers? */ - if (return_to_secure && - ((excret & R_V7M_EXCRET_ES_MASK) == 0 || - (excret & R_V7M_EXCRET_DCRS_MASK) == 0)) { - uint32_t actual_sig; - - pop_ok = v7m_stack_read(cpu, &actual_sig, frameptr, mmu_idx); - - if (pop_ok && v7m_integrity_sig(env, excret) != actual_sig) { - /* Take a SecureFault on the current stack */ - env->v7m.sfsr |= R_V7M_SFSR_INVIS_MASK; - armv7m_nvic_set_pending(env->nvic, ARMV7M_EXCP_SECURE, false); - qemu_log_mask(CPU_LOG_INT, "...taking SecureFault on existing " - "stackframe: failed exception return integrity " - "signature check\n"); - v7m_exception_taken(cpu, excret, true, false); - return; - } - - pop_ok = pop_ok && - v7m_stack_read(cpu, &env->regs[4], frameptr + 0x8, mmu_idx) && - v7m_stack_read(cpu, &env->regs[5], frameptr + 0xc, mmu_idx) && - v7m_stack_read(cpu, &env->regs[6], frameptr + 0x10, mmu_idx) && - v7m_stack_read(cpu, &env->regs[7], frameptr + 0x14, mmu_idx) && - v7m_stack_read(cpu, &env->regs[8], frameptr + 0x18, mmu_idx) && - v7m_stack_read(cpu, &env->regs[9], frameptr + 0x1c, mmu_idx) && - v7m_stack_read(cpu, &env->regs[10], frameptr + 0x20, mmu_idx) && - v7m_stack_read(cpu, &env->regs[11], frameptr + 0x24, mmu_idx); - - frameptr += 0x28; - } - - /* Pop registers */ - pop_ok = pop_ok && - v7m_stack_read(cpu, &env->regs[0], frameptr, mmu_idx) && - v7m_stack_read(cpu, &env->regs[1], frameptr + 0x4, mmu_idx) && - v7m_stack_read(cpu, &env->regs[2], frameptr + 0x8, mmu_idx) && - v7m_stack_read(cpu, &env->regs[3], frameptr + 0xc, mmu_idx) && - v7m_stack_read(cpu, &env->regs[12], frameptr + 0x10, mmu_idx) && - v7m_stack_read(cpu, &env->regs[14], frameptr + 0x14, mmu_idx) && - v7m_stack_read(cpu, &env->regs[15], frameptr + 0x18, mmu_idx) && - v7m_stack_read(cpu, &xpsr, frameptr + 0x1c, mmu_idx); - - if (!pop_ok) { - /* - * v7m_stack_read() pended a fault, so take it (as a tail - * chained exception on the same stack frame) - */ - qemu_log_mask(CPU_LOG_INT, "...derived exception on unstacking\n"); - v7m_exception_taken(cpu, excret, true, false); - return; - } - - /* - * Returning from an exception with a PC with bit 0 set is defined - * behaviour on v8M (bit 0 is ignored), but for v7M it was specified - * to be UNPREDICTABLE. In practice actual v7M hardware seems to ignore - * the lsbit, and there are several RTOSes out there which incorrectly - * assume the r15 in the stack frame should be a Thumb-style "lsbit - * indicates ARM/Thumb" value, so ignore the bit on v7M as well, but - * complain about the badly behaved guest. - */ - if (env->regs[15] & 1) { - env->regs[15] &= ~1U; - if (!arm_feature(env, ARM_FEATURE_V8)) { - qemu_log_mask(LOG_GUEST_ERROR, - "M profile return from interrupt with misaligned " - "PC is UNPREDICTABLE on v7M\n"); - } - } - - if (arm_feature(env, ARM_FEATURE_V8)) { - /* - * For v8M we have to check whether the xPSR exception field - * matches the EXCRET value for return to handler/thread - * before we commit to changing the SP and xPSR. - */ - bool will_be_handler = (xpsr & XPSR_EXCP) != 0; - if (return_to_handler != will_be_handler) { - /* - * Take an INVPC UsageFault on the current stack. - * By this point we will have switched to the security state - * for the background state, so this UsageFault will target - * that state. - */ - armv7m_nvic_set_pending(env->nvic, ARMV7M_EXCP_USAGE, - env->v7m.secure); - env->v7m.cfsr[env->v7m.secure] |= R_V7M_CFSR_INVPC_MASK; - qemu_log_mask(CPU_LOG_INT, "...taking UsageFault on existing " - "stackframe: failed exception return integrity " - "check\n"); - v7m_exception_taken(cpu, excret, true, false); - return; - } - } - - if (!ftype) { - /* FP present and we need to handle it */ - if (!return_to_secure && - (env->v7m.fpccr[M_REG_S] & R_V7M_FPCCR_LSPACT_MASK)) { - armv7m_nvic_set_pending(env->nvic, ARMV7M_EXCP_SECURE, false); - env->v7m.sfsr |= R_V7M_SFSR_LSERR_MASK; - qemu_log_mask(CPU_LOG_INT, - "...taking SecureFault on existing stackframe: " - "Secure LSPACT set but exception return is " - "not to secure state\n"); - v7m_exception_taken(cpu, excret, true, false); - return; - } - - restore_s16_s31 = return_to_secure && - (env->v7m.fpccr[M_REG_S] & R_V7M_FPCCR_TS_MASK); - - if (env->v7m.fpccr[return_to_secure] & R_V7M_FPCCR_LSPACT_MASK) { - /* State in FPU is still valid, just clear LSPACT */ - env->v7m.fpccr[return_to_secure] &= ~R_V7M_FPCCR_LSPACT_MASK; - } else { - int i; - uint32_t fpscr; - bool cpacr_pass, nsacr_pass; - - cpacr_pass = v7m_cpacr_pass(env, return_to_secure, - return_to_priv); - nsacr_pass = return_to_secure || - extract32(env->v7m.nsacr, 10, 1); - - if (!cpacr_pass) { - armv7m_nvic_set_pending(env->nvic, ARMV7M_EXCP_USAGE, - return_to_secure); - env->v7m.cfsr[return_to_secure] |= R_V7M_CFSR_NOCP_MASK; - qemu_log_mask(CPU_LOG_INT, - "...taking UsageFault on existing " - "stackframe: CPACR.CP10 prevents unstacking " - "FP regs\n"); - v7m_exception_taken(cpu, excret, true, false); - return; - } else if (!nsacr_pass) { - armv7m_nvic_set_pending(env->nvic, ARMV7M_EXCP_USAGE, true); - env->v7m.cfsr[M_REG_S] |= R_V7M_CFSR_INVPC_MASK; - qemu_log_mask(CPU_LOG_INT, - "...taking Secure UsageFault on existing " - "stackframe: NSACR.CP10 prevents unstacking " - "FP regs\n"); - v7m_exception_taken(cpu, excret, true, false); - return; - } - - for (i = 0; i < (restore_s16_s31 ? 32 : 16); i += 2) { - uint32_t slo, shi; - uint64_t dn; - uint32_t faddr = frameptr + 0x20 + 4 * i; - - if (i >= 16) { - faddr += 8; /* Skip the slot for the FPSCR */ - } - - pop_ok = pop_ok && - v7m_stack_read(cpu, &slo, faddr, mmu_idx) && - v7m_stack_read(cpu, &shi, faddr + 4, mmu_idx); - - if (!pop_ok) { - break; - } - - dn = (uint64_t)shi << 32 | slo; - *aa32_vfp_dreg(env, i / 2) = dn; - } - pop_ok = pop_ok && - v7m_stack_read(cpu, &fpscr, frameptr + 0x60, mmu_idx); - if (pop_ok) { - vfp_set_fpscr(env, fpscr); - } - if (!pop_ok) { - /* - * These regs are 0 if security extension present; - * otherwise merely UNKNOWN. We zero always. - */ - for (i = 0; i < (restore_s16_s31 ? 32 : 16); i += 2) { - *aa32_vfp_dreg(env, i / 2) = 0; - } - vfp_set_fpscr(env, 0); - } - } - } - env->v7m.control[M_REG_S] = FIELD_DP32(env->v7m.control[M_REG_S], - V7M_CONTROL, FPCA, !ftype); - - /* Commit to consuming the stack frame */ - frameptr += 0x20; - if (!ftype) { - frameptr += 0x48; - if (restore_s16_s31) { - frameptr += 0x40; - } - } - /* - * Undo stack alignment (the SPREALIGN bit indicates that the original - * pre-exception SP was not 8-aligned and we added a padding word to - * align it, so we undo this by ORing in the bit that increases it - * from the current 8-aligned value to the 8-unaligned value. (Adding 4 - * would work too but a logical OR is how the pseudocode specifies it.) - */ - if (xpsr & XPSR_SPREALIGN) { - frameptr |= 4; - } - *frame_sp_p = frameptr; - } - - xpsr_mask = ~(XPSR_SPREALIGN | XPSR_SFPA); - if (!arm_feature(env, ARM_FEATURE_THUMB_DSP)) { - xpsr_mask &= ~XPSR_GE; - } - /* This xpsr_write() will invalidate frame_sp_p as it may switch stack */ - xpsr_write(env, xpsr, xpsr_mask); - - if (env->v7m.secure) { - bool sfpa = xpsr & XPSR_SFPA; - - env->v7m.control[M_REG_S] = FIELD_DP32(env->v7m.control[M_REG_S], - V7M_CONTROL, SFPA, sfpa); - } - - /* - * The restored xPSR exception field will be zero if we're - * resuming in Thread mode. If that doesn't match what the - * exception return excret specified then this is a UsageFault. - * v7M requires we make this check here; v8M did it earlier. - */ - if (return_to_handler != arm_v7m_is_handler_mode(env)) { - /* - * Take an INVPC UsageFault by pushing the stack again; - * we know we're v7M so this is never a Secure UsageFault. - */ - bool ignore_stackfaults; - - assert(!arm_feature(env, ARM_FEATURE_V8)); - armv7m_nvic_set_pending(env->nvic, ARMV7M_EXCP_USAGE, false); - env->v7m.cfsr[env->v7m.secure] |= R_V7M_CFSR_INVPC_MASK; - ignore_stackfaults = v7m_push_stack(cpu); - qemu_log_mask(CPU_LOG_INT, "...taking UsageFault on new stackframe: " - "failed exception return integrity check\n"); - v7m_exception_taken(cpu, excret, false, ignore_stackfaults); - return; - } - - /* Otherwise, we have a successful exception exit. */ - arm_clear_exclusive(env); - qemu_log_mask(CPU_LOG_INT, "...successful exception return\n"); -} - -static bool do_v7m_function_return(ARMCPU *cpu) -{ - /* - * v8M security extensions magic function return. - * We may either: - * (1) throw an exception (longjump) - * (2) return true if we successfully handled the function return - * (3) return false if we failed a consistency check and have - * pended a UsageFault that needs to be taken now - * - * At this point the magic return value is split between env->regs[15] - * and env->thumb. We don't bother to reconstitute it because we don't - * need it (all values are handled the same way). - */ - CPUARMState *env = &cpu->env; - uint32_t newpc, newpsr, newpsr_exc; - - qemu_log_mask(CPU_LOG_INT, "...really v7M secure function return\n"); - - { - bool threadmode, spsel; - TCGMemOpIdx oi; - ARMMMUIdx mmu_idx; - uint32_t *frame_sp_p; - uint32_t frameptr; - - /* Pull the return address and IPSR from the Secure stack */ - threadmode = !arm_v7m_is_handler_mode(env); - spsel = env->v7m.control[M_REG_S] & R_V7M_CONTROL_SPSEL_MASK; - - frame_sp_p = get_v7m_sp_ptr(env, true, threadmode, spsel); - frameptr = *frame_sp_p; - - /* - * These loads may throw an exception (for MPU faults). We want to - * do them as secure, so work out what MMU index that is. - */ - mmu_idx = arm_v7m_mmu_idx_for_secstate(env, true); - oi = make_memop_idx(MO_LE, arm_to_core_mmu_idx(mmu_idx)); - newpc = helper_le_ldul_mmu(env, frameptr, oi, 0); - newpsr = helper_le_ldul_mmu(env, frameptr + 4, oi, 0); - - /* Consistency checks on new IPSR */ - newpsr_exc = newpsr & XPSR_EXCP; - if (!((env->v7m.exception == 0 && newpsr_exc == 0) || - (env->v7m.exception == 1 && newpsr_exc != 0))) { - /* Pend the fault and tell our caller to take it */ - env->v7m.cfsr[env->v7m.secure] |= R_V7M_CFSR_INVPC_MASK; - armv7m_nvic_set_pending(env->nvic, ARMV7M_EXCP_USAGE, - env->v7m.secure); - qemu_log_mask(CPU_LOG_INT, - "...taking INVPC UsageFault: " - "IPSR consistency check failed\n"); - return false; - } - - *frame_sp_p = frameptr + 8; - } - - /* This invalidates frame_sp_p */ - switch_v7m_security_state(env, true); - env->v7m.exception = newpsr_exc; - env->v7m.control[M_REG_S] &= ~R_V7M_CONTROL_SFPA_MASK; - if (newpsr & XPSR_SFPA) { - env->v7m.control[M_REG_S] |= R_V7M_CONTROL_SFPA_MASK; - } - xpsr_write(env, 0, XPSR_IT); - env->thumb = newpc & 1; - env->regs[15] = newpc & ~1; - - qemu_log_mask(CPU_LOG_INT, "...function return successful\n"); - return true; -} - -static bool v7m_read_half_insn(ARMCPU *cpu, ARMMMUIdx mmu_idx, - uint32_t addr, uint16_t *insn) -{ - /* - * Load a 16-bit portion of a v7M instruction, returning true on success, - * or false on failure (in which case we will have pended the appropriate - * exception). - * We need to do the instruction fetch's MPU and SAU checks - * like this because there is no MMU index that would allow - * doing the load with a single function call. Instead we must - * first check that the security attributes permit the load - * and that they don't mismatch on the two halves of the instruction, - * and then we do the load as a secure load (ie using the security - * attributes of the address, not the CPU, as architecturally required). - */ - CPUState *cs = CPU(cpu); - CPUARMState *env = &cpu->env; - V8M_SAttributes sattrs = {}; - MemTxAttrs attrs = {}; - ARMMMUFaultInfo fi = {}; - MemTxResult txres; - target_ulong page_size; - hwaddr physaddr; - int prot; - - v8m_security_lookup(env, addr, MMU_INST_FETCH, mmu_idx, &sattrs); - if (!sattrs.nsc || sattrs.ns) { - /* - * This must be the second half of the insn, and it straddles a - * region boundary with the second half not being S&NSC. - */ - env->v7m.sfsr |= R_V7M_SFSR_INVEP_MASK; - armv7m_nvic_set_pending(env->nvic, ARMV7M_EXCP_SECURE, false); - qemu_log_mask(CPU_LOG_INT, - "...really SecureFault with SFSR.INVEP\n"); - return false; - } - if (get_phys_addr(env, addr, MMU_INST_FETCH, mmu_idx, - &physaddr, &attrs, &prot, &page_size, &fi, NULL)) { - /* the MPU lookup failed */ - env->v7m.cfsr[env->v7m.secure] |= R_V7M_CFSR_IACCVIOL_MASK; - armv7m_nvic_set_pending(env->nvic, ARMV7M_EXCP_MEM, env->v7m.secure); - qemu_log_mask(CPU_LOG_INT, "...really MemManage with CFSR.IACCVIOL\n"); - return false; - } - *insn = address_space_lduw_le(arm_addressspace(cs, attrs), physaddr, - attrs, &txres); - if (txres != MEMTX_OK) { - env->v7m.cfsr[M_REG_NS] |= R_V7M_CFSR_IBUSERR_MASK; - armv7m_nvic_set_pending(env->nvic, ARMV7M_EXCP_BUS, false); - qemu_log_mask(CPU_LOG_INT, "...really BusFault with CFSR.IBUSERR\n"); - return false; - } - return true; -} - -static bool v7m_handle_execute_nsc(ARMCPU *cpu) -{ - /* - * Check whether this attempt to execute code in a Secure & NS-Callable - * memory region is for an SG instruction; if so, then emulate the - * effect of the SG instruction and return true. Otherwise pend - * the correct kind of exception and return false. - */ - CPUARMState *env = &cpu->env; - ARMMMUIdx mmu_idx; - uint16_t insn; - - /* - * We should never get here unless get_phys_addr_pmsav8() caused - * an exception for NS executing in S&NSC memory. - */ - assert(!env->v7m.secure); - assert(arm_feature(env, ARM_FEATURE_M_SECURITY)); - - /* We want to do the MPU lookup as secure; work out what mmu_idx that is */ - mmu_idx = arm_v7m_mmu_idx_for_secstate(env, true); - - if (!v7m_read_half_insn(cpu, mmu_idx, env->regs[15], &insn)) { - return false; - } - - if (!env->thumb) { - goto gen_invep; - } - - if (insn != 0xe97f) { - /* - * Not an SG instruction first half (we choose the IMPDEF - * early-SG-check option). - */ - goto gen_invep; - } - - if (!v7m_read_half_insn(cpu, mmu_idx, env->regs[15] + 2, &insn)) { - return false; - } - - if (insn != 0xe97f) { - /* - * Not an SG instruction second half (yes, both halves of the SG - * insn have the same hex value) - */ - goto gen_invep; - } - - /* - * OK, we have confirmed that we really have an SG instruction. - * We know we're NS in S memory so don't need to repeat those checks. - */ - qemu_log_mask(CPU_LOG_INT, "...really an SG instruction at 0x%08" PRIx32 - ", executing it\n", env->regs[15]); - env->regs[14] &= ~1; - env->v7m.control[M_REG_S] &= ~R_V7M_CONTROL_SFPA_MASK; - switch_v7m_security_state(env, true); - xpsr_write(env, 0, XPSR_IT); - env->regs[15] += 4; - return true; - -gen_invep: - env->v7m.sfsr |= R_V7M_SFSR_INVEP_MASK; - armv7m_nvic_set_pending(env->nvic, ARMV7M_EXCP_SECURE, false); - qemu_log_mask(CPU_LOG_INT, - "...really SecureFault with SFSR.INVEP\n"); - return false; -} - -void arm_v7m_cpu_do_interrupt(CPUState *cs) -{ - ARMCPU *cpu = ARM_CPU(cs); - CPUARMState *env = &cpu->env; - uint32_t lr; - bool ignore_stackfaults; - - arm_log_exception(cs->exception_index); - - /* - * For exceptions we just mark as pending on the NVIC, and let that - * handle it. - */ - switch (cs->exception_index) { - case EXCP_UDEF: - armv7m_nvic_set_pending(env->nvic, ARMV7M_EXCP_USAGE, env->v7m.secure); - env->v7m.cfsr[env->v7m.secure] |= R_V7M_CFSR_UNDEFINSTR_MASK; - break; - case EXCP_NOCP: - { - /* - * NOCP might be directed to something other than the current - * security state if this fault is because of NSACR; we indicate - * the target security state using exception.target_el. - */ - int target_secstate; - - if (env->exception.target_el == 3) { - target_secstate = M_REG_S; - } else { - target_secstate = env->v7m.secure; - } - armv7m_nvic_set_pending(env->nvic, ARMV7M_EXCP_USAGE, target_secstate); - env->v7m.cfsr[target_secstate] |= R_V7M_CFSR_NOCP_MASK; - break; - } - case EXCP_INVSTATE: - armv7m_nvic_set_pending(env->nvic, ARMV7M_EXCP_USAGE, env->v7m.secure); - env->v7m.cfsr[env->v7m.secure] |= R_V7M_CFSR_INVSTATE_MASK; - break; - case EXCP_STKOF: - armv7m_nvic_set_pending(env->nvic, ARMV7M_EXCP_USAGE, env->v7m.secure); - env->v7m.cfsr[env->v7m.secure] |= R_V7M_CFSR_STKOF_MASK; - break; - case EXCP_LSERR: - armv7m_nvic_set_pending(env->nvic, ARMV7M_EXCP_SECURE, false); - env->v7m.sfsr |= R_V7M_SFSR_LSERR_MASK; - break; - case EXCP_UNALIGNED: - armv7m_nvic_set_pending(env->nvic, ARMV7M_EXCP_USAGE, env->v7m.secure); - env->v7m.cfsr[env->v7m.secure] |= R_V7M_CFSR_UNALIGNED_MASK; - break; - case EXCP_SWI: - /* The PC already points to the next instruction. */ - armv7m_nvic_set_pending(env->nvic, ARMV7M_EXCP_SVC, env->v7m.secure); - break; - case EXCP_PREFETCH_ABORT: - case EXCP_DATA_ABORT: - /* - * Note that for M profile we don't have a guest facing FSR, but - * the env->exception.fsr will be populated by the code that - * raises the fault, in the A profile short-descriptor format. - */ - switch (env->exception.fsr & 0xf) { - case M_FAKE_FSR_NSC_EXEC: - /* - * Exception generated when we try to execute code at an address - * which is marked as Secure & Non-Secure Callable and the CPU - * is in the Non-Secure state. The only instruction which can - * be executed like this is SG (and that only if both halves of - * the SG instruction have the same security attributes.) - * Everything else must generate an INVEP SecureFault, so we - * emulate the SG instruction here. - */ - if (v7m_handle_execute_nsc(cpu)) { - return; - } - break; - case M_FAKE_FSR_SFAULT: - /* - * Various flavours of SecureFault for attempts to execute or - * access data in the wrong security state. - */ - switch (cs->exception_index) { - case EXCP_PREFETCH_ABORT: - if (env->v7m.secure) { - env->v7m.sfsr |= R_V7M_SFSR_INVTRAN_MASK; - qemu_log_mask(CPU_LOG_INT, - "...really SecureFault with SFSR.INVTRAN\n"); - } else { - env->v7m.sfsr |= R_V7M_SFSR_INVEP_MASK; - qemu_log_mask(CPU_LOG_INT, - "...really SecureFault with SFSR.INVEP\n"); - } - break; - case EXCP_DATA_ABORT: - /* This must be an NS access to S memory */ - env->v7m.sfsr |= R_V7M_SFSR_AUVIOL_MASK; - qemu_log_mask(CPU_LOG_INT, - "...really SecureFault with SFSR.AUVIOL\n"); - break; - } - armv7m_nvic_set_pending(env->nvic, ARMV7M_EXCP_SECURE, false); - break; - case 0x8: /* External Abort */ - switch (cs->exception_index) { - case EXCP_PREFETCH_ABORT: - env->v7m.cfsr[M_REG_NS] |= R_V7M_CFSR_IBUSERR_MASK; - qemu_log_mask(CPU_LOG_INT, "...with CFSR.IBUSERR\n"); - break; - case EXCP_DATA_ABORT: - env->v7m.cfsr[M_REG_NS] |= - (R_V7M_CFSR_PRECISERR_MASK | R_V7M_CFSR_BFARVALID_MASK); - env->v7m.bfar = env->exception.vaddress; - qemu_log_mask(CPU_LOG_INT, - "...with CFSR.PRECISERR and BFAR 0x%x\n", - env->v7m.bfar); - break; - } - armv7m_nvic_set_pending(env->nvic, ARMV7M_EXCP_BUS, false); - break; - default: - /* - * All other FSR values are either MPU faults or "can't happen - * for M profile" cases. - */ - switch (cs->exception_index) { - case EXCP_PREFETCH_ABORT: - env->v7m.cfsr[env->v7m.secure] |= R_V7M_CFSR_IACCVIOL_MASK; - qemu_log_mask(CPU_LOG_INT, "...with CFSR.IACCVIOL\n"); - break; - case EXCP_DATA_ABORT: - env->v7m.cfsr[env->v7m.secure] |= - (R_V7M_CFSR_DACCVIOL_MASK | R_V7M_CFSR_MMARVALID_MASK); - env->v7m.mmfar[env->v7m.secure] = env->exception.vaddress; - qemu_log_mask(CPU_LOG_INT, - "...with CFSR.DACCVIOL and MMFAR 0x%x\n", - env->v7m.mmfar[env->v7m.secure]); - break; - } - armv7m_nvic_set_pending(env->nvic, ARMV7M_EXCP_MEM, - env->v7m.secure); - break; - } - break; - case EXCP_BKPT: - if (semihosting_enabled()) { - int nr; - nr = arm_lduw_code(env, env->regs[15], arm_sctlr_b(env)) & 0xff; - if (nr == 0xab) { - env->regs[15] += 2; - qemu_log_mask(CPU_LOG_INT, - "...handling as semihosting call 0x%x\n", - env->regs[0]); - env->regs[0] = do_arm_semihosting(env); - return; - } - } - armv7m_nvic_set_pending(env->nvic, ARMV7M_EXCP_DEBUG, false); - break; - case EXCP_IRQ: - break; - case EXCP_EXCEPTION_EXIT: - if (env->regs[15] < EXC_RETURN_MIN_MAGIC) { - /* Must be v8M security extension function return */ - assert(env->regs[15] >= FNC_RETURN_MIN_MAGIC); - assert(arm_feature(env, ARM_FEATURE_M_SECURITY)); - if (do_v7m_function_return(cpu)) { - return; - } - } else { - do_v7m_exception_exit(cpu); - return; - } - break; - case EXCP_LAZYFP: - /* - * We already pended the specific exception in the NVIC in the - * v7m_preserve_fp_state() helper function. - */ - break; - default: - cpu_abort(cs, "Unhandled exception 0x%x\n", cs->exception_index); - return; /* Never happens. Keep compiler happy. */ - } - - if (arm_feature(env, ARM_FEATURE_V8)) { - lr = R_V7M_EXCRET_RES1_MASK | - R_V7M_EXCRET_DCRS_MASK; - /* - * The S bit indicates whether we should return to Secure - * or NonSecure (ie our current state). - * The ES bit indicates whether we're taking this exception - * to Secure or NonSecure (ie our target state). We set it - * later, in v7m_exception_taken(). - * The SPSEL bit is also set in v7m_exception_taken() for v8M. - * This corresponds to the ARM ARM pseudocode for v8M setting - * some LR bits in PushStack() and some in ExceptionTaken(); - * the distinction matters for the tailchain cases where we - * can take an exception without pushing the stack. - */ - if (env->v7m.secure) { - lr |= R_V7M_EXCRET_S_MASK; - } - if (!(env->v7m.control[M_REG_S] & R_V7M_CONTROL_FPCA_MASK)) { - lr |= R_V7M_EXCRET_FTYPE_MASK; - } - } else { - lr = R_V7M_EXCRET_RES1_MASK | - R_V7M_EXCRET_S_MASK | - R_V7M_EXCRET_DCRS_MASK | - R_V7M_EXCRET_FTYPE_MASK | - R_V7M_EXCRET_ES_MASK; - if (env->v7m.control[M_REG_NS] & R_V7M_CONTROL_SPSEL_MASK) { - lr |= R_V7M_EXCRET_SPSEL_MASK; - } - } - if (!arm_v7m_is_handler_mode(env)) { - lr |= R_V7M_EXCRET_MODE_MASK; - } - - ignore_stackfaults = v7m_push_stack(cpu); - v7m_exception_taken(cpu, lr, false, ignore_stackfaults); -} - /* * Function used to synchronize QEMU's AArch64 register set with AArch32 * register set. This is necessary when switching between AArch32 and AArch64 @@ -12754,466 +10613,6 @@ hwaddr arm_cpu_get_phys_page_attrs_debug(CPUState *cs, vaddr addr, return phys_addr; } -uint32_t HELPER(v7m_mrs)(CPUARMState *env, uint32_t reg) -{ - uint32_t mask; - unsigned el = arm_current_el(env); - - /* First handle registers which unprivileged can read */ - - switch (reg) { - case 0 ... 7: /* xPSR sub-fields */ - mask = 0; - if ((reg & 1) && el) { - mask |= XPSR_EXCP; /* IPSR (unpriv. reads as zero) */ - } - if (!(reg & 4)) { - mask |= XPSR_NZCV | XPSR_Q; /* APSR */ - if (arm_feature(env, ARM_FEATURE_THUMB_DSP)) { - mask |= XPSR_GE; - } - } - /* EPSR reads as zero */ - return xpsr_read(env) & mask; - break; - case 20: /* CONTROL */ - { - uint32_t value = env->v7m.control[env->v7m.secure]; - if (!env->v7m.secure) { - /* SFPA is RAZ/WI from NS; FPCA is stored in the M_REG_S bank */ - value |= env->v7m.control[M_REG_S] & R_V7M_CONTROL_FPCA_MASK; - } - return value; - } - case 0x94: /* CONTROL_NS */ - /* - * We have to handle this here because unprivileged Secure code - * can read the NS CONTROL register. - */ - if (!env->v7m.secure) { - return 0; - } - return env->v7m.control[M_REG_NS] | - (env->v7m.control[M_REG_S] & R_V7M_CONTROL_FPCA_MASK); - } - - if (el == 0) { - return 0; /* unprivileged reads others as zero */ - } - - if (arm_feature(env, ARM_FEATURE_M_SECURITY)) { - switch (reg) { - case 0x88: /* MSP_NS */ - if (!env->v7m.secure) { - return 0; - } - return env->v7m.other_ss_msp; - case 0x89: /* PSP_NS */ - if (!env->v7m.secure) { - return 0; - } - return env->v7m.other_ss_psp; - case 0x8a: /* MSPLIM_NS */ - if (!env->v7m.secure) { - return 0; - } - return env->v7m.msplim[M_REG_NS]; - case 0x8b: /* PSPLIM_NS */ - if (!env->v7m.secure) { - return 0; - } - return env->v7m.psplim[M_REG_NS]; - case 0x90: /* PRIMASK_NS */ - if (!env->v7m.secure) { - return 0; - } - return env->v7m.primask[M_REG_NS]; - case 0x91: /* BASEPRI_NS */ - if (!env->v7m.secure) { - return 0; - } - return env->v7m.basepri[M_REG_NS]; - case 0x93: /* FAULTMASK_NS */ - if (!env->v7m.secure) { - return 0; - } - return env->v7m.faultmask[M_REG_NS]; - case 0x98: /* SP_NS */ - { - /* - * This gives the non-secure SP selected based on whether we're - * currently in handler mode or not, using the NS CONTROL.SPSEL. - */ - bool spsel = env->v7m.control[M_REG_NS] & R_V7M_CONTROL_SPSEL_MASK; - - if (!env->v7m.secure) { - return 0; - } - if (!arm_v7m_is_handler_mode(env) && spsel) { - return env->v7m.other_ss_psp; - } else { - return env->v7m.other_ss_msp; - } - } - default: - break; - } - } - - switch (reg) { - case 8: /* MSP */ - return v7m_using_psp(env) ? env->v7m.other_sp : env->regs[13]; - case 9: /* PSP */ - return v7m_using_psp(env) ? env->regs[13] : env->v7m.other_sp; - case 10: /* MSPLIM */ - if (!arm_feature(env, ARM_FEATURE_V8)) { - goto bad_reg; - } - return env->v7m.msplim[env->v7m.secure]; - case 11: /* PSPLIM */ - if (!arm_feature(env, ARM_FEATURE_V8)) { - goto bad_reg; - } - return env->v7m.psplim[env->v7m.secure]; - case 16: /* PRIMASK */ - return env->v7m.primask[env->v7m.secure]; - case 17: /* BASEPRI */ - case 18: /* BASEPRI_MAX */ - return env->v7m.basepri[env->v7m.secure]; - case 19: /* FAULTMASK */ - return env->v7m.faultmask[env->v7m.secure]; - default: - bad_reg: - qemu_log_mask(LOG_GUEST_ERROR, "Attempt to read unknown special" - " register %d\n", reg); - return 0; - } -} - -void HELPER(v7m_msr)(CPUARMState *env, uint32_t maskreg, uint32_t val) -{ - /* - * We're passed bits [11..0] of the instruction; extract - * SYSm and the mask bits. - * Invalid combinations of SYSm and mask are UNPREDICTABLE; - * we choose to treat them as if the mask bits were valid. - * NB that the pseudocode 'mask' variable is bits [11..10], - * whereas ours is [11..8]. - */ - uint32_t mask = extract32(maskreg, 8, 4); - uint32_t reg = extract32(maskreg, 0, 8); - int cur_el = arm_current_el(env); - - if (cur_el == 0 && reg > 7 && reg != 20) { - /* - * only xPSR sub-fields and CONTROL.SFPA may be written by - * unprivileged code - */ - return; - } - - if (arm_feature(env, ARM_FEATURE_M_SECURITY)) { - switch (reg) { - case 0x88: /* MSP_NS */ - if (!env->v7m.secure) { - return; - } - env->v7m.other_ss_msp = val; - return; - case 0x89: /* PSP_NS */ - if (!env->v7m.secure) { - return; - } - env->v7m.other_ss_psp = val; - return; - case 0x8a: /* MSPLIM_NS */ - if (!env->v7m.secure) { - return; - } - env->v7m.msplim[M_REG_NS] = val & ~7; - return; - case 0x8b: /* PSPLIM_NS */ - if (!env->v7m.secure) { - return; - } - env->v7m.psplim[M_REG_NS] = val & ~7; - return; - case 0x90: /* PRIMASK_NS */ - if (!env->v7m.secure) { - return; - } - env->v7m.primask[M_REG_NS] = val & 1; - return; - case 0x91: /* BASEPRI_NS */ - if (!env->v7m.secure || !arm_feature(env, ARM_FEATURE_M_MAIN)) { - return; - } - env->v7m.basepri[M_REG_NS] = val & 0xff; - return; - case 0x93: /* FAULTMASK_NS */ - if (!env->v7m.secure || !arm_feature(env, ARM_FEATURE_M_MAIN)) { - return; - } - env->v7m.faultmask[M_REG_NS] = val & 1; - return; - case 0x94: /* CONTROL_NS */ - if (!env->v7m.secure) { - return; - } - write_v7m_control_spsel_for_secstate(env, - val & R_V7M_CONTROL_SPSEL_MASK, - M_REG_NS); - if (arm_feature(env, ARM_FEATURE_M_MAIN)) { - env->v7m.control[M_REG_NS] &= ~R_V7M_CONTROL_NPRIV_MASK; - env->v7m.control[M_REG_NS] |= val & R_V7M_CONTROL_NPRIV_MASK; - } - /* - * SFPA is RAZ/WI from NS. FPCA is RO if NSACR.CP10 == 0, - * RES0 if the FPU is not present, and is stored in the S bank - */ - if (arm_feature(env, ARM_FEATURE_VFP) && - extract32(env->v7m.nsacr, 10, 1)) { - env->v7m.control[M_REG_S] &= ~R_V7M_CONTROL_FPCA_MASK; - env->v7m.control[M_REG_S] |= val & R_V7M_CONTROL_FPCA_MASK; - } - return; - case 0x98: /* SP_NS */ - { - /* - * This gives the non-secure SP selected based on whether we're - * currently in handler mode or not, using the NS CONTROL.SPSEL. - */ - bool spsel = env->v7m.control[M_REG_NS] & R_V7M_CONTROL_SPSEL_MASK; - bool is_psp = !arm_v7m_is_handler_mode(env) && spsel; - uint32_t limit; - - if (!env->v7m.secure) { - return; - } - - limit = is_psp ? env->v7m.psplim[false] : env->v7m.msplim[false]; - - if (val < limit) { - CPUState *cs = env_cpu(env); - - cpu_restore_state(cs, GETPC(), true); - raise_exception(env, EXCP_STKOF, 0, 1); - } - - if (is_psp) { - env->v7m.other_ss_psp = val; - } else { - env->v7m.other_ss_msp = val; - } - return; - } - default: - break; - } - } - - switch (reg) { - case 0 ... 7: /* xPSR sub-fields */ - /* only APSR is actually writable */ - if (!(reg & 4)) { - uint32_t apsrmask = 0; - - if (mask & 8) { - apsrmask |= XPSR_NZCV | XPSR_Q; - } - if ((mask & 4) && arm_feature(env, ARM_FEATURE_THUMB_DSP)) { - apsrmask |= XPSR_GE; - } - xpsr_write(env, val, apsrmask); - } - break; - case 8: /* MSP */ - if (v7m_using_psp(env)) { - env->v7m.other_sp = val; - } else { - env->regs[13] = val; - } - break; - case 9: /* PSP */ - if (v7m_using_psp(env)) { - env->regs[13] = val; - } else { - env->v7m.other_sp = val; - } - break; - case 10: /* MSPLIM */ - if (!arm_feature(env, ARM_FEATURE_V8)) { - goto bad_reg; - } - env->v7m.msplim[env->v7m.secure] = val & ~7; - break; - case 11: /* PSPLIM */ - if (!arm_feature(env, ARM_FEATURE_V8)) { - goto bad_reg; - } - env->v7m.psplim[env->v7m.secure] = val & ~7; - break; - case 16: /* PRIMASK */ - env->v7m.primask[env->v7m.secure] = val & 1; - break; - case 17: /* BASEPRI */ - if (!arm_feature(env, ARM_FEATURE_M_MAIN)) { - goto bad_reg; - } - env->v7m.basepri[env->v7m.secure] = val & 0xff; - break; - case 18: /* BASEPRI_MAX */ - if (!arm_feature(env, ARM_FEATURE_M_MAIN)) { - goto bad_reg; - } - val &= 0xff; - if (val != 0 && (val < env->v7m.basepri[env->v7m.secure] - || env->v7m.basepri[env->v7m.secure] == 0)) { - env->v7m.basepri[env->v7m.secure] = val; - } - break; - case 19: /* FAULTMASK */ - if (!arm_feature(env, ARM_FEATURE_M_MAIN)) { - goto bad_reg; - } - env->v7m.faultmask[env->v7m.secure] = val & 1; - break; - case 20: /* CONTROL */ - /* - * Writing to the SPSEL bit only has an effect if we are in - * thread mode; other bits can be updated by any privileged code. - * write_v7m_control_spsel() deals with updating the SPSEL bit in - * env->v7m.control, so we only need update the others. - * For v7M, we must just ignore explicit writes to SPSEL in handler - * mode; for v8M the write is permitted but will have no effect. - * All these bits are writes-ignored from non-privileged code, - * except for SFPA. - */ - if (cur_el > 0 && (arm_feature(env, ARM_FEATURE_V8) || - !arm_v7m_is_handler_mode(env))) { - write_v7m_control_spsel(env, (val & R_V7M_CONTROL_SPSEL_MASK) != 0); - } - if (cur_el > 0 && arm_feature(env, ARM_FEATURE_M_MAIN)) { - env->v7m.control[env->v7m.secure] &= ~R_V7M_CONTROL_NPRIV_MASK; - env->v7m.control[env->v7m.secure] |= val & R_V7M_CONTROL_NPRIV_MASK; - } - if (arm_feature(env, ARM_FEATURE_VFP)) { - /* - * SFPA is RAZ/WI from NS or if no FPU. - * FPCA is RO if NSACR.CP10 == 0, RES0 if the FPU is not present. - * Both are stored in the S bank. - */ - if (env->v7m.secure) { - env->v7m.control[M_REG_S] &= ~R_V7M_CONTROL_SFPA_MASK; - env->v7m.control[M_REG_S] |= val & R_V7M_CONTROL_SFPA_MASK; - } - if (cur_el > 0 && - (env->v7m.secure || !arm_feature(env, ARM_FEATURE_M_SECURITY) || - extract32(env->v7m.nsacr, 10, 1))) { - env->v7m.control[M_REG_S] &= ~R_V7M_CONTROL_FPCA_MASK; - env->v7m.control[M_REG_S] |= val & R_V7M_CONTROL_FPCA_MASK; - } - } - break; - default: - bad_reg: - qemu_log_mask(LOG_GUEST_ERROR, "Attempt to write unknown special" - " register %d\n", reg); - return; - } -} - -uint32_t HELPER(v7m_tt)(CPUARMState *env, uint32_t addr, uint32_t op) -{ - /* Implement the TT instruction. op is bits [7:6] of the insn. */ - bool forceunpriv = op & 1; - bool alt = op & 2; - V8M_SAttributes sattrs = {}; - uint32_t tt_resp; - bool r, rw, nsr, nsrw, mrvalid; - int prot; - ARMMMUFaultInfo fi = {}; - MemTxAttrs attrs = {}; - hwaddr phys_addr; - ARMMMUIdx mmu_idx; - uint32_t mregion; - bool targetpriv; - bool targetsec = env->v7m.secure; - bool is_subpage; - - /* - * Work out what the security state and privilege level we're - * interested in is... - */ - if (alt) { - targetsec = !targetsec; - } - - if (forceunpriv) { - targetpriv = false; - } else { - targetpriv = arm_v7m_is_handler_mode(env) || - !(env->v7m.control[targetsec] & R_V7M_CONTROL_NPRIV_MASK); - } - - /* ...and then figure out which MMU index this is */ - mmu_idx = arm_v7m_mmu_idx_for_secstate_and_priv(env, targetsec, targetpriv); - - /* - * We know that the MPU and SAU don't care about the access type - * for our purposes beyond that we don't want to claim to be - * an insn fetch, so we arbitrarily call this a read. - */ - - /* - * MPU region info only available for privileged or if - * inspecting the other MPU state. - */ - if (arm_current_el(env) != 0 || alt) { - /* We can ignore the return value as prot is always set */ - pmsav8_mpu_lookup(env, addr, MMU_DATA_LOAD, mmu_idx, - &phys_addr, &attrs, &prot, &is_subpage, - &fi, &mregion); - if (mregion == -1) { - mrvalid = false; - mregion = 0; - } else { - mrvalid = true; - } - r = prot & PAGE_READ; - rw = prot & PAGE_WRITE; - } else { - r = false; - rw = false; - mrvalid = false; - mregion = 0; - } - - if (env->v7m.secure) { - v8m_security_lookup(env, addr, MMU_DATA_LOAD, mmu_idx, &sattrs); - nsr = sattrs.ns && r; - nsrw = sattrs.ns && rw; - } else { - sattrs.ns = true; - nsr = false; - nsrw = false; - } - - tt_resp = (sattrs.iregion << 24) | - (sattrs.irvalid << 23) | - ((!sattrs.ns) << 22) | - (nsrw << 21) | - (nsr << 20) | - (rw << 19) | - (r << 18) | - (sattrs.srvalid << 17) | - (mrvalid << 16) | - (sattrs.sregion << 8) | - mregion; - - return tt_resp; -} - #endif /* Note that signed overflow is undefined in C. The following routines are @@ -13578,41 +10977,12 @@ int fp_exception_el(CPUARMState *env, int cur_el) return 0; } -ARMMMUIdx arm_v7m_mmu_idx_all(CPUARMState *env, - bool secstate, bool priv, bool negpri) -{ - ARMMMUIdx mmu_idx = ARM_MMU_IDX_M; - - if (priv) { - mmu_idx |= ARM_MMU_IDX_M_PRIV; - } - - if (negpri) { - mmu_idx |= ARM_MMU_IDX_M_NEGPRI; - } - - if (secstate) { - mmu_idx |= ARM_MMU_IDX_M_S; - } - - return mmu_idx; -} - -ARMMMUIdx arm_v7m_mmu_idx_for_secstate_and_priv(CPUARMState *env, - bool secstate, bool priv) -{ - bool negpri = armv7m_nvic_neg_prio_requested(env->nvic, secstate); - - return arm_v7m_mmu_idx_all(env, secstate, priv, negpri); -} - -/* Return the MMU index for a v7M CPU in the specified security state */ +#ifndef CONFIG_TCG ARMMMUIdx arm_v7m_mmu_idx_for_secstate(CPUARMState *env, bool secstate) { - bool priv = arm_current_el(env) != 0; - - return arm_v7m_mmu_idx_for_secstate_and_priv(env, secstate, priv); + g_assert_not_reached(); } +#endif ARMMMUIdx arm_mmu_idx(CPUARMState *env) { diff --git a/target/arm/m_helper.c b/target/arm/m_helper.c new file mode 100644 index 00000000000..1b0ad95a05f --- /dev/null +++ b/target/arm/m_helper.c @@ -0,0 +1,2676 @@ +/* + * ARM generic helpers. + * + * This code is licensed under the GNU GPL v2 or later. + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ +#include "qemu/osdep.h" +#include "qemu/units.h" +#include "target/arm/idau.h" +#include "trace.h" +#include "cpu.h" +#include "internals.h" +#include "exec/gdbstub.h" +#include "exec/helper-proto.h" +#include "qemu/host-utils.h" +#include "sysemu/sysemu.h" +#include "qemu/bitops.h" +#include "qemu/crc32c.h" +#include "qemu/qemu-print.h" +#include "exec/exec-all.h" +#include /* For crc32 */ +#include "hw/semihosting/semihost.h" +#include "sysemu/cpus.h" +#include "sysemu/kvm.h" +#include "qemu/range.h" +#include "qapi/qapi-commands-target.h" +#include "qapi/error.h" +#include "qemu/guest-random.h" +#ifdef CONFIG_TCG +#include "arm_ldst.h" +#include "exec/cpu_ldst.h" +#endif + +#ifdef CONFIG_USER_ONLY + +/* These should probably raise undefined insn exceptions. */ +void HELPER(v7m_msr)(CPUARMState *env, uint32_t reg, uint32_t val) +{ + ARMCPU *cpu = env_archcpu(env); + + cpu_abort(CPU(cpu), "v7m_msr %d\n", reg); +} + +uint32_t HELPER(v7m_mrs)(CPUARMState *env, uint32_t reg) +{ + ARMCPU *cpu = env_archcpu(env); + + cpu_abort(CPU(cpu), "v7m_mrs %d\n", reg); + return 0; +} + +void HELPER(v7m_bxns)(CPUARMState *env, uint32_t dest) +{ + /* translate.c should never generate calls here in user-only mode */ + g_assert_not_reached(); +} + +void HELPER(v7m_blxns)(CPUARMState *env, uint32_t dest) +{ + /* translate.c should never generate calls here in user-only mode */ + g_assert_not_reached(); +} + +void HELPER(v7m_preserve_fp_state)(CPUARMState *env) +{ + /* translate.c should never generate calls here in user-only mode */ + g_assert_not_reached(); +} + +void HELPER(v7m_vlstm)(CPUARMState *env, uint32_t fptr) +{ + /* translate.c should never generate calls here in user-only mode */ + g_assert_not_reached(); +} + +void HELPER(v7m_vlldm)(CPUARMState *env, uint32_t fptr) +{ + /* translate.c should never generate calls here in user-only mode */ + g_assert_not_reached(); +} + +uint32_t HELPER(v7m_tt)(CPUARMState *env, uint32_t addr, uint32_t op) +{ + /* + * The TT instructions can be used by unprivileged code, but in + * user-only emulation we don't have the MPU. + * Luckily since we know we are NonSecure unprivileged (and that in + * turn means that the A flag wasn't specified), all the bits in the + * register must be zero: + * IREGION: 0 because IRVALID is 0 + * IRVALID: 0 because NS + * S: 0 because NS + * NSRW: 0 because NS + * NSR: 0 because NS + * RW: 0 because unpriv and A flag not set + * R: 0 because unpriv and A flag not set + * SRVALID: 0 because NS + * MRVALID: 0 because unpriv and A flag not set + * SREGION: 0 becaus SRVALID is 0 + * MREGION: 0 because MRVALID is 0 + */ + return 0; +} + +#else + +/* + * What kind of stack write are we doing? This affects how exceptions + * generated during the stacking are treated. + */ +typedef enum StackingMode { + STACK_NORMAL, + STACK_IGNFAULTS, + STACK_LAZYFP, +} StackingMode; + +static bool v7m_stack_write(ARMCPU *cpu, uint32_t addr, uint32_t value, + ARMMMUIdx mmu_idx, StackingMode mode) +{ + CPUState *cs = CPU(cpu); + CPUARMState *env = &cpu->env; + MemTxAttrs attrs = {}; + MemTxResult txres; + target_ulong page_size; + hwaddr physaddr; + int prot; + ARMMMUFaultInfo fi = {}; + bool secure = mmu_idx & ARM_MMU_IDX_M_S; + int exc; + bool exc_secure; + + if (get_phys_addr(env, addr, MMU_DATA_STORE, mmu_idx, &physaddr, + &attrs, &prot, &page_size, &fi, NULL)) { + /* MPU/SAU lookup failed */ + if (fi.type == ARMFault_QEMU_SFault) { + if (mode == STACK_LAZYFP) { + qemu_log_mask(CPU_LOG_INT, + "...SecureFault with SFSR.LSPERR " + "during lazy stacking\n"); + env->v7m.sfsr |= R_V7M_SFSR_LSPERR_MASK; + } else { + qemu_log_mask(CPU_LOG_INT, + "...SecureFault with SFSR.AUVIOL " + "during stacking\n"); + env->v7m.sfsr |= R_V7M_SFSR_AUVIOL_MASK; + } + env->v7m.sfsr |= R_V7M_SFSR_SFARVALID_MASK; + env->v7m.sfar = addr; + exc = ARMV7M_EXCP_SECURE; + exc_secure = false; + } else { + if (mode == STACK_LAZYFP) { + qemu_log_mask(CPU_LOG_INT, + "...MemManageFault with CFSR.MLSPERR\n"); + env->v7m.cfsr[secure] |= R_V7M_CFSR_MLSPERR_MASK; + } else { + qemu_log_mask(CPU_LOG_INT, + "...MemManageFault with CFSR.MSTKERR\n"); + env->v7m.cfsr[secure] |= R_V7M_CFSR_MSTKERR_MASK; + } + exc = ARMV7M_EXCP_MEM; + exc_secure = secure; + } + goto pend_fault; + } + address_space_stl_le(arm_addressspace(cs, attrs), physaddr, value, + attrs, &txres); + if (txres != MEMTX_OK) { + /* BusFault trying to write the data */ + if (mode == STACK_LAZYFP) { + qemu_log_mask(CPU_LOG_INT, "...BusFault with BFSR.LSPERR\n"); + env->v7m.cfsr[M_REG_NS] |= R_V7M_CFSR_LSPERR_MASK; + } else { + qemu_log_mask(CPU_LOG_INT, "...BusFault with BFSR.STKERR\n"); + env->v7m.cfsr[M_REG_NS] |= R_V7M_CFSR_STKERR_MASK; + } + exc = ARMV7M_EXCP_BUS; + exc_secure = false; + goto pend_fault; + } + return true; + +pend_fault: + /* + * By pending the exception at this point we are making + * the IMPDEF choice "overridden exceptions pended" (see the + * MergeExcInfo() pseudocode). The other choice would be to not + * pend them now and then make a choice about which to throw away + * later if we have two derived exceptions. + * The only case when we must not pend the exception but instead + * throw it away is if we are doing the push of the callee registers + * and we've already generated a derived exception (this is indicated + * by the caller passing STACK_IGNFAULTS). Even in this case we will + * still update the fault status registers. + */ + switch (mode) { + case STACK_NORMAL: + armv7m_nvic_set_pending_derived(env->nvic, exc, exc_secure); + break; + case STACK_LAZYFP: + armv7m_nvic_set_pending_lazyfp(env->nvic, exc, exc_secure); + break; + case STACK_IGNFAULTS: + break; + } + return false; +} + +static bool v7m_stack_read(ARMCPU *cpu, uint32_t *dest, uint32_t addr, + ARMMMUIdx mmu_idx) +{ + CPUState *cs = CPU(cpu); + CPUARMState *env = &cpu->env; + MemTxAttrs attrs = {}; + MemTxResult txres; + target_ulong page_size; + hwaddr physaddr; + int prot; + ARMMMUFaultInfo fi = {}; + bool secure = mmu_idx & ARM_MMU_IDX_M_S; + int exc; + bool exc_secure; + uint32_t value; + + if (get_phys_addr(env, addr, MMU_DATA_LOAD, mmu_idx, &physaddr, + &attrs, &prot, &page_size, &fi, NULL)) { + /* MPU/SAU lookup failed */ + if (fi.type == ARMFault_QEMU_SFault) { + qemu_log_mask(CPU_LOG_INT, + "...SecureFault with SFSR.AUVIOL during unstack\n"); + env->v7m.sfsr |= R_V7M_SFSR_AUVIOL_MASK | R_V7M_SFSR_SFARVALID_MASK; + env->v7m.sfar = addr; + exc = ARMV7M_EXCP_SECURE; + exc_secure = false; + } else { + qemu_log_mask(CPU_LOG_INT, + "...MemManageFault with CFSR.MUNSTKERR\n"); + env->v7m.cfsr[secure] |= R_V7M_CFSR_MUNSTKERR_MASK; + exc = ARMV7M_EXCP_MEM; + exc_secure = secure; + } + goto pend_fault; + } + + value = address_space_ldl(arm_addressspace(cs, attrs), physaddr, + attrs, &txres); + if (txres != MEMTX_OK) { + /* BusFault trying to read the data */ + qemu_log_mask(CPU_LOG_INT, "...BusFault with BFSR.UNSTKERR\n"); + env->v7m.cfsr[M_REG_NS] |= R_V7M_CFSR_UNSTKERR_MASK; + exc = ARMV7M_EXCP_BUS; + exc_secure = false; + goto pend_fault; + } + + *dest = value; + return true; + +pend_fault: + /* + * By pending the exception at this point we are making + * the IMPDEF choice "overridden exceptions pended" (see the + * MergeExcInfo() pseudocode). The other choice would be to not + * pend them now and then make a choice about which to throw away + * later if we have two derived exceptions. + */ + armv7m_nvic_set_pending(env->nvic, exc, exc_secure); + return false; +} + +void HELPER(v7m_preserve_fp_state)(CPUARMState *env) +{ + /* + * Preserve FP state (because LSPACT was set and we are about + * to execute an FP instruction). This corresponds to the + * PreserveFPState() pseudocode. + * We may throw an exception if the stacking fails. + */ + ARMCPU *cpu = env_archcpu(env); + bool is_secure = env->v7m.fpccr[M_REG_S] & R_V7M_FPCCR_S_MASK; + bool negpri = !(env->v7m.fpccr[M_REG_S] & R_V7M_FPCCR_HFRDY_MASK); + bool is_priv = !(env->v7m.fpccr[is_secure] & R_V7M_FPCCR_USER_MASK); + bool splimviol = env->v7m.fpccr[is_secure] & R_V7M_FPCCR_SPLIMVIOL_MASK; + uint32_t fpcar = env->v7m.fpcar[is_secure]; + bool stacked_ok = true; + bool ts = is_secure && (env->v7m.fpccr[M_REG_S] & R_V7M_FPCCR_TS_MASK); + bool take_exception; + + /* Take the iothread lock as we are going to touch the NVIC */ + qemu_mutex_lock_iothread(); + + /* Check the background context had access to the FPU */ + if (!v7m_cpacr_pass(env, is_secure, is_priv)) { + armv7m_nvic_set_pending_lazyfp(env->nvic, ARMV7M_EXCP_USAGE, is_secure); + env->v7m.cfsr[is_secure] |= R_V7M_CFSR_NOCP_MASK; + stacked_ok = false; + } else if (!is_secure && !extract32(env->v7m.nsacr, 10, 1)) { + armv7m_nvic_set_pending_lazyfp(env->nvic, ARMV7M_EXCP_USAGE, M_REG_S); + env->v7m.cfsr[M_REG_S] |= R_V7M_CFSR_NOCP_MASK; + stacked_ok = false; + } + + if (!splimviol && stacked_ok) { + /* We only stack if the stack limit wasn't violated */ + int i; + ARMMMUIdx mmu_idx; + + mmu_idx = arm_v7m_mmu_idx_all(env, is_secure, is_priv, negpri); + for (i = 0; i < (ts ? 32 : 16); i += 2) { + uint64_t dn = *aa32_vfp_dreg(env, i / 2); + uint32_t faddr = fpcar + 4 * i; + uint32_t slo = extract64(dn, 0, 32); + uint32_t shi = extract64(dn, 32, 32); + + if (i >= 16) { + faddr += 8; /* skip the slot for the FPSCR */ + } + stacked_ok = stacked_ok && + v7m_stack_write(cpu, faddr, slo, mmu_idx, STACK_LAZYFP) && + v7m_stack_write(cpu, faddr + 4, shi, mmu_idx, STACK_LAZYFP); + } + + stacked_ok = stacked_ok && + v7m_stack_write(cpu, fpcar + 0x40, + vfp_get_fpscr(env), mmu_idx, STACK_LAZYFP); + } + + /* + * We definitely pended an exception, but it's possible that it + * might not be able to be taken now. If its priority permits us + * to take it now, then we must not update the LSPACT or FP regs, + * but instead jump out to take the exception immediately. + * If it's just pending and won't be taken until the current + * handler exits, then we do update LSPACT and the FP regs. + */ + take_exception = !stacked_ok && + armv7m_nvic_can_take_pending_exception(env->nvic); + + qemu_mutex_unlock_iothread(); + + if (take_exception) { + raise_exception_ra(env, EXCP_LAZYFP, 0, 1, GETPC()); + } + + env->v7m.fpccr[is_secure] &= ~R_V7M_FPCCR_LSPACT_MASK; + + if (ts) { + /* Clear s0 to s31 and the FPSCR */ + int i; + + for (i = 0; i < 32; i += 2) { + *aa32_vfp_dreg(env, i / 2) = 0; + } + vfp_set_fpscr(env, 0); + } + /* + * Otherwise s0 to s15 and FPSCR are UNKNOWN; we choose to leave them + * unchanged. + */ +} + +/* + * Write to v7M CONTROL.SPSEL bit for the specified security bank. + * This may change the current stack pointer between Main and Process + * stack pointers if it is done for the CONTROL register for the current + * security state. + */ +static void write_v7m_control_spsel_for_secstate(CPUARMState *env, + bool new_spsel, + bool secstate) +{ + bool old_is_psp = v7m_using_psp(env); + + env->v7m.control[secstate] = + deposit32(env->v7m.control[secstate], + R_V7M_CONTROL_SPSEL_SHIFT, + R_V7M_CONTROL_SPSEL_LENGTH, new_spsel); + + if (secstate == env->v7m.secure) { + bool new_is_psp = v7m_using_psp(env); + uint32_t tmp; + + if (old_is_psp != new_is_psp) { + tmp = env->v7m.other_sp; + env->v7m.other_sp = env->regs[13]; + env->regs[13] = tmp; + } + } +} + +/* + * Write to v7M CONTROL.SPSEL bit. This may change the current + * stack pointer between Main and Process stack pointers. + */ +static void write_v7m_control_spsel(CPUARMState *env, bool new_spsel) +{ + write_v7m_control_spsel_for_secstate(env, new_spsel, env->v7m.secure); +} + +void write_v7m_exception(CPUARMState *env, uint32_t new_exc) +{ + /* + * Write a new value to v7m.exception, thus transitioning into or out + * of Handler mode; this may result in a change of active stack pointer. + */ + bool new_is_psp, old_is_psp = v7m_using_psp(env); + uint32_t tmp; + + env->v7m.exception = new_exc; + + new_is_psp = v7m_using_psp(env); + + if (old_is_psp != new_is_psp) { + tmp = env->v7m.other_sp; + env->v7m.other_sp = env->regs[13]; + env->regs[13] = tmp; + } +} + +/* Switch M profile security state between NS and S */ +static void switch_v7m_security_state(CPUARMState *env, bool new_secstate) +{ + uint32_t new_ss_msp, new_ss_psp; + + if (env->v7m.secure == new_secstate) { + return; + } + + /* + * All the banked state is accessed by looking at env->v7m.secure + * except for the stack pointer; rearrange the SP appropriately. + */ + new_ss_msp = env->v7m.other_ss_msp; + new_ss_psp = env->v7m.other_ss_psp; + + if (v7m_using_psp(env)) { + env->v7m.other_ss_psp = env->regs[13]; + env->v7m.other_ss_msp = env->v7m.other_sp; + } else { + env->v7m.other_ss_msp = env->regs[13]; + env->v7m.other_ss_psp = env->v7m.other_sp; + } + + env->v7m.secure = new_secstate; + + if (v7m_using_psp(env)) { + env->regs[13] = new_ss_psp; + env->v7m.other_sp = new_ss_msp; + } else { + env->regs[13] = new_ss_msp; + env->v7m.other_sp = new_ss_psp; + } +} + +void HELPER(v7m_bxns)(CPUARMState *env, uint32_t dest) +{ + /* + * Handle v7M BXNS: + * - if the return value is a magic value, do exception return (like BX) + * - otherwise bit 0 of the return value is the target security state + */ + uint32_t min_magic; + + if (arm_feature(env, ARM_FEATURE_M_SECURITY)) { + /* Covers FNC_RETURN and EXC_RETURN magic */ + min_magic = FNC_RETURN_MIN_MAGIC; + } else { + /* EXC_RETURN magic only */ + min_magic = EXC_RETURN_MIN_MAGIC; + } + + if (dest >= min_magic) { + /* + * This is an exception return magic value; put it where + * do_v7m_exception_exit() expects and raise EXCEPTION_EXIT. + * Note that if we ever add gen_ss_advance() singlestep support to + * M profile this should count as an "instruction execution complete" + * event (compare gen_bx_excret_final_code()). + */ + env->regs[15] = dest & ~1; + env->thumb = dest & 1; + HELPER(exception_internal)(env, EXCP_EXCEPTION_EXIT); + /* notreached */ + } + + /* translate.c should have made BXNS UNDEF unless we're secure */ + assert(env->v7m.secure); + + if (!(dest & 1)) { + env->v7m.control[M_REG_S] &= ~R_V7M_CONTROL_SFPA_MASK; + } + switch_v7m_security_state(env, dest & 1); + env->thumb = 1; + env->regs[15] = dest & ~1; +} + +void HELPER(v7m_blxns)(CPUARMState *env, uint32_t dest) +{ + /* + * Handle v7M BLXNS: + * - bit 0 of the destination address is the target security state + */ + + /* At this point regs[15] is the address just after the BLXNS */ + uint32_t nextinst = env->regs[15] | 1; + uint32_t sp = env->regs[13] - 8; + uint32_t saved_psr; + + /* translate.c will have made BLXNS UNDEF unless we're secure */ + assert(env->v7m.secure); + + if (dest & 1) { + /* + * Target is Secure, so this is just a normal BLX, + * except that the low bit doesn't indicate Thumb/not. + */ + env->regs[14] = nextinst; + env->thumb = 1; + env->regs[15] = dest & ~1; + return; + } + + /* Target is non-secure: first push a stack frame */ + if (!QEMU_IS_ALIGNED(sp, 8)) { + qemu_log_mask(LOG_GUEST_ERROR, + "BLXNS with misaligned SP is UNPREDICTABLE\n"); + } + + if (sp < v7m_sp_limit(env)) { + raise_exception(env, EXCP_STKOF, 0, 1); + } + + saved_psr = env->v7m.exception; + if (env->v7m.control[M_REG_S] & R_V7M_CONTROL_SFPA_MASK) { + saved_psr |= XPSR_SFPA; + } + + /* Note that these stores can throw exceptions on MPU faults */ + cpu_stl_data(env, sp, nextinst); + cpu_stl_data(env, sp + 4, saved_psr); + + env->regs[13] = sp; + env->regs[14] = 0xfeffffff; + if (arm_v7m_is_handler_mode(env)) { + /* + * Write a dummy value to IPSR, to avoid leaking the current secure + * exception number to non-secure code. This is guaranteed not + * to cause write_v7m_exception() to actually change stacks. + */ + write_v7m_exception(env, 1); + } + env->v7m.control[M_REG_S] &= ~R_V7M_CONTROL_SFPA_MASK; + switch_v7m_security_state(env, 0); + env->thumb = 1; + env->regs[15] = dest; +} + +static uint32_t *get_v7m_sp_ptr(CPUARMState *env, bool secure, bool threadmode, + bool spsel) +{ + /* + * Return a pointer to the location where we currently store the + * stack pointer for the requested security state and thread mode. + * This pointer will become invalid if the CPU state is updated + * such that the stack pointers are switched around (eg changing + * the SPSEL control bit). + * Compare the v8M ARM ARM pseudocode LookUpSP_with_security_mode(). + * Unlike that pseudocode, we require the caller to pass us in the + * SPSEL control bit value; this is because we also use this + * function in handling of pushing of the callee-saves registers + * part of the v8M stack frame (pseudocode PushCalleeStack()), + * and in the tailchain codepath the SPSEL bit comes from the exception + * return magic LR value from the previous exception. The pseudocode + * opencodes the stack-selection in PushCalleeStack(), but we prefer + * to make this utility function generic enough to do the job. + */ + bool want_psp = threadmode && spsel; + + if (secure == env->v7m.secure) { + if (want_psp == v7m_using_psp(env)) { + return &env->regs[13]; + } else { + return &env->v7m.other_sp; + } + } else { + if (want_psp) { + return &env->v7m.other_ss_psp; + } else { + return &env->v7m.other_ss_msp; + } + } +} + +static bool arm_v7m_load_vector(ARMCPU *cpu, int exc, bool targets_secure, + uint32_t *pvec) +{ + CPUState *cs = CPU(cpu); + CPUARMState *env = &cpu->env; + MemTxResult result; + uint32_t addr = env->v7m.vecbase[targets_secure] + exc * 4; + uint32_t vector_entry; + MemTxAttrs attrs = {}; + ARMMMUIdx mmu_idx; + bool exc_secure; + + mmu_idx = arm_v7m_mmu_idx_for_secstate_and_priv(env, targets_secure, true); + + /* + * We don't do a get_phys_addr() here because the rules for vector + * loads are special: they always use the default memory map, and + * the default memory map permits reads from all addresses. + * Since there's no easy way to pass through to pmsav8_mpu_lookup() + * that we want this special case which would always say "yes", + * we just do the SAU lookup here followed by a direct physical load. + */ + attrs.secure = targets_secure; + attrs.user = false; + + if (arm_feature(env, ARM_FEATURE_M_SECURITY)) { + V8M_SAttributes sattrs = {}; + + v8m_security_lookup(env, addr, MMU_DATA_LOAD, mmu_idx, &sattrs); + if (sattrs.ns) { + attrs.secure = false; + } else if (!targets_secure) { + /* NS access to S memory */ + goto load_fail; + } + } + + vector_entry = address_space_ldl(arm_addressspace(cs, attrs), addr, + attrs, &result); + if (result != MEMTX_OK) { + goto load_fail; + } + *pvec = vector_entry; + return true; + +load_fail: + /* + * All vector table fetch fails are reported as HardFault, with + * HFSR.VECTTBL and .FORCED set. (FORCED is set because + * technically the underlying exception is a MemManage or BusFault + * that is escalated to HardFault.) This is a terminal exception, + * so we will either take the HardFault immediately or else enter + * lockup (the latter case is handled in armv7m_nvic_set_pending_derived()). + */ + exc_secure = targets_secure || + !(cpu->env.v7m.aircr & R_V7M_AIRCR_BFHFNMINS_MASK); + env->v7m.hfsr |= R_V7M_HFSR_VECTTBL_MASK | R_V7M_HFSR_FORCED_MASK; + armv7m_nvic_set_pending_derived(env->nvic, ARMV7M_EXCP_HARD, exc_secure); + return false; +} + +static uint32_t v7m_integrity_sig(CPUARMState *env, uint32_t lr) +{ + /* + * Return the integrity signature value for the callee-saves + * stack frame section. @lr is the exception return payload/LR value + * whose FType bit forms bit 0 of the signature if FP is present. + */ + uint32_t sig = 0xfefa125a; + + if (!arm_feature(env, ARM_FEATURE_VFP) || (lr & R_V7M_EXCRET_FTYPE_MASK)) { + sig |= 1; + } + return sig; +} + +static bool v7m_push_callee_stack(ARMCPU *cpu, uint32_t lr, bool dotailchain, + bool ignore_faults) +{ + /* + * For v8M, push the callee-saves register part of the stack frame. + * Compare the v8M pseudocode PushCalleeStack(). + * In the tailchaining case this may not be the current stack. + */ + CPUARMState *env = &cpu->env; + uint32_t *frame_sp_p; + uint32_t frameptr; + ARMMMUIdx mmu_idx; + bool stacked_ok; + uint32_t limit; + bool want_psp; + uint32_t sig; + StackingMode smode = ignore_faults ? STACK_IGNFAULTS : STACK_NORMAL; + + if (dotailchain) { + bool mode = lr & R_V7M_EXCRET_MODE_MASK; + bool priv = !(env->v7m.control[M_REG_S] & R_V7M_CONTROL_NPRIV_MASK) || + !mode; + + mmu_idx = arm_v7m_mmu_idx_for_secstate_and_priv(env, M_REG_S, priv); + frame_sp_p = get_v7m_sp_ptr(env, M_REG_S, mode, + lr & R_V7M_EXCRET_SPSEL_MASK); + want_psp = mode && (lr & R_V7M_EXCRET_SPSEL_MASK); + if (want_psp) { + limit = env->v7m.psplim[M_REG_S]; + } else { + limit = env->v7m.msplim[M_REG_S]; + } + } else { + mmu_idx = arm_mmu_idx(env); + frame_sp_p = &env->regs[13]; + limit = v7m_sp_limit(env); + } + + frameptr = *frame_sp_p - 0x28; + if (frameptr < limit) { + /* + * Stack limit failure: set SP to the limit value, and generate + * STKOF UsageFault. Stack pushes below the limit must not be + * performed. It is IMPDEF whether pushes above the limit are + * performed; we choose not to. + */ + qemu_log_mask(CPU_LOG_INT, + "...STKOF during callee-saves register stacking\n"); + env->v7m.cfsr[env->v7m.secure] |= R_V7M_CFSR_STKOF_MASK; + armv7m_nvic_set_pending(env->nvic, ARMV7M_EXCP_USAGE, + env->v7m.secure); + *frame_sp_p = limit; + return true; + } + + /* + * Write as much of the stack frame as we can. A write failure may + * cause us to pend a derived exception. + */ + sig = v7m_integrity_sig(env, lr); + stacked_ok = + v7m_stack_write(cpu, frameptr, sig, mmu_idx, smode) && + v7m_stack_write(cpu, frameptr + 0x8, env->regs[4], mmu_idx, smode) && + v7m_stack_write(cpu, frameptr + 0xc, env->regs[5], mmu_idx, smode) && + v7m_stack_write(cpu, frameptr + 0x10, env->regs[6], mmu_idx, smode) && + v7m_stack_write(cpu, frameptr + 0x14, env->regs[7], mmu_idx, smode) && + v7m_stack_write(cpu, frameptr + 0x18, env->regs[8], mmu_idx, smode) && + v7m_stack_write(cpu, frameptr + 0x1c, env->regs[9], mmu_idx, smode) && + v7m_stack_write(cpu, frameptr + 0x20, env->regs[10], mmu_idx, smode) && + v7m_stack_write(cpu, frameptr + 0x24, env->regs[11], mmu_idx, smode); + + /* Update SP regardless of whether any of the stack accesses failed. */ + *frame_sp_p = frameptr; + + return !stacked_ok; +} + +static void v7m_exception_taken(ARMCPU *cpu, uint32_t lr, bool dotailchain, + bool ignore_stackfaults) +{ + /* + * Do the "take the exception" parts of exception entry, + * but not the pushing of state to the stack. This is + * similar to the pseudocode ExceptionTaken() function. + */ + CPUARMState *env = &cpu->env; + uint32_t addr; + bool targets_secure; + int exc; + bool push_failed = false; + + armv7m_nvic_get_pending_irq_info(env->nvic, &exc, &targets_secure); + qemu_log_mask(CPU_LOG_INT, "...taking pending %s exception %d\n", + targets_secure ? "secure" : "nonsecure", exc); + + if (dotailchain) { + /* Sanitize LR FType and PREFIX bits */ + if (!arm_feature(env, ARM_FEATURE_VFP)) { + lr |= R_V7M_EXCRET_FTYPE_MASK; + } + lr = deposit32(lr, 24, 8, 0xff); + } + + if (arm_feature(env, ARM_FEATURE_V8)) { + if (arm_feature(env, ARM_FEATURE_M_SECURITY) && + (lr & R_V7M_EXCRET_S_MASK)) { + /* + * The background code (the owner of the registers in the + * exception frame) is Secure. This means it may either already + * have or now needs to push callee-saves registers. + */ + if (targets_secure) { + if (dotailchain && !(lr & R_V7M_EXCRET_ES_MASK)) { + /* + * We took an exception from Secure to NonSecure + * (which means the callee-saved registers got stacked) + * and are now tailchaining to a Secure exception. + * Clear DCRS so eventual return from this Secure + * exception unstacks the callee-saved registers. + */ + lr &= ~R_V7M_EXCRET_DCRS_MASK; + } + } else { + /* + * We're going to a non-secure exception; push the + * callee-saves registers to the stack now, if they're + * not already saved. + */ + if (lr & R_V7M_EXCRET_DCRS_MASK && + !(dotailchain && !(lr & R_V7M_EXCRET_ES_MASK))) { + push_failed = v7m_push_callee_stack(cpu, lr, dotailchain, + ignore_stackfaults); + } + lr |= R_V7M_EXCRET_DCRS_MASK; + } + } + + lr &= ~R_V7M_EXCRET_ES_MASK; + if (targets_secure || !arm_feature(env, ARM_FEATURE_M_SECURITY)) { + lr |= R_V7M_EXCRET_ES_MASK; + } + lr &= ~R_V7M_EXCRET_SPSEL_MASK; + if (env->v7m.control[targets_secure] & R_V7M_CONTROL_SPSEL_MASK) { + lr |= R_V7M_EXCRET_SPSEL_MASK; + } + + /* + * Clear registers if necessary to prevent non-secure exception + * code being able to see register values from secure code. + * Where register values become architecturally UNKNOWN we leave + * them with their previous values. + */ + if (arm_feature(env, ARM_FEATURE_M_SECURITY)) { + if (!targets_secure) { + /* + * Always clear the caller-saved registers (they have been + * pushed to the stack earlier in v7m_push_stack()). + * Clear callee-saved registers if the background code is + * Secure (in which case these regs were saved in + * v7m_push_callee_stack()). + */ + int i; + + for (i = 0; i < 13; i++) { + /* r4..r11 are callee-saves, zero only if EXCRET.S == 1 */ + if (i < 4 || i > 11 || (lr & R_V7M_EXCRET_S_MASK)) { + env->regs[i] = 0; + } + } + /* Clear EAPSR */ + xpsr_write(env, 0, XPSR_NZCV | XPSR_Q | XPSR_GE | XPSR_IT); + } + } + } + + if (push_failed && !ignore_stackfaults) { + /* + * Derived exception on callee-saves register stacking: + * we might now want to take a different exception which + * targets a different security state, so try again from the top. + */ + qemu_log_mask(CPU_LOG_INT, + "...derived exception on callee-saves register stacking"); + v7m_exception_taken(cpu, lr, true, true); + return; + } + + if (!arm_v7m_load_vector(cpu, exc, targets_secure, &addr)) { + /* Vector load failed: derived exception */ + qemu_log_mask(CPU_LOG_INT, "...derived exception on vector table load"); + v7m_exception_taken(cpu, lr, true, true); + return; + } + + /* + * Now we've done everything that might cause a derived exception + * we can go ahead and activate whichever exception we're going to + * take (which might now be the derived exception). + */ + armv7m_nvic_acknowledge_irq(env->nvic); + + /* Switch to target security state -- must do this before writing SPSEL */ + switch_v7m_security_state(env, targets_secure); + write_v7m_control_spsel(env, 0); + arm_clear_exclusive(env); + /* Clear SFPA and FPCA (has no effect if no FPU) */ + env->v7m.control[M_REG_S] &= + ~(R_V7M_CONTROL_FPCA_MASK | R_V7M_CONTROL_SFPA_MASK); + /* Clear IT bits */ + env->condexec_bits = 0; + env->regs[14] = lr; + env->regs[15] = addr & 0xfffffffe; + env->thumb = addr & 1; +} + +static void v7m_update_fpccr(CPUARMState *env, uint32_t frameptr, + bool apply_splim) +{ + /* + * Like the pseudocode UpdateFPCCR: save state in FPCAR and FPCCR + * that we will need later in order to do lazy FP reg stacking. + */ + bool is_secure = env->v7m.secure; + void *nvic = env->nvic; + /* + * Some bits are unbanked and live always in fpccr[M_REG_S]; some bits + * are banked and we want to update the bit in the bank for the + * current security state; and in one case we want to specifically + * update the NS banked version of a bit even if we are secure. + */ + uint32_t *fpccr_s = &env->v7m.fpccr[M_REG_S]; + uint32_t *fpccr_ns = &env->v7m.fpccr[M_REG_NS]; + uint32_t *fpccr = &env->v7m.fpccr[is_secure]; + bool hfrdy, bfrdy, mmrdy, ns_ufrdy, s_ufrdy, sfrdy, monrdy; + + env->v7m.fpcar[is_secure] = frameptr & ~0x7; + + if (apply_splim && arm_feature(env, ARM_FEATURE_V8)) { + bool splimviol; + uint32_t splim = v7m_sp_limit(env); + bool ign = armv7m_nvic_neg_prio_requested(nvic, is_secure) && + (env->v7m.ccr[is_secure] & R_V7M_CCR_STKOFHFNMIGN_MASK); + + splimviol = !ign && frameptr < splim; + *fpccr = FIELD_DP32(*fpccr, V7M_FPCCR, SPLIMVIOL, splimviol); + } + + *fpccr = FIELD_DP32(*fpccr, V7M_FPCCR, LSPACT, 1); + + *fpccr_s = FIELD_DP32(*fpccr_s, V7M_FPCCR, S, is_secure); + + *fpccr = FIELD_DP32(*fpccr, V7M_FPCCR, USER, arm_current_el(env) == 0); + + *fpccr = FIELD_DP32(*fpccr, V7M_FPCCR, THREAD, + !arm_v7m_is_handler_mode(env)); + + hfrdy = armv7m_nvic_get_ready_status(nvic, ARMV7M_EXCP_HARD, false); + *fpccr_s = FIELD_DP32(*fpccr_s, V7M_FPCCR, HFRDY, hfrdy); + + bfrdy = armv7m_nvic_get_ready_status(nvic, ARMV7M_EXCP_BUS, false); + *fpccr_s = FIELD_DP32(*fpccr_s, V7M_FPCCR, BFRDY, bfrdy); + + mmrdy = armv7m_nvic_get_ready_status(nvic, ARMV7M_EXCP_MEM, is_secure); + *fpccr = FIELD_DP32(*fpccr, V7M_FPCCR, MMRDY, mmrdy); + + ns_ufrdy = armv7m_nvic_get_ready_status(nvic, ARMV7M_EXCP_USAGE, false); + *fpccr_ns = FIELD_DP32(*fpccr_ns, V7M_FPCCR, UFRDY, ns_ufrdy); + + monrdy = armv7m_nvic_get_ready_status(nvic, ARMV7M_EXCP_DEBUG, false); + *fpccr_s = FIELD_DP32(*fpccr_s, V7M_FPCCR, MONRDY, monrdy); + + if (arm_feature(env, ARM_FEATURE_M_SECURITY)) { + s_ufrdy = armv7m_nvic_get_ready_status(nvic, ARMV7M_EXCP_USAGE, true); + *fpccr_s = FIELD_DP32(*fpccr_s, V7M_FPCCR, UFRDY, s_ufrdy); + + sfrdy = armv7m_nvic_get_ready_status(nvic, ARMV7M_EXCP_SECURE, false); + *fpccr_s = FIELD_DP32(*fpccr_s, V7M_FPCCR, SFRDY, sfrdy); + } +} + +void HELPER(v7m_vlstm)(CPUARMState *env, uint32_t fptr) +{ + /* fptr is the value of Rn, the frame pointer we store the FP regs to */ + bool s = env->v7m.fpccr[M_REG_S] & R_V7M_FPCCR_S_MASK; + bool lspact = env->v7m.fpccr[s] & R_V7M_FPCCR_LSPACT_MASK; + + assert(env->v7m.secure); + + if (!(env->v7m.control[M_REG_S] & R_V7M_CONTROL_SFPA_MASK)) { + return; + } + + /* Check access to the coprocessor is permitted */ + if (!v7m_cpacr_pass(env, true, arm_current_el(env) != 0)) { + raise_exception_ra(env, EXCP_NOCP, 0, 1, GETPC()); + } + + if (lspact) { + /* LSPACT should not be active when there is active FP state */ + raise_exception_ra(env, EXCP_LSERR, 0, 1, GETPC()); + } + + if (fptr & 7) { + raise_exception_ra(env, EXCP_UNALIGNED, 0, 1, GETPC()); + } + + /* + * Note that we do not use v7m_stack_write() here, because the + * accesses should not set the FSR bits for stacking errors if they + * fail. (In pseudocode terms, they are AccType_NORMAL, not AccType_STACK + * or AccType_LAZYFP). Faults in cpu_stl_data() will throw exceptions + * and longjmp out. + */ + if (!(env->v7m.fpccr[M_REG_S] & R_V7M_FPCCR_LSPEN_MASK)) { + bool ts = env->v7m.fpccr[M_REG_S] & R_V7M_FPCCR_TS_MASK; + int i; + + for (i = 0; i < (ts ? 32 : 16); i += 2) { + uint64_t dn = *aa32_vfp_dreg(env, i / 2); + uint32_t faddr = fptr + 4 * i; + uint32_t slo = extract64(dn, 0, 32); + uint32_t shi = extract64(dn, 32, 32); + + if (i >= 16) { + faddr += 8; /* skip the slot for the FPSCR */ + } + cpu_stl_data(env, faddr, slo); + cpu_stl_data(env, faddr + 4, shi); + } + cpu_stl_data(env, fptr + 0x40, vfp_get_fpscr(env)); + + /* + * If TS is 0 then s0 to s15 and FPSCR are UNKNOWN; we choose to + * leave them unchanged, matching our choice in v7m_preserve_fp_state. + */ + if (ts) { + for (i = 0; i < 32; i += 2) { + *aa32_vfp_dreg(env, i / 2) = 0; + } + vfp_set_fpscr(env, 0); + } + } else { + v7m_update_fpccr(env, fptr, false); + } + + env->v7m.control[M_REG_S] &= ~R_V7M_CONTROL_FPCA_MASK; +} + +void HELPER(v7m_vlldm)(CPUARMState *env, uint32_t fptr) +{ + /* fptr is the value of Rn, the frame pointer we load the FP regs from */ + assert(env->v7m.secure); + + if (!(env->v7m.control[M_REG_S] & R_V7M_CONTROL_SFPA_MASK)) { + return; + } + + /* Check access to the coprocessor is permitted */ + if (!v7m_cpacr_pass(env, true, arm_current_el(env) != 0)) { + raise_exception_ra(env, EXCP_NOCP, 0, 1, GETPC()); + } + + if (env->v7m.fpccr[M_REG_S] & R_V7M_FPCCR_LSPACT_MASK) { + /* State in FP is still valid */ + env->v7m.fpccr[M_REG_S] &= ~R_V7M_FPCCR_LSPACT_MASK; + } else { + bool ts = env->v7m.fpccr[M_REG_S] & R_V7M_FPCCR_TS_MASK; + int i; + uint32_t fpscr; + + if (fptr & 7) { + raise_exception_ra(env, EXCP_UNALIGNED, 0, 1, GETPC()); + } + + for (i = 0; i < (ts ? 32 : 16); i += 2) { + uint32_t slo, shi; + uint64_t dn; + uint32_t faddr = fptr + 4 * i; + + if (i >= 16) { + faddr += 8; /* skip the slot for the FPSCR */ + } + + slo = cpu_ldl_data(env, faddr); + shi = cpu_ldl_data(env, faddr + 4); + + dn = (uint64_t) shi << 32 | slo; + *aa32_vfp_dreg(env, i / 2) = dn; + } + fpscr = cpu_ldl_data(env, fptr + 0x40); + vfp_set_fpscr(env, fpscr); + } + + env->v7m.control[M_REG_S] |= R_V7M_CONTROL_FPCA_MASK; +} + +static bool v7m_push_stack(ARMCPU *cpu) +{ + /* + * Do the "set up stack frame" part of exception entry, + * similar to pseudocode PushStack(). + * Return true if we generate a derived exception (and so + * should ignore further stack faults trying to process + * that derived exception.) + */ + bool stacked_ok = true, limitviol = false; + CPUARMState *env = &cpu->env; + uint32_t xpsr = xpsr_read(env); + uint32_t frameptr = env->regs[13]; + ARMMMUIdx mmu_idx = arm_mmu_idx(env); + uint32_t framesize; + bool nsacr_cp10 = extract32(env->v7m.nsacr, 10, 1); + + if ((env->v7m.control[M_REG_S] & R_V7M_CONTROL_FPCA_MASK) && + (env->v7m.secure || nsacr_cp10)) { + if (env->v7m.secure && + env->v7m.fpccr[M_REG_S] & R_V7M_FPCCR_TS_MASK) { + framesize = 0xa8; + } else { + framesize = 0x68; + } + } else { + framesize = 0x20; + } + + /* Align stack pointer if the guest wants that */ + if ((frameptr & 4) && + (env->v7m.ccr[env->v7m.secure] & R_V7M_CCR_STKALIGN_MASK)) { + frameptr -= 4; + xpsr |= XPSR_SPREALIGN; + } + + xpsr &= ~XPSR_SFPA; + if (env->v7m.secure && + (env->v7m.control[M_REG_S] & R_V7M_CONTROL_SFPA_MASK)) { + xpsr |= XPSR_SFPA; + } + + frameptr -= framesize; + + if (arm_feature(env, ARM_FEATURE_V8)) { + uint32_t limit = v7m_sp_limit(env); + + if (frameptr < limit) { + /* + * Stack limit failure: set SP to the limit value, and generate + * STKOF UsageFault. Stack pushes below the limit must not be + * performed. It is IMPDEF whether pushes above the limit are + * performed; we choose not to. + */ + qemu_log_mask(CPU_LOG_INT, + "...STKOF during stacking\n"); + env->v7m.cfsr[env->v7m.secure] |= R_V7M_CFSR_STKOF_MASK; + armv7m_nvic_set_pending(env->nvic, ARMV7M_EXCP_USAGE, + env->v7m.secure); + env->regs[13] = limit; + /* + * We won't try to perform any further memory accesses but + * we must continue through the following code to check for + * permission faults during FPU state preservation, and we + * must update FPCCR if lazy stacking is enabled. + */ + limitviol = true; + stacked_ok = false; + } + } + + /* + * Write as much of the stack frame as we can. If we fail a stack + * write this will result in a derived exception being pended + * (which may be taken in preference to the one we started with + * if it has higher priority). + */ + stacked_ok = stacked_ok && + v7m_stack_write(cpu, frameptr, env->regs[0], mmu_idx, STACK_NORMAL) && + v7m_stack_write(cpu, frameptr + 4, env->regs[1], + mmu_idx, STACK_NORMAL) && + v7m_stack_write(cpu, frameptr + 8, env->regs[2], + mmu_idx, STACK_NORMAL) && + v7m_stack_write(cpu, frameptr + 12, env->regs[3], + mmu_idx, STACK_NORMAL) && + v7m_stack_write(cpu, frameptr + 16, env->regs[12], + mmu_idx, STACK_NORMAL) && + v7m_stack_write(cpu, frameptr + 20, env->regs[14], + mmu_idx, STACK_NORMAL) && + v7m_stack_write(cpu, frameptr + 24, env->regs[15], + mmu_idx, STACK_NORMAL) && + v7m_stack_write(cpu, frameptr + 28, xpsr, mmu_idx, STACK_NORMAL); + + if (env->v7m.control[M_REG_S] & R_V7M_CONTROL_FPCA_MASK) { + /* FPU is active, try to save its registers */ + bool fpccr_s = env->v7m.fpccr[M_REG_S] & R_V7M_FPCCR_S_MASK; + bool lspact = env->v7m.fpccr[fpccr_s] & R_V7M_FPCCR_LSPACT_MASK; + + if (lspact && arm_feature(env, ARM_FEATURE_M_SECURITY)) { + qemu_log_mask(CPU_LOG_INT, + "...SecureFault because LSPACT and FPCA both set\n"); + env->v7m.sfsr |= R_V7M_SFSR_LSERR_MASK; + armv7m_nvic_set_pending(env->nvic, ARMV7M_EXCP_SECURE, false); + } else if (!env->v7m.secure && !nsacr_cp10) { + qemu_log_mask(CPU_LOG_INT, + "...Secure UsageFault with CFSR.NOCP because " + "NSACR.CP10 prevents stacking FP regs\n"); + armv7m_nvic_set_pending(env->nvic, ARMV7M_EXCP_USAGE, M_REG_S); + env->v7m.cfsr[M_REG_S] |= R_V7M_CFSR_NOCP_MASK; + } else { + if (!(env->v7m.fpccr[M_REG_S] & R_V7M_FPCCR_LSPEN_MASK)) { + /* Lazy stacking disabled, save registers now */ + int i; + bool cpacr_pass = v7m_cpacr_pass(env, env->v7m.secure, + arm_current_el(env) != 0); + + if (stacked_ok && !cpacr_pass) { + /* + * Take UsageFault if CPACR forbids access. The pseudocode + * here does a full CheckCPEnabled() but we know the NSACR + * check can never fail as we have already handled that. + */ + qemu_log_mask(CPU_LOG_INT, + "...UsageFault with CFSR.NOCP because " + "CPACR.CP10 prevents stacking FP regs\n"); + armv7m_nvic_set_pending(env->nvic, ARMV7M_EXCP_USAGE, + env->v7m.secure); + env->v7m.cfsr[env->v7m.secure] |= R_V7M_CFSR_NOCP_MASK; + stacked_ok = false; + } + + for (i = 0; i < ((framesize == 0xa8) ? 32 : 16); i += 2) { + uint64_t dn = *aa32_vfp_dreg(env, i / 2); + uint32_t faddr = frameptr + 0x20 + 4 * i; + uint32_t slo = extract64(dn, 0, 32); + uint32_t shi = extract64(dn, 32, 32); + + if (i >= 16) { + faddr += 8; /* skip the slot for the FPSCR */ + } + stacked_ok = stacked_ok && + v7m_stack_write(cpu, faddr, slo, + mmu_idx, STACK_NORMAL) && + v7m_stack_write(cpu, faddr + 4, shi, + mmu_idx, STACK_NORMAL); + } + stacked_ok = stacked_ok && + v7m_stack_write(cpu, frameptr + 0x60, + vfp_get_fpscr(env), mmu_idx, STACK_NORMAL); + if (cpacr_pass) { + for (i = 0; i < ((framesize == 0xa8) ? 32 : 16); i += 2) { + *aa32_vfp_dreg(env, i / 2) = 0; + } + vfp_set_fpscr(env, 0); + } + } else { + /* Lazy stacking enabled, save necessary info to stack later */ + v7m_update_fpccr(env, frameptr + 0x20, true); + } + } + } + + /* + * If we broke a stack limit then SP was already updated earlier; + * otherwise we update SP regardless of whether any of the stack + * accesses failed or we took some other kind of fault. + */ + if (!limitviol) { + env->regs[13] = frameptr; + } + + return !stacked_ok; +} + +static void do_v7m_exception_exit(ARMCPU *cpu) +{ + CPUARMState *env = &cpu->env; + uint32_t excret; + uint32_t xpsr, xpsr_mask; + bool ufault = false; + bool sfault = false; + bool return_to_sp_process; + bool return_to_handler; + bool rettobase = false; + bool exc_secure = false; + bool return_to_secure; + bool ftype; + bool restore_s16_s31; + + /* + * If we're not in Handler mode then jumps to magic exception-exit + * addresses don't have magic behaviour. However for the v8M + * security extensions the magic secure-function-return has to + * work in thread mode too, so to avoid doing an extra check in + * the generated code we allow exception-exit magic to also cause the + * internal exception and bring us here in thread mode. Correct code + * will never try to do this (the following insn fetch will always + * fault) so we the overhead of having taken an unnecessary exception + * doesn't matter. + */ + if (!arm_v7m_is_handler_mode(env)) { + return; + } + + /* + * In the spec pseudocode ExceptionReturn() is called directly + * from BXWritePC() and gets the full target PC value including + * bit zero. In QEMU's implementation we treat it as a normal + * jump-to-register (which is then caught later on), and so split + * the target value up between env->regs[15] and env->thumb in + * gen_bx(). Reconstitute it. + */ + excret = env->regs[15]; + if (env->thumb) { + excret |= 1; + } + + qemu_log_mask(CPU_LOG_INT, "Exception return: magic PC %" PRIx32 + " previous exception %d\n", + excret, env->v7m.exception); + + if ((excret & R_V7M_EXCRET_RES1_MASK) != R_V7M_EXCRET_RES1_MASK) { + qemu_log_mask(LOG_GUEST_ERROR, "M profile: zero high bits in exception " + "exit PC value 0x%" PRIx32 " are UNPREDICTABLE\n", + excret); + } + + ftype = excret & R_V7M_EXCRET_FTYPE_MASK; + + if (!arm_feature(env, ARM_FEATURE_VFP) && !ftype) { + qemu_log_mask(LOG_GUEST_ERROR, "M profile: zero FTYPE in exception " + "exit PC value 0x%" PRIx32 " is UNPREDICTABLE " + "if FPU not present\n", + excret); + ftype = true; + } + + if (arm_feature(env, ARM_FEATURE_M_SECURITY)) { + /* + * EXC_RETURN.ES validation check (R_SMFL). We must do this before + * we pick which FAULTMASK to clear. + */ + if (!env->v7m.secure && + ((excret & R_V7M_EXCRET_ES_MASK) || + !(excret & R_V7M_EXCRET_DCRS_MASK))) { + sfault = 1; + /* For all other purposes, treat ES as 0 (R_HXSR) */ + excret &= ~R_V7M_EXCRET_ES_MASK; + } + exc_secure = excret & R_V7M_EXCRET_ES_MASK; + } + + if (env->v7m.exception != ARMV7M_EXCP_NMI) { + /* + * Auto-clear FAULTMASK on return from other than NMI. + * If the security extension is implemented then this only + * happens if the raw execution priority is >= 0; the + * value of the ES bit in the exception return value indicates + * which security state's faultmask to clear. (v8M ARM ARM R_KBNF.) + */ + if (arm_feature(env, ARM_FEATURE_M_SECURITY)) { + if (armv7m_nvic_raw_execution_priority(env->nvic) >= 0) { + env->v7m.faultmask[exc_secure] = 0; + } + } else { + env->v7m.faultmask[M_REG_NS] = 0; + } + } + + switch (armv7m_nvic_complete_irq(env->nvic, env->v7m.exception, + exc_secure)) { + case -1: + /* attempt to exit an exception that isn't active */ + ufault = true; + break; + case 0: + /* still an irq active now */ + break; + case 1: + /* + * We returned to base exception level, no nesting. + * (In the pseudocode this is written using "NestedActivation != 1" + * where we have 'rettobase == false'.) + */ + rettobase = true; + break; + default: + g_assert_not_reached(); + } + + return_to_handler = !(excret & R_V7M_EXCRET_MODE_MASK); + return_to_sp_process = excret & R_V7M_EXCRET_SPSEL_MASK; + return_to_secure = arm_feature(env, ARM_FEATURE_M_SECURITY) && + (excret & R_V7M_EXCRET_S_MASK); + + if (arm_feature(env, ARM_FEATURE_V8)) { + if (!arm_feature(env, ARM_FEATURE_M_SECURITY)) { + /* + * UNPREDICTABLE if S == 1 or DCRS == 0 or ES == 1 (R_XLCP); + * we choose to take the UsageFault. + */ + if ((excret & R_V7M_EXCRET_S_MASK) || + (excret & R_V7M_EXCRET_ES_MASK) || + !(excret & R_V7M_EXCRET_DCRS_MASK)) { + ufault = true; + } + } + if (excret & R_V7M_EXCRET_RES0_MASK) { + ufault = true; + } + } else { + /* For v7M we only recognize certain combinations of the low bits */ + switch (excret & 0xf) { + case 1: /* Return to Handler */ + break; + case 13: /* Return to Thread using Process stack */ + case 9: /* Return to Thread using Main stack */ + /* + * We only need to check NONBASETHRDENA for v7M, because in + * v8M this bit does not exist (it is RES1). + */ + if (!rettobase && + !(env->v7m.ccr[env->v7m.secure] & + R_V7M_CCR_NONBASETHRDENA_MASK)) { + ufault = true; + } + break; + default: + ufault = true; + } + } + + /* + * Set CONTROL.SPSEL from excret.SPSEL. Since we're still in + * Handler mode (and will be until we write the new XPSR.Interrupt + * field) this does not switch around the current stack pointer. + * We must do this before we do any kind of tailchaining, including + * for the derived exceptions on integrity check failures, or we will + * give the guest an incorrect EXCRET.SPSEL value on exception entry. + */ + write_v7m_control_spsel_for_secstate(env, return_to_sp_process, exc_secure); + + /* + * Clear scratch FP values left in caller saved registers; this + * must happen before any kind of tail chaining. + */ + if ((env->v7m.fpccr[M_REG_S] & R_V7M_FPCCR_CLRONRET_MASK) && + (env->v7m.control[M_REG_S] & R_V7M_CONTROL_FPCA_MASK)) { + if (env->v7m.fpccr[M_REG_S] & R_V7M_FPCCR_LSPACT_MASK) { + env->v7m.sfsr |= R_V7M_SFSR_LSERR_MASK; + armv7m_nvic_set_pending(env->nvic, ARMV7M_EXCP_SECURE, false); + qemu_log_mask(CPU_LOG_INT, "...taking SecureFault on existing " + "stackframe: error during lazy state deactivation\n"); + v7m_exception_taken(cpu, excret, true, false); + return; + } else { + /* Clear s0..s15 and FPSCR */ + int i; + + for (i = 0; i < 16; i += 2) { + *aa32_vfp_dreg(env, i / 2) = 0; + } + vfp_set_fpscr(env, 0); + } + } + + if (sfault) { + env->v7m.sfsr |= R_V7M_SFSR_INVER_MASK; + armv7m_nvic_set_pending(env->nvic, ARMV7M_EXCP_SECURE, false); + qemu_log_mask(CPU_LOG_INT, "...taking SecureFault on existing " + "stackframe: failed EXC_RETURN.ES validity check\n"); + v7m_exception_taken(cpu, excret, true, false); + return; + } + + if (ufault) { + /* + * Bad exception return: instead of popping the exception + * stack, directly take a usage fault on the current stack. + */ + env->v7m.cfsr[env->v7m.secure] |= R_V7M_CFSR_INVPC_MASK; + armv7m_nvic_set_pending(env->nvic, ARMV7M_EXCP_USAGE, env->v7m.secure); + qemu_log_mask(CPU_LOG_INT, "...taking UsageFault on existing " + "stackframe: failed exception return integrity check\n"); + v7m_exception_taken(cpu, excret, true, false); + return; + } + + /* + * Tailchaining: if there is currently a pending exception that + * is high enough priority to preempt execution at the level we're + * about to return to, then just directly take that exception now, + * avoiding an unstack-and-then-stack. Note that now we have + * deactivated the previous exception by calling armv7m_nvic_complete_irq() + * our current execution priority is already the execution priority we are + * returning to -- none of the state we would unstack or set based on + * the EXCRET value affects it. + */ + if (armv7m_nvic_can_take_pending_exception(env->nvic)) { + qemu_log_mask(CPU_LOG_INT, "...tailchaining to pending exception\n"); + v7m_exception_taken(cpu, excret, true, false); + return; + } + + switch_v7m_security_state(env, return_to_secure); + + { + /* + * The stack pointer we should be reading the exception frame from + * depends on bits in the magic exception return type value (and + * for v8M isn't necessarily the stack pointer we will eventually + * end up resuming execution with). Get a pointer to the location + * in the CPU state struct where the SP we need is currently being + * stored; we will use and modify it in place. + * We use this limited C variable scope so we don't accidentally + * use 'frame_sp_p' after we do something that makes it invalid. + */ + uint32_t *frame_sp_p = get_v7m_sp_ptr(env, + return_to_secure, + !return_to_handler, + return_to_sp_process); + uint32_t frameptr = *frame_sp_p; + bool pop_ok = true; + ARMMMUIdx mmu_idx; + bool return_to_priv = return_to_handler || + !(env->v7m.control[return_to_secure] & R_V7M_CONTROL_NPRIV_MASK); + + mmu_idx = arm_v7m_mmu_idx_for_secstate_and_priv(env, return_to_secure, + return_to_priv); + + if (!QEMU_IS_ALIGNED(frameptr, 8) && + arm_feature(env, ARM_FEATURE_V8)) { + qemu_log_mask(LOG_GUEST_ERROR, + "M profile exception return with non-8-aligned SP " + "for destination state is UNPREDICTABLE\n"); + } + + /* Do we need to pop callee-saved registers? */ + if (return_to_secure && + ((excret & R_V7M_EXCRET_ES_MASK) == 0 || + (excret & R_V7M_EXCRET_DCRS_MASK) == 0)) { + uint32_t actual_sig; + + pop_ok = v7m_stack_read(cpu, &actual_sig, frameptr, mmu_idx); + + if (pop_ok && v7m_integrity_sig(env, excret) != actual_sig) { + /* Take a SecureFault on the current stack */ + env->v7m.sfsr |= R_V7M_SFSR_INVIS_MASK; + armv7m_nvic_set_pending(env->nvic, ARMV7M_EXCP_SECURE, false); + qemu_log_mask(CPU_LOG_INT, "...taking SecureFault on existing " + "stackframe: failed exception return integrity " + "signature check\n"); + v7m_exception_taken(cpu, excret, true, false); + return; + } + + pop_ok = pop_ok && + v7m_stack_read(cpu, &env->regs[4], frameptr + 0x8, mmu_idx) && + v7m_stack_read(cpu, &env->regs[5], frameptr + 0xc, mmu_idx) && + v7m_stack_read(cpu, &env->regs[6], frameptr + 0x10, mmu_idx) && + v7m_stack_read(cpu, &env->regs[7], frameptr + 0x14, mmu_idx) && + v7m_stack_read(cpu, &env->regs[8], frameptr + 0x18, mmu_idx) && + v7m_stack_read(cpu, &env->regs[9], frameptr + 0x1c, mmu_idx) && + v7m_stack_read(cpu, &env->regs[10], frameptr + 0x20, mmu_idx) && + v7m_stack_read(cpu, &env->regs[11], frameptr + 0x24, mmu_idx); + + frameptr += 0x28; + } + + /* Pop registers */ + pop_ok = pop_ok && + v7m_stack_read(cpu, &env->regs[0], frameptr, mmu_idx) && + v7m_stack_read(cpu, &env->regs[1], frameptr + 0x4, mmu_idx) && + v7m_stack_read(cpu, &env->regs[2], frameptr + 0x8, mmu_idx) && + v7m_stack_read(cpu, &env->regs[3], frameptr + 0xc, mmu_idx) && + v7m_stack_read(cpu, &env->regs[12], frameptr + 0x10, mmu_idx) && + v7m_stack_read(cpu, &env->regs[14], frameptr + 0x14, mmu_idx) && + v7m_stack_read(cpu, &env->regs[15], frameptr + 0x18, mmu_idx) && + v7m_stack_read(cpu, &xpsr, frameptr + 0x1c, mmu_idx); + + if (!pop_ok) { + /* + * v7m_stack_read() pended a fault, so take it (as a tail + * chained exception on the same stack frame) + */ + qemu_log_mask(CPU_LOG_INT, "...derived exception on unstacking\n"); + v7m_exception_taken(cpu, excret, true, false); + return; + } + + /* + * Returning from an exception with a PC with bit 0 set is defined + * behaviour on v8M (bit 0 is ignored), but for v7M it was specified + * to be UNPREDICTABLE. In practice actual v7M hardware seems to ignore + * the lsbit, and there are several RTOSes out there which incorrectly + * assume the r15 in the stack frame should be a Thumb-style "lsbit + * indicates ARM/Thumb" value, so ignore the bit on v7M as well, but + * complain about the badly behaved guest. + */ + if (env->regs[15] & 1) { + env->regs[15] &= ~1U; + if (!arm_feature(env, ARM_FEATURE_V8)) { + qemu_log_mask(LOG_GUEST_ERROR, + "M profile return from interrupt with misaligned " + "PC is UNPREDICTABLE on v7M\n"); + } + } + + if (arm_feature(env, ARM_FEATURE_V8)) { + /* + * For v8M we have to check whether the xPSR exception field + * matches the EXCRET value for return to handler/thread + * before we commit to changing the SP and xPSR. + */ + bool will_be_handler = (xpsr & XPSR_EXCP) != 0; + if (return_to_handler != will_be_handler) { + /* + * Take an INVPC UsageFault on the current stack. + * By this point we will have switched to the security state + * for the background state, so this UsageFault will target + * that state. + */ + armv7m_nvic_set_pending(env->nvic, ARMV7M_EXCP_USAGE, + env->v7m.secure); + env->v7m.cfsr[env->v7m.secure] |= R_V7M_CFSR_INVPC_MASK; + qemu_log_mask(CPU_LOG_INT, "...taking UsageFault on existing " + "stackframe: failed exception return integrity " + "check\n"); + v7m_exception_taken(cpu, excret, true, false); + return; + } + } + + if (!ftype) { + /* FP present and we need to handle it */ + if (!return_to_secure && + (env->v7m.fpccr[M_REG_S] & R_V7M_FPCCR_LSPACT_MASK)) { + armv7m_nvic_set_pending(env->nvic, ARMV7M_EXCP_SECURE, false); + env->v7m.sfsr |= R_V7M_SFSR_LSERR_MASK; + qemu_log_mask(CPU_LOG_INT, + "...taking SecureFault on existing stackframe: " + "Secure LSPACT set but exception return is " + "not to secure state\n"); + v7m_exception_taken(cpu, excret, true, false); + return; + } + + restore_s16_s31 = return_to_secure && + (env->v7m.fpccr[M_REG_S] & R_V7M_FPCCR_TS_MASK); + + if (env->v7m.fpccr[return_to_secure] & R_V7M_FPCCR_LSPACT_MASK) { + /* State in FPU is still valid, just clear LSPACT */ + env->v7m.fpccr[return_to_secure] &= ~R_V7M_FPCCR_LSPACT_MASK; + } else { + int i; + uint32_t fpscr; + bool cpacr_pass, nsacr_pass; + + cpacr_pass = v7m_cpacr_pass(env, return_to_secure, + return_to_priv); + nsacr_pass = return_to_secure || + extract32(env->v7m.nsacr, 10, 1); + + if (!cpacr_pass) { + armv7m_nvic_set_pending(env->nvic, ARMV7M_EXCP_USAGE, + return_to_secure); + env->v7m.cfsr[return_to_secure] |= R_V7M_CFSR_NOCP_MASK; + qemu_log_mask(CPU_LOG_INT, + "...taking UsageFault on existing " + "stackframe: CPACR.CP10 prevents unstacking " + "FP regs\n"); + v7m_exception_taken(cpu, excret, true, false); + return; + } else if (!nsacr_pass) { + armv7m_nvic_set_pending(env->nvic, ARMV7M_EXCP_USAGE, true); + env->v7m.cfsr[M_REG_S] |= R_V7M_CFSR_INVPC_MASK; + qemu_log_mask(CPU_LOG_INT, + "...taking Secure UsageFault on existing " + "stackframe: NSACR.CP10 prevents unstacking " + "FP regs\n"); + v7m_exception_taken(cpu, excret, true, false); + return; + } + + for (i = 0; i < (restore_s16_s31 ? 32 : 16); i += 2) { + uint32_t slo, shi; + uint64_t dn; + uint32_t faddr = frameptr + 0x20 + 4 * i; + + if (i >= 16) { + faddr += 8; /* Skip the slot for the FPSCR */ + } + + pop_ok = pop_ok && + v7m_stack_read(cpu, &slo, faddr, mmu_idx) && + v7m_stack_read(cpu, &shi, faddr + 4, mmu_idx); + + if (!pop_ok) { + break; + } + + dn = (uint64_t)shi << 32 | slo; + *aa32_vfp_dreg(env, i / 2) = dn; + } + pop_ok = pop_ok && + v7m_stack_read(cpu, &fpscr, frameptr + 0x60, mmu_idx); + if (pop_ok) { + vfp_set_fpscr(env, fpscr); + } + if (!pop_ok) { + /* + * These regs are 0 if security extension present; + * otherwise merely UNKNOWN. We zero always. + */ + for (i = 0; i < (restore_s16_s31 ? 32 : 16); i += 2) { + *aa32_vfp_dreg(env, i / 2) = 0; + } + vfp_set_fpscr(env, 0); + } + } + } + env->v7m.control[M_REG_S] = FIELD_DP32(env->v7m.control[M_REG_S], + V7M_CONTROL, FPCA, !ftype); + + /* Commit to consuming the stack frame */ + frameptr += 0x20; + if (!ftype) { + frameptr += 0x48; + if (restore_s16_s31) { + frameptr += 0x40; + } + } + /* + * Undo stack alignment (the SPREALIGN bit indicates that the original + * pre-exception SP was not 8-aligned and we added a padding word to + * align it, so we undo this by ORing in the bit that increases it + * from the current 8-aligned value to the 8-unaligned value. (Adding 4 + * would work too but a logical OR is how the pseudocode specifies it.) + */ + if (xpsr & XPSR_SPREALIGN) { + frameptr |= 4; + } + *frame_sp_p = frameptr; + } + + xpsr_mask = ~(XPSR_SPREALIGN | XPSR_SFPA); + if (!arm_feature(env, ARM_FEATURE_THUMB_DSP)) { + xpsr_mask &= ~XPSR_GE; + } + /* This xpsr_write() will invalidate frame_sp_p as it may switch stack */ + xpsr_write(env, xpsr, xpsr_mask); + + if (env->v7m.secure) { + bool sfpa = xpsr & XPSR_SFPA; + + env->v7m.control[M_REG_S] = FIELD_DP32(env->v7m.control[M_REG_S], + V7M_CONTROL, SFPA, sfpa); + } + + /* + * The restored xPSR exception field will be zero if we're + * resuming in Thread mode. If that doesn't match what the + * exception return excret specified then this is a UsageFault. + * v7M requires we make this check here; v8M did it earlier. + */ + if (return_to_handler != arm_v7m_is_handler_mode(env)) { + /* + * Take an INVPC UsageFault by pushing the stack again; + * we know we're v7M so this is never a Secure UsageFault. + */ + bool ignore_stackfaults; + + assert(!arm_feature(env, ARM_FEATURE_V8)); + armv7m_nvic_set_pending(env->nvic, ARMV7M_EXCP_USAGE, false); + env->v7m.cfsr[env->v7m.secure] |= R_V7M_CFSR_INVPC_MASK; + ignore_stackfaults = v7m_push_stack(cpu); + qemu_log_mask(CPU_LOG_INT, "...taking UsageFault on new stackframe: " + "failed exception return integrity check\n"); + v7m_exception_taken(cpu, excret, false, ignore_stackfaults); + return; + } + + /* Otherwise, we have a successful exception exit. */ + arm_clear_exclusive(env); + qemu_log_mask(CPU_LOG_INT, "...successful exception return\n"); +} + +static bool do_v7m_function_return(ARMCPU *cpu) +{ + /* + * v8M security extensions magic function return. + * We may either: + * (1) throw an exception (longjump) + * (2) return true if we successfully handled the function return + * (3) return false if we failed a consistency check and have + * pended a UsageFault that needs to be taken now + * + * At this point the magic return value is split between env->regs[15] + * and env->thumb. We don't bother to reconstitute it because we don't + * need it (all values are handled the same way). + */ + CPUARMState *env = &cpu->env; + uint32_t newpc, newpsr, newpsr_exc; + + qemu_log_mask(CPU_LOG_INT, "...really v7M secure function return\n"); + + { + bool threadmode, spsel; + TCGMemOpIdx oi; + ARMMMUIdx mmu_idx; + uint32_t *frame_sp_p; + uint32_t frameptr; + + /* Pull the return address and IPSR from the Secure stack */ + threadmode = !arm_v7m_is_handler_mode(env); + spsel = env->v7m.control[M_REG_S] & R_V7M_CONTROL_SPSEL_MASK; + + frame_sp_p = get_v7m_sp_ptr(env, true, threadmode, spsel); + frameptr = *frame_sp_p; + + /* + * These loads may throw an exception (for MPU faults). We want to + * do them as secure, so work out what MMU index that is. + */ + mmu_idx = arm_v7m_mmu_idx_for_secstate(env, true); + oi = make_memop_idx(MO_LE, arm_to_core_mmu_idx(mmu_idx)); + newpc = helper_le_ldul_mmu(env, frameptr, oi, 0); + newpsr = helper_le_ldul_mmu(env, frameptr + 4, oi, 0); + + /* Consistency checks on new IPSR */ + newpsr_exc = newpsr & XPSR_EXCP; + if (!((env->v7m.exception == 0 && newpsr_exc == 0) || + (env->v7m.exception == 1 && newpsr_exc != 0))) { + /* Pend the fault and tell our caller to take it */ + env->v7m.cfsr[env->v7m.secure] |= R_V7M_CFSR_INVPC_MASK; + armv7m_nvic_set_pending(env->nvic, ARMV7M_EXCP_USAGE, + env->v7m.secure); + qemu_log_mask(CPU_LOG_INT, + "...taking INVPC UsageFault: " + "IPSR consistency check failed\n"); + return false; + } + + *frame_sp_p = frameptr + 8; + } + + /* This invalidates frame_sp_p */ + switch_v7m_security_state(env, true); + env->v7m.exception = newpsr_exc; + env->v7m.control[M_REG_S] &= ~R_V7M_CONTROL_SFPA_MASK; + if (newpsr & XPSR_SFPA) { + env->v7m.control[M_REG_S] |= R_V7M_CONTROL_SFPA_MASK; + } + xpsr_write(env, 0, XPSR_IT); + env->thumb = newpc & 1; + env->regs[15] = newpc & ~1; + + qemu_log_mask(CPU_LOG_INT, "...function return successful\n"); + return true; +} + +static bool v7m_read_half_insn(ARMCPU *cpu, ARMMMUIdx mmu_idx, + uint32_t addr, uint16_t *insn) +{ + /* + * Load a 16-bit portion of a v7M instruction, returning true on success, + * or false on failure (in which case we will have pended the appropriate + * exception). + * We need to do the instruction fetch's MPU and SAU checks + * like this because there is no MMU index that would allow + * doing the load with a single function call. Instead we must + * first check that the security attributes permit the load + * and that they don't mismatch on the two halves of the instruction, + * and then we do the load as a secure load (ie using the security + * attributes of the address, not the CPU, as architecturally required). + */ + CPUState *cs = CPU(cpu); + CPUARMState *env = &cpu->env; + V8M_SAttributes sattrs = {}; + MemTxAttrs attrs = {}; + ARMMMUFaultInfo fi = {}; + MemTxResult txres; + target_ulong page_size; + hwaddr physaddr; + int prot; + + v8m_security_lookup(env, addr, MMU_INST_FETCH, mmu_idx, &sattrs); + if (!sattrs.nsc || sattrs.ns) { + /* + * This must be the second half of the insn, and it straddles a + * region boundary with the second half not being S&NSC. + */ + env->v7m.sfsr |= R_V7M_SFSR_INVEP_MASK; + armv7m_nvic_set_pending(env->nvic, ARMV7M_EXCP_SECURE, false); + qemu_log_mask(CPU_LOG_INT, + "...really SecureFault with SFSR.INVEP\n"); + return false; + } + if (get_phys_addr(env, addr, MMU_INST_FETCH, mmu_idx, + &physaddr, &attrs, &prot, &page_size, &fi, NULL)) { + /* the MPU lookup failed */ + env->v7m.cfsr[env->v7m.secure] |= R_V7M_CFSR_IACCVIOL_MASK; + armv7m_nvic_set_pending(env->nvic, ARMV7M_EXCP_MEM, env->v7m.secure); + qemu_log_mask(CPU_LOG_INT, "...really MemManage with CFSR.IACCVIOL\n"); + return false; + } + *insn = address_space_lduw_le(arm_addressspace(cs, attrs), physaddr, + attrs, &txres); + if (txres != MEMTX_OK) { + env->v7m.cfsr[M_REG_NS] |= R_V7M_CFSR_IBUSERR_MASK; + armv7m_nvic_set_pending(env->nvic, ARMV7M_EXCP_BUS, false); + qemu_log_mask(CPU_LOG_INT, "...really BusFault with CFSR.IBUSERR\n"); + return false; + } + return true; +} + +static bool v7m_handle_execute_nsc(ARMCPU *cpu) +{ + /* + * Check whether this attempt to execute code in a Secure & NS-Callable + * memory region is for an SG instruction; if so, then emulate the + * effect of the SG instruction and return true. Otherwise pend + * the correct kind of exception and return false. + */ + CPUARMState *env = &cpu->env; + ARMMMUIdx mmu_idx; + uint16_t insn; + + /* + * We should never get here unless get_phys_addr_pmsav8() caused + * an exception for NS executing in S&NSC memory. + */ + assert(!env->v7m.secure); + assert(arm_feature(env, ARM_FEATURE_M_SECURITY)); + + /* We want to do the MPU lookup as secure; work out what mmu_idx that is */ + mmu_idx = arm_v7m_mmu_idx_for_secstate(env, true); + + if (!v7m_read_half_insn(cpu, mmu_idx, env->regs[15], &insn)) { + return false; + } + + if (!env->thumb) { + goto gen_invep; + } + + if (insn != 0xe97f) { + /* + * Not an SG instruction first half (we choose the IMPDEF + * early-SG-check option). + */ + goto gen_invep; + } + + if (!v7m_read_half_insn(cpu, mmu_idx, env->regs[15] + 2, &insn)) { + return false; + } + + if (insn != 0xe97f) { + /* + * Not an SG instruction second half (yes, both halves of the SG + * insn have the same hex value) + */ + goto gen_invep; + } + + /* + * OK, we have confirmed that we really have an SG instruction. + * We know we're NS in S memory so don't need to repeat those checks. + */ + qemu_log_mask(CPU_LOG_INT, "...really an SG instruction at 0x%08" PRIx32 + ", executing it\n", env->regs[15]); + env->regs[14] &= ~1; + env->v7m.control[M_REG_S] &= ~R_V7M_CONTROL_SFPA_MASK; + switch_v7m_security_state(env, true); + xpsr_write(env, 0, XPSR_IT); + env->regs[15] += 4; + return true; + +gen_invep: + env->v7m.sfsr |= R_V7M_SFSR_INVEP_MASK; + armv7m_nvic_set_pending(env->nvic, ARMV7M_EXCP_SECURE, false); + qemu_log_mask(CPU_LOG_INT, + "...really SecureFault with SFSR.INVEP\n"); + return false; +} + +void arm_v7m_cpu_do_interrupt(CPUState *cs) +{ + ARMCPU *cpu = ARM_CPU(cs); + CPUARMState *env = &cpu->env; + uint32_t lr; + bool ignore_stackfaults; + + arm_log_exception(cs->exception_index); + + /* + * For exceptions we just mark as pending on the NVIC, and let that + * handle it. + */ + switch (cs->exception_index) { + case EXCP_UDEF: + armv7m_nvic_set_pending(env->nvic, ARMV7M_EXCP_USAGE, env->v7m.secure); + env->v7m.cfsr[env->v7m.secure] |= R_V7M_CFSR_UNDEFINSTR_MASK; + break; + case EXCP_NOCP: + { + /* + * NOCP might be directed to something other than the current + * security state if this fault is because of NSACR; we indicate + * the target security state using exception.target_el. + */ + int target_secstate; + + if (env->exception.target_el == 3) { + target_secstate = M_REG_S; + } else { + target_secstate = env->v7m.secure; + } + armv7m_nvic_set_pending(env->nvic, ARMV7M_EXCP_USAGE, target_secstate); + env->v7m.cfsr[target_secstate] |= R_V7M_CFSR_NOCP_MASK; + break; + } + case EXCP_INVSTATE: + armv7m_nvic_set_pending(env->nvic, ARMV7M_EXCP_USAGE, env->v7m.secure); + env->v7m.cfsr[env->v7m.secure] |= R_V7M_CFSR_INVSTATE_MASK; + break; + case EXCP_STKOF: + armv7m_nvic_set_pending(env->nvic, ARMV7M_EXCP_USAGE, env->v7m.secure); + env->v7m.cfsr[env->v7m.secure] |= R_V7M_CFSR_STKOF_MASK; + break; + case EXCP_LSERR: + armv7m_nvic_set_pending(env->nvic, ARMV7M_EXCP_SECURE, false); + env->v7m.sfsr |= R_V7M_SFSR_LSERR_MASK; + break; + case EXCP_UNALIGNED: + armv7m_nvic_set_pending(env->nvic, ARMV7M_EXCP_USAGE, env->v7m.secure); + env->v7m.cfsr[env->v7m.secure] |= R_V7M_CFSR_UNALIGNED_MASK; + break; + case EXCP_SWI: + /* The PC already points to the next instruction. */ + armv7m_nvic_set_pending(env->nvic, ARMV7M_EXCP_SVC, env->v7m.secure); + break; + case EXCP_PREFETCH_ABORT: + case EXCP_DATA_ABORT: + /* + * Note that for M profile we don't have a guest facing FSR, but + * the env->exception.fsr will be populated by the code that + * raises the fault, in the A profile short-descriptor format. + */ + switch (env->exception.fsr & 0xf) { + case M_FAKE_FSR_NSC_EXEC: + /* + * Exception generated when we try to execute code at an address + * which is marked as Secure & Non-Secure Callable and the CPU + * is in the Non-Secure state. The only instruction which can + * be executed like this is SG (and that only if both halves of + * the SG instruction have the same security attributes.) + * Everything else must generate an INVEP SecureFault, so we + * emulate the SG instruction here. + */ + if (v7m_handle_execute_nsc(cpu)) { + return; + } + break; + case M_FAKE_FSR_SFAULT: + /* + * Various flavours of SecureFault for attempts to execute or + * access data in the wrong security state. + */ + switch (cs->exception_index) { + case EXCP_PREFETCH_ABORT: + if (env->v7m.secure) { + env->v7m.sfsr |= R_V7M_SFSR_INVTRAN_MASK; + qemu_log_mask(CPU_LOG_INT, + "...really SecureFault with SFSR.INVTRAN\n"); + } else { + env->v7m.sfsr |= R_V7M_SFSR_INVEP_MASK; + qemu_log_mask(CPU_LOG_INT, + "...really SecureFault with SFSR.INVEP\n"); + } + break; + case EXCP_DATA_ABORT: + /* This must be an NS access to S memory */ + env->v7m.sfsr |= R_V7M_SFSR_AUVIOL_MASK; + qemu_log_mask(CPU_LOG_INT, + "...really SecureFault with SFSR.AUVIOL\n"); + break; + } + armv7m_nvic_set_pending(env->nvic, ARMV7M_EXCP_SECURE, false); + break; + case 0x8: /* External Abort */ + switch (cs->exception_index) { + case EXCP_PREFETCH_ABORT: + env->v7m.cfsr[M_REG_NS] |= R_V7M_CFSR_IBUSERR_MASK; + qemu_log_mask(CPU_LOG_INT, "...with CFSR.IBUSERR\n"); + break; + case EXCP_DATA_ABORT: + env->v7m.cfsr[M_REG_NS] |= + (R_V7M_CFSR_PRECISERR_MASK | R_V7M_CFSR_BFARVALID_MASK); + env->v7m.bfar = env->exception.vaddress; + qemu_log_mask(CPU_LOG_INT, + "...with CFSR.PRECISERR and BFAR 0x%x\n", + env->v7m.bfar); + break; + } + armv7m_nvic_set_pending(env->nvic, ARMV7M_EXCP_BUS, false); + break; + default: + /* + * All other FSR values are either MPU faults or "can't happen + * for M profile" cases. + */ + switch (cs->exception_index) { + case EXCP_PREFETCH_ABORT: + env->v7m.cfsr[env->v7m.secure] |= R_V7M_CFSR_IACCVIOL_MASK; + qemu_log_mask(CPU_LOG_INT, "...with CFSR.IACCVIOL\n"); + break; + case EXCP_DATA_ABORT: + env->v7m.cfsr[env->v7m.secure] |= + (R_V7M_CFSR_DACCVIOL_MASK | R_V7M_CFSR_MMARVALID_MASK); + env->v7m.mmfar[env->v7m.secure] = env->exception.vaddress; + qemu_log_mask(CPU_LOG_INT, + "...with CFSR.DACCVIOL and MMFAR 0x%x\n", + env->v7m.mmfar[env->v7m.secure]); + break; + } + armv7m_nvic_set_pending(env->nvic, ARMV7M_EXCP_MEM, + env->v7m.secure); + break; + } + break; + case EXCP_BKPT: + if (semihosting_enabled()) { + int nr; + nr = arm_lduw_code(env, env->regs[15], arm_sctlr_b(env)) & 0xff; + if (nr == 0xab) { + env->regs[15] += 2; + qemu_log_mask(CPU_LOG_INT, + "...handling as semihosting call 0x%x\n", + env->regs[0]); + env->regs[0] = do_arm_semihosting(env); + return; + } + } + armv7m_nvic_set_pending(env->nvic, ARMV7M_EXCP_DEBUG, false); + break; + case EXCP_IRQ: + break; + case EXCP_EXCEPTION_EXIT: + if (env->regs[15] < EXC_RETURN_MIN_MAGIC) { + /* Must be v8M security extension function return */ + assert(env->regs[15] >= FNC_RETURN_MIN_MAGIC); + assert(arm_feature(env, ARM_FEATURE_M_SECURITY)); + if (do_v7m_function_return(cpu)) { + return; + } + } else { + do_v7m_exception_exit(cpu); + return; + } + break; + case EXCP_LAZYFP: + /* + * We already pended the specific exception in the NVIC in the + * v7m_preserve_fp_state() helper function. + */ + break; + default: + cpu_abort(cs, "Unhandled exception 0x%x\n", cs->exception_index); + return; /* Never happens. Keep compiler happy. */ + } + + if (arm_feature(env, ARM_FEATURE_V8)) { + lr = R_V7M_EXCRET_RES1_MASK | + R_V7M_EXCRET_DCRS_MASK; + /* + * The S bit indicates whether we should return to Secure + * or NonSecure (ie our current state). + * The ES bit indicates whether we're taking this exception + * to Secure or NonSecure (ie our target state). We set it + * later, in v7m_exception_taken(). + * The SPSEL bit is also set in v7m_exception_taken() for v8M. + * This corresponds to the ARM ARM pseudocode for v8M setting + * some LR bits in PushStack() and some in ExceptionTaken(); + * the distinction matters for the tailchain cases where we + * can take an exception without pushing the stack. + */ + if (env->v7m.secure) { + lr |= R_V7M_EXCRET_S_MASK; + } + if (!(env->v7m.control[M_REG_S] & R_V7M_CONTROL_FPCA_MASK)) { + lr |= R_V7M_EXCRET_FTYPE_MASK; + } + } else { + lr = R_V7M_EXCRET_RES1_MASK | + R_V7M_EXCRET_S_MASK | + R_V7M_EXCRET_DCRS_MASK | + R_V7M_EXCRET_FTYPE_MASK | + R_V7M_EXCRET_ES_MASK; + if (env->v7m.control[M_REG_NS] & R_V7M_CONTROL_SPSEL_MASK) { + lr |= R_V7M_EXCRET_SPSEL_MASK; + } + } + if (!arm_v7m_is_handler_mode(env)) { + lr |= R_V7M_EXCRET_MODE_MASK; + } + + ignore_stackfaults = v7m_push_stack(cpu); + v7m_exception_taken(cpu, lr, false, ignore_stackfaults); +} + +uint32_t HELPER(v7m_mrs)(CPUARMState *env, uint32_t reg) +{ + uint32_t mask; + unsigned el = arm_current_el(env); + + /* First handle registers which unprivileged can read */ + + switch (reg) { + case 0 ... 7: /* xPSR sub-fields */ + mask = 0; + if ((reg & 1) && el) { + mask |= XPSR_EXCP; /* IPSR (unpriv. reads as zero) */ + } + if (!(reg & 4)) { + mask |= XPSR_NZCV | XPSR_Q; /* APSR */ + if (arm_feature(env, ARM_FEATURE_THUMB_DSP)) { + mask |= XPSR_GE; + } + } + /* EPSR reads as zero */ + return xpsr_read(env) & mask; + break; + case 20: /* CONTROL */ + { + uint32_t value = env->v7m.control[env->v7m.secure]; + if (!env->v7m.secure) { + /* SFPA is RAZ/WI from NS; FPCA is stored in the M_REG_S bank */ + value |= env->v7m.control[M_REG_S] & R_V7M_CONTROL_FPCA_MASK; + } + return value; + } + case 0x94: /* CONTROL_NS */ + /* + * We have to handle this here because unprivileged Secure code + * can read the NS CONTROL register. + */ + if (!env->v7m.secure) { + return 0; + } + return env->v7m.control[M_REG_NS] | + (env->v7m.control[M_REG_S] & R_V7M_CONTROL_FPCA_MASK); + } + + if (el == 0) { + return 0; /* unprivileged reads others as zero */ + } + + if (arm_feature(env, ARM_FEATURE_M_SECURITY)) { + switch (reg) { + case 0x88: /* MSP_NS */ + if (!env->v7m.secure) { + return 0; + } + return env->v7m.other_ss_msp; + case 0x89: /* PSP_NS */ + if (!env->v7m.secure) { + return 0; + } + return env->v7m.other_ss_psp; + case 0x8a: /* MSPLIM_NS */ + if (!env->v7m.secure) { + return 0; + } + return env->v7m.msplim[M_REG_NS]; + case 0x8b: /* PSPLIM_NS */ + if (!env->v7m.secure) { + return 0; + } + return env->v7m.psplim[M_REG_NS]; + case 0x90: /* PRIMASK_NS */ + if (!env->v7m.secure) { + return 0; + } + return env->v7m.primask[M_REG_NS]; + case 0x91: /* BASEPRI_NS */ + if (!env->v7m.secure) { + return 0; + } + return env->v7m.basepri[M_REG_NS]; + case 0x93: /* FAULTMASK_NS */ + if (!env->v7m.secure) { + return 0; + } + return env->v7m.faultmask[M_REG_NS]; + case 0x98: /* SP_NS */ + { + /* + * This gives the non-secure SP selected based on whether we're + * currently in handler mode or not, using the NS CONTROL.SPSEL. + */ + bool spsel = env->v7m.control[M_REG_NS] & R_V7M_CONTROL_SPSEL_MASK; + + if (!env->v7m.secure) { + return 0; + } + if (!arm_v7m_is_handler_mode(env) && spsel) { + return env->v7m.other_ss_psp; + } else { + return env->v7m.other_ss_msp; + } + } + default: + break; + } + } + + switch (reg) { + case 8: /* MSP */ + return v7m_using_psp(env) ? env->v7m.other_sp : env->regs[13]; + case 9: /* PSP */ + return v7m_using_psp(env) ? env->regs[13] : env->v7m.other_sp; + case 10: /* MSPLIM */ + if (!arm_feature(env, ARM_FEATURE_V8)) { + goto bad_reg; + } + return env->v7m.msplim[env->v7m.secure]; + case 11: /* PSPLIM */ + if (!arm_feature(env, ARM_FEATURE_V8)) { + goto bad_reg; + } + return env->v7m.psplim[env->v7m.secure]; + case 16: /* PRIMASK */ + return env->v7m.primask[env->v7m.secure]; + case 17: /* BASEPRI */ + case 18: /* BASEPRI_MAX */ + return env->v7m.basepri[env->v7m.secure]; + case 19: /* FAULTMASK */ + return env->v7m.faultmask[env->v7m.secure]; + default: + bad_reg: + qemu_log_mask(LOG_GUEST_ERROR, "Attempt to read unknown special" + " register %d\n", reg); + return 0; + } +} + +void HELPER(v7m_msr)(CPUARMState *env, uint32_t maskreg, uint32_t val) +{ + /* + * We're passed bits [11..0] of the instruction; extract + * SYSm and the mask bits. + * Invalid combinations of SYSm and mask are UNPREDICTABLE; + * we choose to treat them as if the mask bits were valid. + * NB that the pseudocode 'mask' variable is bits [11..10], + * whereas ours is [11..8]. + */ + uint32_t mask = extract32(maskreg, 8, 4); + uint32_t reg = extract32(maskreg, 0, 8); + int cur_el = arm_current_el(env); + + if (cur_el == 0 && reg > 7 && reg != 20) { + /* + * only xPSR sub-fields and CONTROL.SFPA may be written by + * unprivileged code + */ + return; + } + + if (arm_feature(env, ARM_FEATURE_M_SECURITY)) { + switch (reg) { + case 0x88: /* MSP_NS */ + if (!env->v7m.secure) { + return; + } + env->v7m.other_ss_msp = val; + return; + case 0x89: /* PSP_NS */ + if (!env->v7m.secure) { + return; + } + env->v7m.other_ss_psp = val; + return; + case 0x8a: /* MSPLIM_NS */ + if (!env->v7m.secure) { + return; + } + env->v7m.msplim[M_REG_NS] = val & ~7; + return; + case 0x8b: /* PSPLIM_NS */ + if (!env->v7m.secure) { + return; + } + env->v7m.psplim[M_REG_NS] = val & ~7; + return; + case 0x90: /* PRIMASK_NS */ + if (!env->v7m.secure) { + return; + } + env->v7m.primask[M_REG_NS] = val & 1; + return; + case 0x91: /* BASEPRI_NS */ + if (!env->v7m.secure || !arm_feature(env, ARM_FEATURE_M_MAIN)) { + return; + } + env->v7m.basepri[M_REG_NS] = val & 0xff; + return; + case 0x93: /* FAULTMASK_NS */ + if (!env->v7m.secure || !arm_feature(env, ARM_FEATURE_M_MAIN)) { + return; + } + env->v7m.faultmask[M_REG_NS] = val & 1; + return; + case 0x94: /* CONTROL_NS */ + if (!env->v7m.secure) { + return; + } + write_v7m_control_spsel_for_secstate(env, + val & R_V7M_CONTROL_SPSEL_MASK, + M_REG_NS); + if (arm_feature(env, ARM_FEATURE_M_MAIN)) { + env->v7m.control[M_REG_NS] &= ~R_V7M_CONTROL_NPRIV_MASK; + env->v7m.control[M_REG_NS] |= val & R_V7M_CONTROL_NPRIV_MASK; + } + /* + * SFPA is RAZ/WI from NS. FPCA is RO if NSACR.CP10 == 0, + * RES0 if the FPU is not present, and is stored in the S bank + */ + if (arm_feature(env, ARM_FEATURE_VFP) && + extract32(env->v7m.nsacr, 10, 1)) { + env->v7m.control[M_REG_S] &= ~R_V7M_CONTROL_FPCA_MASK; + env->v7m.control[M_REG_S] |= val & R_V7M_CONTROL_FPCA_MASK; + } + return; + case 0x98: /* SP_NS */ + { + /* + * This gives the non-secure SP selected based on whether we're + * currently in handler mode or not, using the NS CONTROL.SPSEL. + */ + bool spsel = env->v7m.control[M_REG_NS] & R_V7M_CONTROL_SPSEL_MASK; + bool is_psp = !arm_v7m_is_handler_mode(env) && spsel; + uint32_t limit; + + if (!env->v7m.secure) { + return; + } + + limit = is_psp ? env->v7m.psplim[false] : env->v7m.msplim[false]; + + if (val < limit) { + CPUState *cs = env_cpu(env); + + cpu_restore_state(cs, GETPC(), true); + raise_exception(env, EXCP_STKOF, 0, 1); + } + + if (is_psp) { + env->v7m.other_ss_psp = val; + } else { + env->v7m.other_ss_msp = val; + } + return; + } + default: + break; + } + } + + switch (reg) { + case 0 ... 7: /* xPSR sub-fields */ + /* only APSR is actually writable */ + if (!(reg & 4)) { + uint32_t apsrmask = 0; + + if (mask & 8) { + apsrmask |= XPSR_NZCV | XPSR_Q; + } + if ((mask & 4) && arm_feature(env, ARM_FEATURE_THUMB_DSP)) { + apsrmask |= XPSR_GE; + } + xpsr_write(env, val, apsrmask); + } + break; + case 8: /* MSP */ + if (v7m_using_psp(env)) { + env->v7m.other_sp = val; + } else { + env->regs[13] = val; + } + break; + case 9: /* PSP */ + if (v7m_using_psp(env)) { + env->regs[13] = val; + } else { + env->v7m.other_sp = val; + } + break; + case 10: /* MSPLIM */ + if (!arm_feature(env, ARM_FEATURE_V8)) { + goto bad_reg; + } + env->v7m.msplim[env->v7m.secure] = val & ~7; + break; + case 11: /* PSPLIM */ + if (!arm_feature(env, ARM_FEATURE_V8)) { + goto bad_reg; + } + env->v7m.psplim[env->v7m.secure] = val & ~7; + break; + case 16: /* PRIMASK */ + env->v7m.primask[env->v7m.secure] = val & 1; + break; + case 17: /* BASEPRI */ + if (!arm_feature(env, ARM_FEATURE_M_MAIN)) { + goto bad_reg; + } + env->v7m.basepri[env->v7m.secure] = val & 0xff; + break; + case 18: /* BASEPRI_MAX */ + if (!arm_feature(env, ARM_FEATURE_M_MAIN)) { + goto bad_reg; + } + val &= 0xff; + if (val != 0 && (val < env->v7m.basepri[env->v7m.secure] + || env->v7m.basepri[env->v7m.secure] == 0)) { + env->v7m.basepri[env->v7m.secure] = val; + } + break; + case 19: /* FAULTMASK */ + if (!arm_feature(env, ARM_FEATURE_M_MAIN)) { + goto bad_reg; + } + env->v7m.faultmask[env->v7m.secure] = val & 1; + break; + case 20: /* CONTROL */ + /* + * Writing to the SPSEL bit only has an effect if we are in + * thread mode; other bits can be updated by any privileged code. + * write_v7m_control_spsel() deals with updating the SPSEL bit in + * env->v7m.control, so we only need update the others. + * For v7M, we must just ignore explicit writes to SPSEL in handler + * mode; for v8M the write is permitted but will have no effect. + * All these bits are writes-ignored from non-privileged code, + * except for SFPA. + */ + if (cur_el > 0 && (arm_feature(env, ARM_FEATURE_V8) || + !arm_v7m_is_handler_mode(env))) { + write_v7m_control_spsel(env, (val & R_V7M_CONTROL_SPSEL_MASK) != 0); + } + if (cur_el > 0 && arm_feature(env, ARM_FEATURE_M_MAIN)) { + env->v7m.control[env->v7m.secure] &= ~R_V7M_CONTROL_NPRIV_MASK; + env->v7m.control[env->v7m.secure] |= val & R_V7M_CONTROL_NPRIV_MASK; + } + if (arm_feature(env, ARM_FEATURE_VFP)) { + /* + * SFPA is RAZ/WI from NS or if no FPU. + * FPCA is RO if NSACR.CP10 == 0, RES0 if the FPU is not present. + * Both are stored in the S bank. + */ + if (env->v7m.secure) { + env->v7m.control[M_REG_S] &= ~R_V7M_CONTROL_SFPA_MASK; + env->v7m.control[M_REG_S] |= val & R_V7M_CONTROL_SFPA_MASK; + } + if (cur_el > 0 && + (env->v7m.secure || !arm_feature(env, ARM_FEATURE_M_SECURITY) || + extract32(env->v7m.nsacr, 10, 1))) { + env->v7m.control[M_REG_S] &= ~R_V7M_CONTROL_FPCA_MASK; + env->v7m.control[M_REG_S] |= val & R_V7M_CONTROL_FPCA_MASK; + } + } + break; + default: + bad_reg: + qemu_log_mask(LOG_GUEST_ERROR, "Attempt to write unknown special" + " register %d\n", reg); + return; + } +} + +uint32_t HELPER(v7m_tt)(CPUARMState *env, uint32_t addr, uint32_t op) +{ + /* Implement the TT instruction. op is bits [7:6] of the insn. */ + bool forceunpriv = op & 1; + bool alt = op & 2; + V8M_SAttributes sattrs = {}; + uint32_t tt_resp; + bool r, rw, nsr, nsrw, mrvalid; + int prot; + ARMMMUFaultInfo fi = {}; + MemTxAttrs attrs = {}; + hwaddr phys_addr; + ARMMMUIdx mmu_idx; + uint32_t mregion; + bool targetpriv; + bool targetsec = env->v7m.secure; + bool is_subpage; + + /* + * Work out what the security state and privilege level we're + * interested in is... + */ + if (alt) { + targetsec = !targetsec; + } + + if (forceunpriv) { + targetpriv = false; + } else { + targetpriv = arm_v7m_is_handler_mode(env) || + !(env->v7m.control[targetsec] & R_V7M_CONTROL_NPRIV_MASK); + } + + /* ...and then figure out which MMU index this is */ + mmu_idx = arm_v7m_mmu_idx_for_secstate_and_priv(env, targetsec, targetpriv); + + /* + * We know that the MPU and SAU don't care about the access type + * for our purposes beyond that we don't want to claim to be + * an insn fetch, so we arbitrarily call this a read. + */ + + /* + * MPU region info only available for privileged or if + * inspecting the other MPU state. + */ + if (arm_current_el(env) != 0 || alt) { + /* We can ignore the return value as prot is always set */ + pmsav8_mpu_lookup(env, addr, MMU_DATA_LOAD, mmu_idx, + &phys_addr, &attrs, &prot, &is_subpage, + &fi, &mregion); + if (mregion == -1) { + mrvalid = false; + mregion = 0; + } else { + mrvalid = true; + } + r = prot & PAGE_READ; + rw = prot & PAGE_WRITE; + } else { + r = false; + rw = false; + mrvalid = false; + mregion = 0; + } + + if (env->v7m.secure) { + v8m_security_lookup(env, addr, MMU_DATA_LOAD, mmu_idx, &sattrs); + nsr = sattrs.ns && r; + nsrw = sattrs.ns && rw; + } else { + sattrs.ns = true; + nsr = false; + nsrw = false; + } + + tt_resp = (sattrs.iregion << 24) | + (sattrs.irvalid << 23) | + ((!sattrs.ns) << 22) | + (nsrw << 21) | + (nsr << 20) | + (rw << 19) | + (r << 18) | + (sattrs.srvalid << 17) | + (mrvalid << 16) | + (sattrs.sregion << 8) | + mregion; + + return tt_resp; +} + +#endif /* !CONFIG_USER_ONLY */ + +ARMMMUIdx arm_v7m_mmu_idx_all(CPUARMState *env, + bool secstate, bool priv, bool negpri) +{ + ARMMMUIdx mmu_idx = ARM_MMU_IDX_M; + + if (priv) { + mmu_idx |= ARM_MMU_IDX_M_PRIV; + } + + if (negpri) { + mmu_idx |= ARM_MMU_IDX_M_NEGPRI; + } + + if (secstate) { + mmu_idx |= ARM_MMU_IDX_M_S; + } + + return mmu_idx; +} + +ARMMMUIdx arm_v7m_mmu_idx_for_secstate_and_priv(CPUARMState *env, + bool secstate, bool priv) +{ + bool negpri = armv7m_nvic_neg_prio_requested(env->nvic, secstate); + + return arm_v7m_mmu_idx_all(env, secstate, priv, negpri); +} + +/* Return the MMU index for a v7M CPU in the specified security state */ +ARMMMUIdx arm_v7m_mmu_idx_for_secstate(CPUARMState *env, bool secstate) +{ + bool priv = arm_current_el(env) != 0; + + return arm_v7m_mmu_idx_for_secstate_and_priv(env, secstate, priv); +} From patchwork Thu Jul 4 16:07:57 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Peter Maydell X-Patchwork-Id: 11031725 Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id 3D21513BD for ; Thu, 4 Jul 2019 16:24:03 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 2A44F28A68 for ; Thu, 4 Jul 2019 16:24:03 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id 1DD0428A6B; Thu, 4 Jul 2019 16:24:03 +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=-5.0 required=2.0 tests=BAYES_00,DKIM_INVALID, DKIM_SIGNED,MAILING_LIST_MULTI,RCVD_IN_DNSWL_MED autolearn=ham version=3.3.1 Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) (using TLSv1.2 with cipher AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by mail.wl.linuxfoundation.org (Postfix) with ESMTPS id B6F5028A68 for ; Thu, 4 Jul 2019 16:24:02 +0000 (UTC) Received: from localhost ([::1]:47550 helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.86_2) (envelope-from ) id 1hj4Wg-0007Qo-2D for patchwork-qemu-devel@patchwork.kernel.org; Thu, 04 Jul 2019 12:24:02 -0400 Received: from eggs.gnu.org ([2001:470:142:3::10]:44222) by lists.gnu.org with esmtp (Exim 4.86_2) (envelope-from ) id 1hj4He-00084M-6W for qemu-devel@nongnu.org; Thu, 04 Jul 2019 12:08:31 -0400 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1hj4Hb-0002JG-VN for qemu-devel@nongnu.org; Thu, 04 Jul 2019 12:08:29 -0400 Received: from mail-wm1-x334.google.com ([2a00:1450:4864:20::334]:40526) by eggs.gnu.org with esmtps (TLS1.0:RSA_AES_128_CBC_SHA1:16) (Exim 4.71) (envelope-from ) id 1hj4HU-000273-90 for qemu-devel@nongnu.org; Thu, 04 Jul 2019 12:08:22 -0400 Received: by mail-wm1-x334.google.com with SMTP id v19so6665283wmj.5 for ; Thu, 04 Jul 2019 09:08:17 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linaro.org; s=google; h=from:to:subject:date:message-id:in-reply-to:references:mime-version :content-transfer-encoding; bh=5wnKprFO/ti+kN4tLaASb1pcE7lyZq24vpfhZiH+fvc=; b=XhEdfjUoB0lVWD6VZG5D22/kRWGs7Iem0vrbNPguu8wIaCs2mIAkoix7/e+z6XQUFr JRGkLDO+IeZ7PvXVe9wEcyGv38WNEQsZ5qFEQpe9i6zye5F9G/1eP3sto8aw3azKQzcR 9vl6SprLy4DMbrmNTN+xFQG0XUrOGRvPfE/i1lH4/91x74m+WUFMhQWn7KEWA8c1WzWi inPq7EMw7AgWgLwE3pZJ4uI+x+ROAJiuoDgwH0WOUWdMoi0zEOu2aJoWy5sTqcOG0oMu N7lVfyE0U82zf+uHghIfuu8wA63FyYZJYXx668sXoQNBxvTf38AG/DEHy/GO1jJQWoit a1+A== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:subject:date:message-id:in-reply-to :references:mime-version:content-transfer-encoding; bh=5wnKprFO/ti+kN4tLaASb1pcE7lyZq24vpfhZiH+fvc=; b=gjyBpfOkKCvNEOoMvIzR6ceWBB9RDjvPvqlmZmEdjD/HGdgAOJmntROB1VnffJYLCA fPCAM/KO+cfOubsE3PMkNcROcIJ55/tKD2nXC3d7X+TzC8WpSucePBBx8907GOZE2frX aqWgeBVpp8GfyIaRSwYInWuBHUzi7PMfYpCcxf9pfrsiYiqmLgio5LoVCTvBcHn9+Kmf cXeOxPcra3+JjGls5i5UOizWezCHqlnV2P9j5jlSIUJMZ1lszaagC+kMwfyZRxha2e1v s5BPIAyQdYnEfYDjG6xPVGRcvZE5hV2WOuREAW4T0Cx+CvB8L65FbS8CCMQtp4wflUGa nXag== X-Gm-Message-State: APjAAAXeZVhIPFgm3qCgK/afqmNvQ/MJoHsbqd9Rl2reQ248ShaK07vs 3nHUDn6oeF3qkHuMu6cMYn6+/5aLynI4OQ== X-Google-Smtp-Source: APXvYqxrw3LuhmVaU4JoV2JQvyZrWQlOtJSaW4RKKpSfPB5o9ezfwWCyrTnH+5ijRY5ZoqelHGMIYQ== X-Received: by 2002:a7b:c301:: with SMTP id k1mr192061wmj.43.1562256493136; Thu, 04 Jul 2019 09:08:13 -0700 (PDT) Received: from orth.archaic.org.uk (orth.archaic.org.uk. [81.2.115.148]) by smtp.gmail.com with ESMTPSA id s2sm3849690wmj.33.2019.07.04.09.08.12 for (version=TLS1_3 cipher=AEAD-AES256-GCM-SHA384 bits=256/256); Thu, 04 Jul 2019 09:08:12 -0700 (PDT) From: Peter Maydell To: qemu-devel@nongnu.org Date: Thu, 4 Jul 2019 17:07:57 +0100 Message-Id: <20190704160802.12419-5-peter.maydell@linaro.org> X-Mailer: git-send-email 2.20.1 In-Reply-To: <20190704160802.12419-1-peter.maydell@linaro.org> References: <20190704160802.12419-1-peter.maydell@linaro.org> MIME-Version: 1.0 X-detected-operating-system: by eggs.gnu.org: Genre and OS details not recognized. X-Received-From: 2a00:1450:4864:20::334 Subject: [Qemu-devel] [PULL 4/9] arm v8M: Forcibly clear negative-priority exceptions on deactivate X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.23 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: qemu-devel-bounces+patchwork-qemu-devel=patchwork.kernel.org@nongnu.org Sender: "Qemu-devel" X-Virus-Scanned: ClamAV using ClamSMTP To prevent execution priority remaining negative if the guest returns from an NMI or HardFault with a corrupted IPSR, the v8M interrupt deactivation process forces the HardFault and NMI to inactive based on the current raw execution priority, even if the interrupt the guest is trying to deactivate is something else. In the pseudocode this is done in the Deactivate() function. Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson Message-id: 20190617175317.27557-3-peter.maydell@linaro.org --- hw/intc/armv7m_nvic.c | 40 +++++++++++++++++++++++++++++++++++----- 1 file changed, 35 insertions(+), 5 deletions(-) diff --git a/hw/intc/armv7m_nvic.c b/hw/intc/armv7m_nvic.c index b8ede30b3cb..330eb728dd5 100644 --- a/hw/intc/armv7m_nvic.c +++ b/hw/intc/armv7m_nvic.c @@ -812,15 +812,45 @@ void armv7m_nvic_get_pending_irq_info(void *opaque, int armv7m_nvic_complete_irq(void *opaque, int irq, bool secure) { NVICState *s = (NVICState *)opaque; - VecInfo *vec; + VecInfo *vec = NULL; int ret; assert(irq > ARMV7M_EXCP_RESET && irq < s->num_irq); - if (secure && exc_is_banked(irq)) { - vec = &s->sec_vectors[irq]; - } else { - vec = &s->vectors[irq]; + /* + * For negative priorities, v8M will forcibly deactivate the appropriate + * NMI or HardFault regardless of what interrupt we're being asked to + * deactivate (compare the DeActivate() pseudocode). This is a guard + * against software returning from NMI or HardFault with a corrupted + * IPSR and leaving the CPU in a negative-priority state. + * v7M does not do this, but simply deactivates the requested interrupt. + */ + if (arm_feature(&s->cpu->env, ARM_FEATURE_V8)) { + switch (armv7m_nvic_raw_execution_priority(s)) { + case -1: + if (s->cpu->env.v7m.aircr & R_V7M_AIRCR_BFHFNMINS_MASK) { + vec = &s->vectors[ARMV7M_EXCP_HARD]; + } else { + vec = &s->sec_vectors[ARMV7M_EXCP_HARD]; + } + break; + case -2: + vec = &s->vectors[ARMV7M_EXCP_NMI]; + break; + case -3: + vec = &s->sec_vectors[ARMV7M_EXCP_HARD]; + break; + default: + break; + } + } + + if (!vec) { + if (secure && exc_is_banked(irq)) { + vec = &s->sec_vectors[irq]; + } else { + vec = &s->vectors[irq]; + } } trace_nvic_complete_irq(irq, secure); From patchwork Thu Jul 4 16:07:58 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Peter Maydell X-Patchwork-Id: 11031715 Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id A74CA112C for ; Thu, 4 Jul 2019 16:16:13 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 9724E28745 for ; Thu, 4 Jul 2019 16:16:13 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id 88CBF28AB6; Thu, 4 Jul 2019 16:16:13 +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=-5.0 required=2.0 tests=BAYES_00,DKIM_INVALID, DKIM_SIGNED,MAILING_LIST_MULTI,RCVD_IN_DNSWL_MED autolearn=ham version=3.3.1 Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) (using TLSv1.2 with cipher AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by mail.wl.linuxfoundation.org (Postfix) with ESMTPS id 31B0128745 for ; Thu, 4 Jul 2019 16:16:13 +0000 (UTC) Received: from localhost ([::1]:47492 helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.86_2) (envelope-from ) id 1hj4P6-0000OG-He for patchwork-qemu-devel@patchwork.kernel.org; Thu, 04 Jul 2019 12:16:12 -0400 Received: from eggs.gnu.org ([2001:470:142:3::10]:44174) by lists.gnu.org with esmtp (Exim 4.86_2) (envelope-from ) id 1hj4HU-000802-97 for qemu-devel@nongnu.org; Thu, 04 Jul 2019 12:08:22 -0400 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1hj4HS-0002AM-3g for qemu-devel@nongnu.org; Thu, 04 Jul 2019 12:08:20 -0400 Received: from mail-wr1-x42d.google.com ([2a00:1450:4864:20::42d]:46243) by eggs.gnu.org with esmtps (TLS1.0:RSA_AES_128_CBC_SHA1:16) (Exim 4.71) (envelope-from ) id 1hj4HQ-00026z-Ck for qemu-devel@nongnu.org; Thu, 04 Jul 2019 12:08:16 -0400 Received: by mail-wr1-x42d.google.com with SMTP id z1so2539230wru.13 for ; Thu, 04 Jul 2019 09:08:15 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linaro.org; s=google; h=from:to:subject:date:message-id:in-reply-to:references:mime-version :content-transfer-encoding; bh=yj2MtKDVZXdSKXJUOki1opdC2pkjoIjecllTKPnecKA=; b=qW7pyOplwRLn1LKQJRwt3ABOlbmEVfj3WNS2lEDnWNv/raBsnO7br6KaY7WsyTqqwJ Y/wUYA8NqiGlNYvRrns3fv1E3vTaaXFE0WAq9o/pJAL7uY6M2H+RUZ8Cdplm4Dv8+yAu mrhyr7vxhwwsvNgThg3xyoQsH0q9j86+eRmk8icBumpOMrzq6GmDDe2+uA+SAAKbZdTe umul4btJVy/ZYCEIeG7l131bzXTQMUaMgOZ4npM0chCLA0Na660UyzHvVxRemEBhoUou rHaEJ8OROYzcRvOXDfTAGggIqhN1g3KWZ9SoI17bPReOM27u/j0cgzo8z8wGBq6qp/Pk qb3g== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:subject:date:message-id:in-reply-to :references:mime-version:content-transfer-encoding; bh=yj2MtKDVZXdSKXJUOki1opdC2pkjoIjecllTKPnecKA=; b=SXG/J4Qh204mNeCht5YU0uvDIPiJoripE/DxsPSGdhDD5XHcYL54CyWjHh4Td7kfXs s1rvwyDh9dXoptmaKarmBx8GmFqvaMdFXok0G6wgP4gFtr2Jly5WgmdeCYE/v/PgXETA D6kqJ4wqj/qrNjhWCRgYsQiwCkObCD/sIRCZWA5dxXTloPXLMzY7vNEEEoXf+bH0R21k 0Wg0Yem7SOd0k/nG6PzerYzD1IWPbM6VpPOvUlXhxRDsWzCs0aldXKAtn64stJW3aOJn i9BiDrYioF3hqtXFlcy33p5mIJ3rVBco3bi85E3Y4WQYDsUbE0AhZizwyg68IfpgyOOY SNlQ== X-Gm-Message-State: APjAAAWcccKjy+08mGATJtpJizRgzm6gNg8LoXdvnG9moD3UZUdo+kbP sSE/Z8PCBECTYGb018V+PJtC9x29yS9eyw== X-Google-Smtp-Source: APXvYqyCnk/ZUVWb0raidbfK6aysKYbIj/GEeWm/kxvRJBLRsWwzvzgAqMjjgorVdiPA4R/9qVPPZg== X-Received: by 2002:a5d:5186:: with SMTP id k6mr37209531wrv.30.1562256493932; Thu, 04 Jul 2019 09:08:13 -0700 (PDT) Received: from orth.archaic.org.uk (orth.archaic.org.uk. [81.2.115.148]) by smtp.gmail.com with ESMTPSA id s2sm3849690wmj.33.2019.07.04.09.08.13 for (version=TLS1_3 cipher=AEAD-AES256-GCM-SHA384 bits=256/256); Thu, 04 Jul 2019 09:08:13 -0700 (PDT) From: Peter Maydell To: qemu-devel@nongnu.org Date: Thu, 4 Jul 2019 17:07:58 +0100 Message-Id: <20190704160802.12419-6-peter.maydell@linaro.org> X-Mailer: git-send-email 2.20.1 In-Reply-To: <20190704160802.12419-1-peter.maydell@linaro.org> References: <20190704160802.12419-1-peter.maydell@linaro.org> MIME-Version: 1.0 X-detected-operating-system: by eggs.gnu.org: Genre and OS details not recognized. X-Received-From: 2a00:1450:4864:20::42d Subject: [Qemu-devel] [PULL 5/9] target/arm: v8M: Check state of exception being returned from X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.23 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: qemu-devel-bounces+patchwork-qemu-devel=patchwork.kernel.org@nongnu.org Sender: "Qemu-devel" X-Virus-Scanned: ClamAV using ClamSMTP In v8M, an attempt to return from an exception which is not active is an illegal exception return. For this purpose, exceptions which can configurably target either Secure or NonSecure are not considered to be active if they are configured for the opposite security state for the one we're trying to return from (eg attempt to return from an NS NMI but NMI targets Secure). In the pseudocode this is handled by IsActiveForState(). Detect this case rather than counting an active exception possibly of the wrong security state as being sufficient. Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson Message-id: 20190617175317.27557-4-peter.maydell@linaro.org --- hw/intc/armv7m_nvic.c | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/hw/intc/armv7m_nvic.c b/hw/intc/armv7m_nvic.c index 330eb728dd5..9f8f0d3ff55 100644 --- a/hw/intc/armv7m_nvic.c +++ b/hw/intc/armv7m_nvic.c @@ -860,7 +860,19 @@ int armv7m_nvic_complete_irq(void *opaque, int irq, bool secure) return -1; } - ret = nvic_rettobase(s); + /* + * If this is a configurable exception and it is currently + * targeting the opposite security state from the one we're trying + * to complete it for, this counts as an illegal exception return. + * We still need to deactivate whatever vector the logic above has + * selected, though, as it might not be the same as the one for the + * requested exception number. + */ + if (!exc_is_banked(irq) && exc_targets_secure(s, irq) != secure) { + ret = -1; + } else { + ret = nvic_rettobase(s); + } vec->active = 0; if (vec->level) { From patchwork Thu Jul 4 16:07:59 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Peter Maydell X-Patchwork-Id: 11031719 Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id 6578513BD for ; Thu, 4 Jul 2019 16:20:20 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 5530528745 for ; Thu, 4 Jul 2019 16:20:20 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id 49ABA28991; Thu, 4 Jul 2019 16:20:20 +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=-5.0 required=2.0 tests=BAYES_00,DKIM_INVALID, DKIM_SIGNED,MAILING_LIST_MULTI,RCVD_IN_DNSWL_MED autolearn=ham version=3.3.1 Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) (using TLSv1.2 with cipher AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by mail.wl.linuxfoundation.org (Postfix) with ESMTPS id CADD428745 for ; Thu, 4 Jul 2019 16:20:19 +0000 (UTC) Received: from localhost ([::1]:47520 helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.86_2) (envelope-from ) id 1hj4T5-0003bE-5p for patchwork-qemu-devel@patchwork.kernel.org; Thu, 04 Jul 2019 12:20:19 -0400 Received: from eggs.gnu.org ([2001:470:142:3::10]:44173) by lists.gnu.org with esmtp (Exim 4.86_2) (envelope-from ) id 1hj4HU-000800-93 for qemu-devel@nongnu.org; Thu, 04 Jul 2019 12:08:22 -0400 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1hj4HS-0002Ah-Be for qemu-devel@nongnu.org; Thu, 04 Jul 2019 12:08:20 -0400 Received: from mail-wr1-x443.google.com ([2a00:1450:4864:20::443]:45491) by eggs.gnu.org with esmtps (TLS1.0:RSA_AES_128_CBC_SHA1:16) (Exim 4.71) (envelope-from ) id 1hj4HQ-00028x-Dp for qemu-devel@nongnu.org; Thu, 04 Jul 2019 12:08:16 -0400 Received: by mail-wr1-x443.google.com with SMTP id f9so7149309wre.12 for ; Thu, 04 Jul 2019 09:08:16 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linaro.org; s=google; h=from:to:subject:date:message-id:in-reply-to:references:mime-version :content-transfer-encoding; bh=JEKrypWivlM4LroK8QYHrkEvHc8PX/KQovIQmzmw7AE=; b=T232z+HhaMbCzO+VCVswfH74Spkj4eUkZvD9NYpMj7oZy0awDOjwROJyamA5k/Q0gG cEN4GCWyomZW9JFOCYseP7D49g3nDSQO/HWX3Mxcl7iOxFz8kFIJ4oGP+2IYie3nFclb fpTFgtDu6fF7G2bHp5QQYEZ6Id4/Gg230b2AEkoO91QC45q9HoHlVRtI7FRLEqPBbuTA hoFRr01UEYIfR4roEQByQfalQBU7MkEVnuMY4HWmhs4M58h1B1JNhjcdXB+CIRNPBRhC ZuGoQwBLKpqmGUOFK2SE34kOyhmkyBIj2qcujKckxQs9CX+a5sDfbhUmALllK/0J3OnU rISw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:subject:date:message-id:in-reply-to :references:mime-version:content-transfer-encoding; bh=JEKrypWivlM4LroK8QYHrkEvHc8PX/KQovIQmzmw7AE=; b=OsOnz43ZF+PIMwuZg62uIrbMZtM8NeO6fCDRvS1H4XHnfP82lzR5M/tlcEEeHy7jde O1Q9SChVONBgjRfKxazgWaIbbrc1CA06cUgu/WF1cpR9V25Nm+Ok57aABPSqa1Id+d+V g+lIKjY3YRs1IWx521zj6n5P28WwwPmslLB4yZzSC6OawThbu5pAwciJ0tN3Ofy4S3cf RS6PchPTHdmgeDPgofx/svNYUc3EgpxsR2f/Zw29iJrnKsb2JsmZ7GANWVdEKFbYoXSZ hF4FNPdMPmNfrQ3fzNtYbWNGE8zRviXX+K0R4pag3MjYbNPCUw9WuJ1Rq+39/JNNV+sB GIVA== X-Gm-Message-State: APjAAAVHWpfFcJXRmA2u2e/AUur0Xi1FyF5aettFEym2q7HVMtxGhKSD 6S3RdldjvlflwebYga/5tgshf+t37UwjWA== X-Google-Smtp-Source: APXvYqwYAOGBmuaEjTe92vOKMT+u9G2sjLCS7YvY2BT22bofCR9zDqtFoejgzjWhZRqQELfVyHzZTA== X-Received: by 2002:adf:db4f:: with SMTP id f15mr8592292wrj.188.1562256494977; Thu, 04 Jul 2019 09:08:14 -0700 (PDT) Received: from orth.archaic.org.uk (orth.archaic.org.uk. [81.2.115.148]) by smtp.gmail.com with ESMTPSA id s2sm3849690wmj.33.2019.07.04.09.08.13 for (version=TLS1_3 cipher=AEAD-AES256-GCM-SHA384 bits=256/256); Thu, 04 Jul 2019 09:08:14 -0700 (PDT) From: Peter Maydell To: qemu-devel@nongnu.org Date: Thu, 4 Jul 2019 17:07:59 +0100 Message-Id: <20190704160802.12419-7-peter.maydell@linaro.org> X-Mailer: git-send-email 2.20.1 In-Reply-To: <20190704160802.12419-1-peter.maydell@linaro.org> References: <20190704160802.12419-1-peter.maydell@linaro.org> MIME-Version: 1.0 X-detected-operating-system: by eggs.gnu.org: Genre and OS details not recognized. X-Received-From: 2a00:1450:4864:20::443 Subject: [Qemu-devel] [PULL 6/9] target/arm: Use _ra versions of cpu_stl_data() in v7M helpers X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.23 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: qemu-devel-bounces+patchwork-qemu-devel=patchwork.kernel.org@nongnu.org Sender: "Qemu-devel" X-Virus-Scanned: ClamAV using ClamSMTP In the various helper functions for v7M/v8M instructions, use the _ra versions of cpu_stl_data() and friends. Otherwise we may get wrong behaviour or an assert() due to not being able to locate the TB if there is an exception on the memory access or if it performs an IO operation when in icount mode. Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson Message-id: 20190617175317.27557-5-peter.maydell@linaro.org --- target/arm/m_helper.c | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/target/arm/m_helper.c b/target/arm/m_helper.c index 1b0ad95a05f..dc6dbd8b961 100644 --- a/target/arm/m_helper.c +++ b/target/arm/m_helper.c @@ -537,8 +537,8 @@ void HELPER(v7m_blxns)(CPUARMState *env, uint32_t dest) } /* Note that these stores can throw exceptions on MPU faults */ - cpu_stl_data(env, sp, nextinst); - cpu_stl_data(env, sp + 4, saved_psr); + cpu_stl_data_ra(env, sp, nextinst, GETPC()); + cpu_stl_data_ra(env, sp + 4, saved_psr, GETPC()); env->regs[13] = sp; env->regs[14] = 0xfeffffff; @@ -953,6 +953,7 @@ void HELPER(v7m_vlstm)(CPUARMState *env, uint32_t fptr) /* fptr is the value of Rn, the frame pointer we store the FP regs to */ bool s = env->v7m.fpccr[M_REG_S] & R_V7M_FPCCR_S_MASK; bool lspact = env->v7m.fpccr[s] & R_V7M_FPCCR_LSPACT_MASK; + uintptr_t ra = GETPC(); assert(env->v7m.secure); @@ -978,7 +979,7 @@ void HELPER(v7m_vlstm)(CPUARMState *env, uint32_t fptr) * Note that we do not use v7m_stack_write() here, because the * accesses should not set the FSR bits for stacking errors if they * fail. (In pseudocode terms, they are AccType_NORMAL, not AccType_STACK - * or AccType_LAZYFP). Faults in cpu_stl_data() will throw exceptions + * or AccType_LAZYFP). Faults in cpu_stl_data_ra() will throw exceptions * and longjmp out. */ if (!(env->v7m.fpccr[M_REG_S] & R_V7M_FPCCR_LSPEN_MASK)) { @@ -994,10 +995,10 @@ void HELPER(v7m_vlstm)(CPUARMState *env, uint32_t fptr) if (i >= 16) { faddr += 8; /* skip the slot for the FPSCR */ } - cpu_stl_data(env, faddr, slo); - cpu_stl_data(env, faddr + 4, shi); + cpu_stl_data_ra(env, faddr, slo, ra); + cpu_stl_data_ra(env, faddr + 4, shi, ra); } - cpu_stl_data(env, fptr + 0x40, vfp_get_fpscr(env)); + cpu_stl_data_ra(env, fptr + 0x40, vfp_get_fpscr(env), ra); /* * If TS is 0 then s0 to s15 and FPSCR are UNKNOWN; we choose to @@ -1018,6 +1019,8 @@ void HELPER(v7m_vlstm)(CPUARMState *env, uint32_t fptr) void HELPER(v7m_vlldm)(CPUARMState *env, uint32_t fptr) { + uintptr_t ra = GETPC(); + /* fptr is the value of Rn, the frame pointer we load the FP regs from */ assert(env->v7m.secure); @@ -1051,13 +1054,13 @@ void HELPER(v7m_vlldm)(CPUARMState *env, uint32_t fptr) faddr += 8; /* skip the slot for the FPSCR */ } - slo = cpu_ldl_data(env, faddr); - shi = cpu_ldl_data(env, faddr + 4); + slo = cpu_ldl_data_ra(env, faddr, ra); + shi = cpu_ldl_data_ra(env, faddr + 4, ra); dn = (uint64_t) shi << 32 | slo; *aa32_vfp_dreg(env, i / 2) = dn; } - fpscr = cpu_ldl_data(env, fptr + 0x40); + fpscr = cpu_ldl_data_ra(env, fptr + 0x40, ra); vfp_set_fpscr(env, fpscr); } From patchwork Thu Jul 4 16:08:00 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: Peter Maydell X-Patchwork-Id: 11031705 Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id 3412A112C for ; Thu, 4 Jul 2019 16:14:10 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 21EC628AB1 for ; Thu, 4 Jul 2019 16:14:10 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id 10CBD28AB6; Thu, 4 Jul 2019 16:14:10 +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=-5.0 required=2.0 tests=BAYES_00,DKIM_INVALID, DKIM_SIGNED,MAILING_LIST_MULTI,RCVD_IN_DNSWL_MED autolearn=ham version=3.3.1 Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) (using TLSv1.2 with cipher AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by mail.wl.linuxfoundation.org (Postfix) with ESMTPS id A3E4528AB1 for ; Thu, 4 Jul 2019 16:14:09 +0000 (UTC) Received: from localhost ([::1]:47450 helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.86_2) (envelope-from ) id 1hj4N6-0006Sq-V3 for patchwork-qemu-devel@patchwork.kernel.org; Thu, 04 Jul 2019 12:14:08 -0400 Received: from eggs.gnu.org ([2001:470:142:3::10]:44218) by lists.gnu.org with esmtp (Exim 4.86_2) (envelope-from ) id 1hj4He-00084I-6W for qemu-devel@nongnu.org; Thu, 04 Jul 2019 12:08:31 -0400 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1hj4Hb-0002J3-Qy for qemu-devel@nongnu.org; Thu, 04 Jul 2019 12:08:29 -0400 Received: from mail-wr1-x444.google.com ([2a00:1450:4864:20::444]:38342) by eggs.gnu.org with esmtps (TLS1.0:RSA_AES_128_CBC_SHA1:16) (Exim 4.71) (envelope-from ) id 1hj4HU-00029V-8V for qemu-devel@nongnu.org; Thu, 04 Jul 2019 12:08:22 -0400 Received: by mail-wr1-x444.google.com with SMTP id p11so1674521wro.5 for ; Thu, 04 Jul 2019 09:08:17 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linaro.org; s=google; h=from:to:subject:date:message-id:in-reply-to:references:mime-version :content-transfer-encoding; bh=ZYBnDUJAtZx/GB1Y3ulbQN75K6eLU21AWTYRu4y7eKY=; b=AISnyTEvdVKOJfjt6IejblXYshj9aENKpqh+Ur/b/hX3dMAA7E7EunEMkLtXZtxrmO 55JJWGpVEoulv5hO59X2SknscOR5EItwZJvUb7RZne8CKYL2WK4FaR7jEWtXIwITiicL QUscExFORSmRgRGjy40UXiPm5Ah3BAVJEXQYxiEf4AHYYrARKmVcxfKX3apgtavF3kBz zbzI7ImOM7N5f6vcemUD7iKT+8Oc9gTQ0TFErXHoQ5vt5Da445fBmhy2m400Gxwr7yX5 Ny1eFog0PtBXj1zASCJJvmkjb15Z/tboeDx/c2gaQfp/MvExohXWdn36mgse3u5dy8o2 Xuvg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:subject:date:message-id:in-reply-to :references:mime-version:content-transfer-encoding; bh=ZYBnDUJAtZx/GB1Y3ulbQN75K6eLU21AWTYRu4y7eKY=; b=FnqlBZrg7af3dyqLw2kBErIDQ/EfQXQUhcWjo6PkTsOVkeLxkW9O8UyUGNm1/sEI+n vcxZR5HwgcnOki6hWsqP55jQ+daDDIUX3v2f0lSNNw5VHt0W2MD8xLf15lL+k3jioCJU R4rYEBStTZP0bn0z65RLNbCgDq0wmLuYmhdPXd0cnvjQUv9EFzfwDKA3FdRLEq9bBKbx PRTvcxtb0+fzbqmP/ZnGlK8SGd3PzHZPPBYPaB21l47qilAyrhNc+sfB4W+4tsEo55Nu ZQZUc/PiGMTNYjPnLwOklDeTp/2cu+J8AZYkQDAKf918Z+DTVjeq/k7xguLB29HAXtU0 VZfw== X-Gm-Message-State: APjAAAVFxucRl7jDE5uLZ1yoxH5PFP+tgdrW01SHQxWyi3YQO+S9aI5Q 9B6pUl6hHl8+XSY3Nu5qJ6AtC1IIqO0Ojw== X-Google-Smtp-Source: APXvYqz5HTYdfWdWgpV0Q6MGOZLcygTIq1IOI6fMwO27e11z406Xm6NwEnGE4ZDJMBXbHl5KgA0SqQ== X-Received: by 2002:a5d:4a02:: with SMTP id m2mr28912059wrq.193.1562256495923; Thu, 04 Jul 2019 09:08:15 -0700 (PDT) Received: from orth.archaic.org.uk (orth.archaic.org.uk. [81.2.115.148]) by smtp.gmail.com with ESMTPSA id s2sm3849690wmj.33.2019.07.04.09.08.15 for (version=TLS1_3 cipher=AEAD-AES256-GCM-SHA384 bits=256/256); Thu, 04 Jul 2019 09:08:15 -0700 (PDT) From: Peter Maydell To: qemu-devel@nongnu.org Date: Thu, 4 Jul 2019 17:08:00 +0100 Message-Id: <20190704160802.12419-8-peter.maydell@linaro.org> X-Mailer: git-send-email 2.20.1 In-Reply-To: <20190704160802.12419-1-peter.maydell@linaro.org> References: <20190704160802.12419-1-peter.maydell@linaro.org> MIME-Version: 1.0 X-detected-operating-system: by eggs.gnu.org: Genre and OS details not recognized. X-Received-From: 2a00:1450:4864:20::444 Subject: [Qemu-devel] [PULL 7/9] hw/timer/armv7m_systick: Forbid non-privileged accesses X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.23 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: qemu-devel-bounces+patchwork-qemu-devel=patchwork.kernel.org@nongnu.org Sender: "Qemu-devel" X-Virus-Scanned: ClamAV using ClamSMTP Like most of the v7M memory mapped system registers, the systick registers are accessible to privileged code only and user accesses must generate a BusFault. We implement that for registers in the NVIC proper already, but missed it for systick since we implement it as a separate device. Correct the omission. Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson Reviewed-by: Philippe Mathieu-Daudé Message-id: 20190617175317.27557-6-peter.maydell@linaro.org --- hw/timer/armv7m_systick.c | 26 ++++++++++++++++++++------ 1 file changed, 20 insertions(+), 6 deletions(-) diff --git a/hw/timer/armv7m_systick.c b/hw/timer/armv7m_systick.c index a17317ce2fe..94640743b5d 100644 --- a/hw/timer/armv7m_systick.c +++ b/hw/timer/armv7m_systick.c @@ -75,11 +75,17 @@ static void systick_timer_tick(void *opaque) } } -static uint64_t systick_read(void *opaque, hwaddr addr, unsigned size) +static MemTxResult systick_read(void *opaque, hwaddr addr, uint64_t *data, + unsigned size, MemTxAttrs attrs) { SysTickState *s = opaque; uint32_t val; + if (attrs.user) { + /* Generate BusFault for unprivileged accesses */ + return MEMTX_ERROR; + } + switch (addr) { case 0x0: /* SysTick Control and Status. */ val = s->control; @@ -121,14 +127,21 @@ static uint64_t systick_read(void *opaque, hwaddr addr, unsigned size) } trace_systick_read(addr, val, size); - return val; + *data = val; + return MEMTX_OK; } -static void systick_write(void *opaque, hwaddr addr, - uint64_t value, unsigned size) +static MemTxResult systick_write(void *opaque, hwaddr addr, + uint64_t value, unsigned size, + MemTxAttrs attrs) { SysTickState *s = opaque; + if (attrs.user) { + /* Generate BusFault for unprivileged accesses */ + return MEMTX_ERROR; + } + trace_systick_write(addr, value, size); switch (addr) { @@ -172,11 +185,12 @@ static void systick_write(void *opaque, hwaddr addr, qemu_log_mask(LOG_GUEST_ERROR, "SysTick: Bad write offset 0x%" HWADDR_PRIx "\n", addr); } + return MEMTX_OK; } static const MemoryRegionOps systick_ops = { - .read = systick_read, - .write = systick_write, + .read_with_attrs = systick_read, + .write_with_attrs = systick_write, .endianness = DEVICE_NATIVE_ENDIAN, .valid.min_access_size = 4, .valid.max_access_size = 4, From patchwork Thu Jul 4 16:08:01 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Peter Maydell X-Patchwork-Id: 11031723 Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id 588AB13BD for ; Thu, 4 Jul 2019 16:24:00 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 47D1D28A68 for ; Thu, 4 Jul 2019 16:24:00 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id 38EE528A6B; Thu, 4 Jul 2019 16:24:00 +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=-5.0 required=2.0 tests=BAYES_00,DKIM_INVALID, DKIM_SIGNED,MAILING_LIST_MULTI,RCVD_IN_DNSWL_MED autolearn=ham version=3.3.1 Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) (using TLSv1.2 with cipher AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by mail.wl.linuxfoundation.org (Postfix) with ESMTPS id C637D28A68 for ; Thu, 4 Jul 2019 16:23:59 +0000 (UTC) Received: from localhost ([::1]:47548 helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.86_2) (envelope-from ) id 1hj4Wd-0007NB-3v for patchwork-qemu-devel@patchwork.kernel.org; Thu, 04 Jul 2019 12:23:59 -0400 Received: from eggs.gnu.org ([2001:470:142:3::10]:44221) by lists.gnu.org with esmtp (Exim 4.86_2) (envelope-from ) id 1hj4Hd-00084K-W0 for qemu-devel@nongnu.org; Thu, 04 Jul 2019 12:08:31 -0400 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1hj4Hb-0002JM-Ve for qemu-devel@nongnu.org; Thu, 04 Jul 2019 12:08:29 -0400 Received: from mail-wm1-x342.google.com ([2a00:1450:4864:20::342]:50648) by eggs.gnu.org with esmtps (TLS1.0:RSA_AES_128_CBC_SHA1:16) (Exim 4.71) (envelope-from ) id 1hj4HU-00029z-9v for qemu-devel@nongnu.org; Thu, 04 Jul 2019 12:08:22 -0400 Received: by mail-wm1-x342.google.com with SMTP id n9so6260236wmi.0 for ; Thu, 04 Jul 2019 09:08:17 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linaro.org; s=google; h=from:to:subject:date:message-id:in-reply-to:references:mime-version :content-transfer-encoding; bh=jcWZDKHnpMlELavBPABwOL/us8df3sdUAODi66ZZKI0=; b=tSXv39r13iwNG0UMBVZxL5JOUyLSsMKDQbaRCGhSqkt1FWsOdMal/CvCbXCi2nLdxA idWGRwgkXZmjDwg/rLVNlcPxDj3HV69/HtdTYd36IouEMcclmRmJuta31v476WlV2xf6 noepUSQ7aq3jgHqUQiwG8qhl2bdvigoPnlKygNMdHaFRh6obu3sbs5TkmBAle5fIM1cC sqsJT0x3uWYmECCQwU69yHhauh8uudmSDzL0xQNXZDUaDpG3zMzFFupG+NK1PNyNToMD 6DuMUiOEKum2CB6NXGxsFsIJcLnAO0yugMs0my9qB7bpaDhcdlSkNrxtIXHbHna8DTjp /F1A== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:subject:date:message-id:in-reply-to :references:mime-version:content-transfer-encoding; bh=jcWZDKHnpMlELavBPABwOL/us8df3sdUAODi66ZZKI0=; b=hK1sMVyGX4KZ17v6oHpoLtRXuRH1ygM4Kurt0lZDol64Psw7usia7MpvRIfKraTWbp EUxqeGe+OLF2Va/GUAATmeVnwUuJBe7jsB+ElFiqDh4AVLVUcumGFHDp4sZ5sah1xgJA CoRa12o8u3NCTW81szeefNBAD6Q+UOibhx/mgXwg+wjwDiIeZxq3ELf/AeRMgsifEfOu vmM6xKGNpHOsLbwk2arL+KfADxFLBYOJdqgFwMtPsXogYPpbyPcz+40NxReGjhWL0qSW GVEkDuD5YVOcRdf5OBevs8zo7eP+dulBpIDVHJppOKaes+zcsczkgfl4B+STp1QNM04E BeUQ== X-Gm-Message-State: APjAAAUdOm6sSXEAdLp7amh9wctMXnALrm6ExqCdvHVUVKCJ+QSX9aYt D+7/cuWxQsCpTgRnqSsR/nLRVHyCg/NnAQ== X-Google-Smtp-Source: APXvYqzBX+EHenXYqytwBzWzz1kyfQkeD/L8LEBMOOgUEIk+aZ55TMN9OaUV0FRdIHrnlBnXaV/+EA== X-Received: by 2002:a05:600c:2189:: with SMTP id e9mr209600wme.56.1562256496744; Thu, 04 Jul 2019 09:08:16 -0700 (PDT) Received: from orth.archaic.org.uk (orth.archaic.org.uk. [81.2.115.148]) by smtp.gmail.com with ESMTPSA id s2sm3849690wmj.33.2019.07.04.09.08.15 for (version=TLS1_3 cipher=AEAD-AES256-GCM-SHA384 bits=256/256); Thu, 04 Jul 2019 09:08:16 -0700 (PDT) From: Peter Maydell To: qemu-devel@nongnu.org Date: Thu, 4 Jul 2019 17:08:01 +0100 Message-Id: <20190704160802.12419-9-peter.maydell@linaro.org> X-Mailer: git-send-email 2.20.1 In-Reply-To: <20190704160802.12419-1-peter.maydell@linaro.org> References: <20190704160802.12419-1-peter.maydell@linaro.org> MIME-Version: 1.0 X-detected-operating-system: by eggs.gnu.org: Genre and OS details not recognized. X-Received-From: 2a00:1450:4864:20::342 Subject: [Qemu-devel] [PULL 8/9] target/arm: Execute Thumb instructions when their condbits are 0xf X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.23 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: qemu-devel-bounces+patchwork-qemu-devel=patchwork.kernel.org@nongnu.org Sender: "Qemu-devel" X-Virus-Scanned: ClamAV using ClamSMTP Thumb instructions in an IT block are set up to be conditionally executed depending on a set of condition bits encoded into the IT bits of the CPSR/XPSR. The architecture specifies that if the condition bits are 0b1111 this means "always execute" (like 0b1110), not "never execute"; we were treating it as "never execute". (See the ConditionHolds() pseudocode in both the A-profile and M-profile Arm ARM.) This is a bit of an obscure corner case, because the only legal way to get to an 0b1111 set of condbits is to do an exception return which sets the XPSR/CPSR up that way. An IT instruction which encodes a condition sequence that would include an 0b1111 is UNPREDICTABLE, and for v8A the CONSTRAINED UNPREDICTABLE choices for such an IT insn are to NOP, UNDEF, or treat 0b1111 like 0b1110. Add a comment noting that we take the latter option. Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson Message-id: 20190617175317.27557-7-peter.maydell@linaro.org --- target/arm/translate.c | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/target/arm/translate.c b/target/arm/translate.c index a5d77234232..7853462b21b 100644 --- a/target/arm/translate.c +++ b/target/arm/translate.c @@ -11594,7 +11594,14 @@ static void disas_thumb_insn(DisasContext *s, uint32_t insn) gen_nop_hint(s, (insn >> 4) & 0xf); break; } - /* If Then. */ + /* + * IT (If-Then) + * + * Combinations of firstcond and mask which set up an 0b1111 + * condition are UNPREDICTABLE; we take the CONSTRAINED + * UNPREDICTABLE choice to treat 0b1111 the same as 0b1110, + * i.e. both meaning "execute always". + */ s->condexec_cond = (insn >> 4) & 0xe; s->condexec_mask = insn & 0x1f; /* No actual code generated for this insn, just setup state. */ @@ -12128,7 +12135,11 @@ static void thumb_tr_translate_insn(DisasContextBase *dcbase, CPUState *cpu) if (dc->condexec_mask && !thumb_insn_is_unconditional(dc, insn)) { uint32_t cond = dc->condexec_cond; - if (cond != 0x0e) { /* Skip conditional when condition is AL. */ + /* + * Conditionally skip the insn. Note that both 0xe and 0xf mean + * "always"; 0xf is not "never". + */ + if (cond < 0x0e) { arm_skip_unless(dc, cond); } } From patchwork Thu Jul 4 16:08:02 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Peter Maydell X-Patchwork-Id: 11031721 Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id 71AF91510 for ; Thu, 4 Jul 2019 16:20:28 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 6100D28745 for ; Thu, 4 Jul 2019 16:20:28 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id 5313928991; Thu, 4 Jul 2019 16:20:28 +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=-5.0 required=2.0 tests=BAYES_00,DKIM_INVALID, DKIM_SIGNED,MAILING_LIST_MULTI,RCVD_IN_DNSWL_MED autolearn=ham version=3.3.1 Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) (using TLSv1.2 with cipher AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by mail.wl.linuxfoundation.org (Postfix) with ESMTPS id 0252328745 for ; Thu, 4 Jul 2019 16:20:28 +0000 (UTC) Received: from localhost ([::1]:47522 helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.86_2) (envelope-from ) id 1hj4TD-0003j9-Cq for patchwork-qemu-devel@patchwork.kernel.org; Thu, 04 Jul 2019 12:20:27 -0400 Received: from eggs.gnu.org ([2001:470:142:3::10]:44219) by lists.gnu.org with esmtp (Exim 4.86_2) (envelope-from ) id 1hj4He-00084J-6X for qemu-devel@nongnu.org; Thu, 04 Jul 2019 12:08:31 -0400 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1hj4Hb-0002JO-VZ for qemu-devel@nongnu.org; Thu, 04 Jul 2019 12:08:29 -0400 Received: from mail-wr1-x433.google.com ([2a00:1450:4864:20::433]:38354) by eggs.gnu.org with esmtps (TLS1.0:RSA_AES_128_CBC_SHA1:16) (Exim 4.71) (envelope-from ) id 1hj4HU-0002AT-99 for qemu-devel@nongnu.org; Thu, 04 Jul 2019 12:08:22 -0400 Received: by mail-wr1-x433.google.com with SMTP id p11so1674609wro.5 for ; Thu, 04 Jul 2019 09:08:18 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linaro.org; s=google; h=from:to:subject:date:message-id:in-reply-to:references:mime-version :content-transfer-encoding; bh=Fw40v64gwndWG5ZTpcv6OGPGniw0mg+4+kLx37gJbPc=; b=dTf84enzxEGbbHLJlNjJx0ux0knDfC7GnEcWwI9MObcb/+zOtSF3K0hqeZpPy8pUOP MBNTjKxUpjzeCYA/vnK7z5R4ngqL9jpLEefbSxRqbWU9fcOtG1XN+ZCayspheymYLxk2 8jix/7o1bDY0XUdM2x7A8oHwnmvVC31SP8to4+PnHpZKyOhMYr8tba04//FBsqzaKmX7 mPYAADmyw4HlNNENTyk5fDKzyE7rED7nolQApYwEPHtCVFvQyfGnXZ7RXZleOsiTS1o9 reGqZZ+7cT+sMT1eMA+4sXxf9+/wqr/xb3Ce7mwXpIp5owlRDQYezqqsc3S/7I9frr4a VVNw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:subject:date:message-id:in-reply-to :references:mime-version:content-transfer-encoding; bh=Fw40v64gwndWG5ZTpcv6OGPGniw0mg+4+kLx37gJbPc=; b=WxsissNXbsvTUNFGTU5dm6UnCtP/EfMp8LRY+FKWUNh8C08c1/+mzdT1JV4WsxunuA GGcGnizHSWsduhBPtNMOqVgkeHp4Csv99pTWhAAQoihAsrTETyb4Q620R0HVz3TIOJ2H dtE/GWacpWHoxn4TsN96oLf3zBCeIfexzx8AONeRxA4aGE/GX4SklLvJ8RfiKn8swq/W 1KSOxYtj95k2GfTdxF8+LnfiNjOLW9Eeh3dMq36l5Q4S/2aiTnEn0iteq4QsgH3PNyQP pt/jOSY/ulaoPf7a74tzTe8ACjytvgPey7IXoNinV7+bTbfW/ZIFMcYP7FvaJyeTHwGY vR7A== X-Gm-Message-State: APjAAAW/1QptnS9qKYfd8hdFNUOjj7OpahDS4R0WqDNBCDwRkQqXDR/o ooCw7LDjcbZCMmqv3lSskGy3/IjC5Gwi9Q== X-Google-Smtp-Source: APXvYqzPwNfZzzg/bCwrawXMW/8qx7DknDMEQaGe5JK/lJFoUAQwRMQwko71w9tBERnxnJ0NTgbEMQ== X-Received: by 2002:a5d:5302:: with SMTP id e2mr33618128wrv.347.1562256497574; Thu, 04 Jul 2019 09:08:17 -0700 (PDT) Received: from orth.archaic.org.uk (orth.archaic.org.uk. [81.2.115.148]) by smtp.gmail.com with ESMTPSA id s2sm3849690wmj.33.2019.07.04.09.08.16 for (version=TLS1_3 cipher=AEAD-AES256-GCM-SHA384 bits=256/256); Thu, 04 Jul 2019 09:08:17 -0700 (PDT) From: Peter Maydell To: qemu-devel@nongnu.org Date: Thu, 4 Jul 2019 17:08:02 +0100 Message-Id: <20190704160802.12419-10-peter.maydell@linaro.org> X-Mailer: git-send-email 2.20.1 In-Reply-To: <20190704160802.12419-1-peter.maydell@linaro.org> References: <20190704160802.12419-1-peter.maydell@linaro.org> MIME-Version: 1.0 X-detected-operating-system: by eggs.gnu.org: Genre and OS details not recognized. X-Received-From: 2a00:1450:4864:20::433 Subject: [Qemu-devel] [PULL 9/9] target/arm: Correct VMOV_imm_dp handling of short vectors X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.23 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: qemu-devel-bounces+patchwork-qemu-devel=patchwork.kernel.org@nongnu.org Sender: "Qemu-devel" X-Virus-Scanned: ClamAV using ClamSMTP Coverity points out (CID 1402195) that the loop in trans_VMOV_imm_dp() that iterates over the destination registers in a short-vector VMOV accidentally throws away the returned updated register number from vfp_advance_dreg(). Add the missing assignment. (We got this correct in trans_VMOV_imm_sp().) Fixes: 18cf951af9a27ae573a Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson Message-id: 20190702105115.9465-1-peter.maydell@linaro.org --- target/arm/translate-vfp.inc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/target/arm/translate-vfp.inc.c b/target/arm/translate-vfp.inc.c index deaddb04421..092eb5ec53d 100644 --- a/target/arm/translate-vfp.inc.c +++ b/target/arm/translate-vfp.inc.c @@ -1971,7 +1971,7 @@ static bool trans_VMOV_imm_dp(DisasContext *s, arg_VMOV_imm_dp *a) /* Set up the operands for the next iteration */ veclen--; - vfp_advance_dreg(vd, delta_d); + vd = vfp_advance_dreg(vd, delta_d); } tcg_temp_free_i64(fd);