From patchwork Mon Jul 1 19:49:35 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: =?utf-8?q?Philippe_Mathieu-Daud=C3=A9?= X-Patchwork-Id: 11026725 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 5112F14F6 for ; Mon, 1 Jul 2019 21:00:19 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 422C226538 for ; Mon, 1 Jul 2019 21:00:19 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id 35B22286E2; Mon, 1 Jul 2019 21:00:19 +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.2 required=2.0 tests=BAYES_00,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 EF8FE26538 for ; Mon, 1 Jul 2019 21:00:17 +0000 (UTC) Received: from localhost ([::1]:45490 helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.86_2) (envelope-from ) id 1hi3PM-0001Tj-QX for patchwork-qemu-devel@patchwork.kernel.org; Mon, 01 Jul 2019 17:00:17 -0400 Received: from [209.51.188.92] (port=43080 helo=eggs.gnu.org) by lists.gnu.org with esmtp (Exim 4.86_2) (envelope-from ) id 1hi3AX-000809-2e for qemu-devel@nongnu.org; Mon, 01 Jul 2019 16:45:00 -0400 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1hi3AP-00083W-BD for qemu-devel@nongnu.org; Mon, 01 Jul 2019 16:44:52 -0400 Received: from [209.132.183.28] (port=46292 helo=mx1.redhat.com) by eggs.gnu.org with esmtps (TLS1.0:DHE_RSA_AES_256_CBC_SHA1:32) (Exim 4.71) (envelope-from ) id 1hi3AD-0007nS-6N; Mon, 01 Jul 2019 16:44:37 -0400 Received: from smtp.corp.redhat.com (int-mx01.intmail.prod.int.phx2.redhat.com [10.5.11.11]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mx1.redhat.com (Postfix) with ESMTPS id 79A8930BBE69; Mon, 1 Jul 2019 19:49:55 +0000 (UTC) Received: from x1w.redhat.com (unknown [10.40.205.170]) by smtp.corp.redhat.com (Postfix) with ESMTPS id 1D7E2379F; Mon, 1 Jul 2019 19:49:50 +0000 (UTC) From: =?utf-8?q?Philippe_Mathieu-Daud=C3=A9?= To: qemu-devel@nongnu.org Date: Mon, 1 Jul 2019 21:49:35 +0200 Message-Id: <20190701194942.10092-2-philmd@redhat.com> In-Reply-To: <20190701194942.10092-1-philmd@redhat.com> References: <20190701194942.10092-1-philmd@redhat.com> MIME-Version: 1.0 X-Scanned-By: MIMEDefang 2.79 on 10.5.11.11 X-Greylist: Sender IP whitelisted, not delayed by milter-greylist-4.5.16 (mx1.redhat.com [10.5.110.49]); Mon, 01 Jul 2019 19:50:00 +0000 (UTC) X-detected-operating-system: by eggs.gnu.org: GNU/Linux 2.2.x-3.x [generic] X-Received-From: 209.132.183.28 Subject: [Qemu-devel] [PATCH v4 1/8] 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: , Cc: Yang Zhong , Peter Maydell , Andrew Jones , Samuel Ortiz , Rob Bradford , =?utf-8?q?Philippe_Mathieu-Daud?= =?utf-8?q?=C3=A9?= , Thomas Huth , qemu-arm@nongnu.org, Paolo Bonzini , =?utf-8?q?Alex_Be?= =?utf-8?q?nn=C3=A9e?= Errors-To: qemu-devel-bounces+patchwork-qemu-devel=patchwork.kernel.org@nongnu.org Sender: "Qemu-devel" X-Virus-Scanned: ClamAV using ClamSMTP These routines are TCG specific. Signed-off-by: Philippe Mathieu-Daudé --- v4: get_phys_page_attrs_debug is not TCG specific (Peter) --- 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 5c154f01c5..294433da88 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 f21261c8ff..ca718fb38f 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 0000000000..dde80273ff --- /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 9850993c11..1ab91f915e 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 Mon Jul 1 19:49:36 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: =?utf-8?q?Philippe_Mathieu-Daud=C3=A9?= X-Patchwork-Id: 11027007 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 39885112C for ; Tue, 2 Jul 2019 01:43:43 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 294B828845 for ; Tue, 2 Jul 2019 01:43:43 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id 1D0452885F; Tue, 2 Jul 2019 01:43:43 +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.2 required=2.0 tests=BAYES_00,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 B341E28845 for ; Tue, 2 Jul 2019 01:43:42 +0000 (UTC) Received: from localhost ([::1]:46998 helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.86_2) (envelope-from ) id 1hi7pe-0006sO-4O for patchwork-qemu-devel@patchwork.kernel.org; Mon, 01 Jul 2019 21:43:42 -0400 Received: from eggs.gnu.org ([2001:470:142:3::10]:47082) by lists.gnu.org with esmtp (Exim 4.86_2) (envelope-from ) id 1hi3xH-0000WT-AE for qemu-devel@nongnu.org; Mon, 01 Jul 2019 17:35:20 -0400 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1hi3xF-0002Ec-4o for qemu-devel@nongnu.org; Mon, 01 Jul 2019 17:35:19 -0400 Received: from mx1.redhat.com ([209.132.183.28]:50126) by eggs.gnu.org with esmtps (TLS1.0:DHE_RSA_AES_256_CBC_SHA1:32) (Exim 4.71) (envelope-from ) id 1hi3xB-00021g-GE; Mon, 01 Jul 2019 17:35:13 -0400 Received: from smtp.corp.redhat.com (int-mx01.intmail.prod.int.phx2.redhat.com [10.5.11.11]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mx1.redhat.com (Postfix) with ESMTPS id E399630C3192; Mon, 1 Jul 2019 19:49:59 +0000 (UTC) Received: from x1w.redhat.com (unknown [10.40.205.170]) by smtp.corp.redhat.com (Postfix) with ESMTPS id F2ABC87A0; Mon, 1 Jul 2019 19:49:55 +0000 (UTC) From: =?utf-8?q?Philippe_Mathieu-Daud=C3=A9?= To: qemu-devel@nongnu.org Date: Mon, 1 Jul 2019 21:49:36 +0200 Message-Id: <20190701194942.10092-3-philmd@redhat.com> In-Reply-To: <20190701194942.10092-1-philmd@redhat.com> References: <20190701194942.10092-1-philmd@redhat.com> MIME-Version: 1.0 X-Scanned-By: MIMEDefang 2.79 on 10.5.11.11 X-Greylist: Sender IP whitelisted, not delayed by milter-greylist-4.5.16 (mx1.redhat.com [10.5.110.40]); Mon, 01 Jul 2019 19:50:00 +0000 (UTC) X-detected-operating-system: by eggs.gnu.org: GNU/Linux 2.2.x-3.x [generic] X-Received-From: 209.132.183.28 Subject: [Qemu-devel] [PATCH v4 2/8] 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: , Cc: Yang Zhong , Peter Maydell , Andrew Jones , Samuel Ortiz , Rob Bradford , =?utf-8?q?Philippe_Mathieu-Daud?= =?utf-8?q?=C3=A9?= , Thomas Huth , qemu-arm@nongnu.org, Paolo Bonzini , =?utf-8?q?Alex_Be?= =?utf-8?q?nn=C3=A9e?= Errors-To: qemu-devel-bounces+patchwork-qemu-devel=patchwork.kernel.org@nongnu.org Sender: "Qemu-devel" X-Virus-Scanned: ClamAV using ClamSMTP 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é Reviewed-by: Peter Maydell --- v3: inline call to g_assert_not_reached if !TCG v4: fixed some ifdef that had moved in the next patch (Peter) --- 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 294433da88..82bedefc3d 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 a9be18660f..94c990cddb 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 38b73430cb..6d91e0a1f9 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-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 Mon Jul 1 19:49:37 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: =?utf-8?q?Philippe_Mathieu-Daud=C3=A9?= X-Patchwork-Id: 11026923 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 99A261510 for ; Mon, 1 Jul 2019 22:13:06 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 7438F28751 for ; Mon, 1 Jul 2019 22:13:06 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id 66A50287D1; Mon, 1 Jul 2019 22:13: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.2 required=2.0 tests=BAYES_00,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 45BA728751 for ; Mon, 1 Jul 2019 22:12:58 +0000 (UTC) Received: from localhost ([::1]:45968 helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.86_2) (envelope-from ) id 1hi4Xh-00046Q-5y for patchwork-qemu-devel@patchwork.kernel.org; Mon, 01 Jul 2019 18:12:57 -0400 Received: from eggs.gnu.org ([2001:470:142:3::10]:56982) by lists.gnu.org with esmtp (Exim 4.86_2) (envelope-from ) id 1hi3Y9-0000K0-5i for qemu-devel@nongnu.org; Mon, 01 Jul 2019 17:09:32 -0400 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1hi3Xx-0006iQ-47 for qemu-devel@nongnu.org; Mon, 01 Jul 2019 17:09:21 -0400 Received: from mx1.redhat.com ([209.132.183.28]:56246) by eggs.gnu.org with esmtps (TLS1.0:DHE_RSA_AES_256_CBC_SHA1:32) (Exim 4.71) (envelope-from ) id 1hi3XT-0006R9-LP; Mon, 01 Jul 2019 17:08:40 -0400 Received: from smtp.corp.redhat.com (int-mx01.intmail.prod.int.phx2.redhat.com [10.5.11.11]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mx1.redhat.com (Postfix) with ESMTPS id B6067223862; Mon, 1 Jul 2019 19:50:06 +0000 (UTC) Received: from x1w.redhat.com (unknown [10.40.205.170]) by smtp.corp.redhat.com (Postfix) with ESMTPS id 7F4966C35B; Mon, 1 Jul 2019 19:50:00 +0000 (UTC) From: =?utf-8?q?Philippe_Mathieu-Daud=C3=A9?= To: qemu-devel@nongnu.org Date: Mon, 1 Jul 2019 21:49:37 +0200 Message-Id: <20190701194942.10092-4-philmd@redhat.com> In-Reply-To: <20190701194942.10092-1-philmd@redhat.com> References: <20190701194942.10092-1-philmd@redhat.com> MIME-Version: 1.0 X-Scanned-By: MIMEDefang 2.79 on 10.5.11.11 X-Greylist: Sender IP whitelisted, not delayed by milter-greylist-4.5.16 (mx1.redhat.com [10.5.110.39]); Mon, 01 Jul 2019 19:50:10 +0000 (UTC) X-detected-operating-system: by eggs.gnu.org: GNU/Linux 2.2.x-3.x [generic] X-Received-From: 209.132.183.28 Subject: [Qemu-devel] [PATCH v4 3/8] 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: , Cc: Yang Zhong , Peter Maydell , Andrew Jones , Samuel Ortiz , Rob Bradford , =?utf-8?q?Philippe_Mathieu-Daud?= =?utf-8?q?=C3=A9?= , Thomas Huth , qemu-arm@nongnu.org, Paolo Bonzini , =?utf-8?q?Alex_Be?= =?utf-8?q?nn=C3=A9e?= Errors-To: qemu-devel-bounces+patchwork-qemu-devel=patchwork.kernel.org@nongnu.org Sender: "Qemu-devel" X-Virus-Scanned: ClamAV using ClamSMTP 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é --- patch generated with git diff --histogram. v4: rebased --- target/arm/Makefile.objs | 1 + target/arm/helper.c | 2661 +------------------------------------ target/arm/m_helper.c | 2676 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 2681 insertions(+), 2657 deletions(-) create mode 100644 target/arm/m_helper.c diff --git a/target/arm/Makefile.objs b/target/arm/Makefile.objs index 82bedefc3d..89cd7c36e3 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-$(CONFIG_ARM_V7M) += m_helper.o obj-$(CONFIG_SOFTMMU) += psci.o diff --git a/target/arm/helper.c b/target/arm/helper.c index 6d91e0a1f9..7ce01f4398 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 @@ -12731,489 +10590,6 @@ bool get_phys_addr(CPUARMState *env, target_ulong address, } } -hwaddr arm_cpu_get_phys_page_attrs_debug(CPUState *cs, vaddr addr, - MemTxAttrs *attrs) -{ - ARMCPU *cpu = ARM_CPU(cs); - CPUARMState *env = &cpu->env; - hwaddr phys_addr; - target_ulong page_size; - int prot; - bool ret; - ARMMMUFaultInfo fi = {}; - ARMMMUIdx mmu_idx = arm_mmu_idx(env); - - *attrs = (MemTxAttrs) {}; - - ret = get_phys_addr(env, addr, 0, mmu_idx, &phys_addr, - attrs, &prot, &page_size, &fi, NULL); - - if (ret) { - return -1; - } - 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 +10954,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 0000000000..1b0ad95a05 --- /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 Mon Jul 1 19:49:38 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: =?utf-8?q?Philippe_Mathieu-Daud=C3=A9?= X-Patchwork-Id: 11026925 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 3C45A14F6 for ; Mon, 1 Jul 2019 22:15:38 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 305F626E55 for ; Mon, 1 Jul 2019 22:15:38 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id 21A6327F54; Mon, 1 Jul 2019 22:15:38 +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.2 required=2.0 tests=BAYES_00,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 BA37626E55 for ; Mon, 1 Jul 2019 22:15:37 +0000 (UTC) Received: from localhost ([::1]:46002 helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.86_2) (envelope-from ) id 1hi4aH-0006Nr-2a for patchwork-qemu-devel@patchwork.kernel.org; Mon, 01 Jul 2019 18:15:37 -0400 Received: from eggs.gnu.org ([2001:470:142:3::10]:54780) by lists.gnu.org with esmtp (Exim 4.86_2) (envelope-from ) id 1hi3WG-0006k1-Ph for qemu-devel@nongnu.org; Mon, 01 Jul 2019 17:07:25 -0400 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1hi3WF-0005bj-Mj for qemu-devel@nongnu.org; Mon, 01 Jul 2019 17:07:24 -0400 Received: from mx1.redhat.com ([209.132.183.28]:33570) by eggs.gnu.org with esmtps (TLS1.0:DHE_RSA_AES_256_CBC_SHA1:32) (Exim 4.71) (envelope-from ) id 1hi3WC-0005Tx-7Y; Mon, 01 Jul 2019 17:07:20 -0400 Received: from smtp.corp.redhat.com (int-mx01.intmail.prod.int.phx2.redhat.com [10.5.11.11]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mx1.redhat.com (Postfix) with ESMTPS id 4BFAB3162916; Mon, 1 Jul 2019 19:50:14 +0000 (UTC) Received: from x1w.redhat.com (unknown [10.40.205.170]) by smtp.corp.redhat.com (Postfix) with ESMTPS id 344429089; Mon, 1 Jul 2019 19:50:06 +0000 (UTC) From: =?utf-8?q?Philippe_Mathieu-Daud=C3=A9?= To: qemu-devel@nongnu.org Date: Mon, 1 Jul 2019 21:49:38 +0200 Message-Id: <20190701194942.10092-5-philmd@redhat.com> In-Reply-To: <20190701194942.10092-1-philmd@redhat.com> References: <20190701194942.10092-1-philmd@redhat.com> MIME-Version: 1.0 X-Scanned-By: MIMEDefang 2.79 on 10.5.11.11 X-Greylist: Sender IP whitelisted, not delayed by milter-greylist-4.5.16 (mx1.redhat.com [10.5.110.41]); Mon, 01 Jul 2019 19:50:14 +0000 (UTC) X-detected-operating-system: by eggs.gnu.org: GNU/Linux 2.2.x-3.x [generic] X-Received-From: 209.132.183.28 Subject: [Qemu-devel] [PATCH v4 4/8] RFC target/arm: Restrict pre-ARMv7 cpus 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: , Cc: Yang Zhong , Peter Maydell , Andrew Jones , Samuel Ortiz , Rob Bradford , =?utf-8?q?Philippe_Mathieu-Daud?= =?utf-8?q?=C3=A9?= , Thomas Huth , qemu-arm@nongnu.org, Paolo Bonzini , =?utf-8?q?Alex_Be?= =?utf-8?q?nn=C3=A9e?= Errors-To: qemu-devel-bounces+patchwork-qemu-devel=patchwork.kernel.org@nongnu.org Sender: "Qemu-devel" X-Virus-Scanned: ClamAV using ClamSMTP KVM requires at least a ARMv7 cpu. Signed-off-by: Philippe Mathieu-Daudé --- target/arm/cpu.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/target/arm/cpu.c b/target/arm/cpu.c index ca718fb38f..290ef16e52 100644 --- a/target/arm/cpu.c +++ b/target/arm/cpu.c @@ -1643,6 +1643,8 @@ static ObjectClass *arm_cpu_class_by_name(const char *cpu_model) /* CPU models. These are not needed for the AArch64 linux-user build. */ #if !defined(CONFIG_USER_ONLY) || !defined(TARGET_AARCH64) +#ifdef CONFIG_TCG + static void arm926_initfn(Object *obj) { ARMCPU *cpu = ARM_CPU(obj); @@ -1853,6 +1855,8 @@ static void cortex_m0_initfn(Object *obj) cpu->midr = 0x410cc200; } +#endif + static void cortex_m3_initfn(Object *obj) { ARMCPU *cpu = ARM_CPU(obj); @@ -2234,6 +2238,8 @@ static void cortex_a15_initfn(Object *obj) define_arm_cp_regs(cpu, cortexa15_cp_reginfo); } +#ifdef CONFIG_TCG + static void ti925t_initfn(Object *obj) { ARMCPU *cpu = ARM_CPU(obj); @@ -2402,6 +2408,8 @@ static void pxa270c5_initfn(Object *obj) cpu->reset_sctlr = 0x00000078; } +#endif + #ifndef TARGET_AARCH64 /* -cpu max: if KVM is enabled, like -cpu host (best possible with this host); * otherwise, a CPU with as many features enabled as our emulation supports. @@ -2470,6 +2478,7 @@ struct ARMCPUInfo { static const ARMCPUInfo arm_cpus[] = { #if !defined(CONFIG_USER_ONLY) || !defined(TARGET_AARCH64) +#ifdef CONFIG_TCG { .name = "arm926", .initfn = arm926_initfn }, { .name = "arm946", .initfn = arm946_initfn }, { .name = "arm1026", .initfn = arm1026_initfn }, @@ -2482,6 +2491,7 @@ static const ARMCPUInfo arm_cpus[] = { { .name = "arm1176", .initfn = arm1176_initfn }, { .name = "arm11mpcore", .initfn = arm11mpcore_initfn }, { .name = "cortex-m0", .initfn = cortex_m0_initfn, +#endif .class_init = arm_v7m_class_init }, { .name = "cortex-m3", .initfn = cortex_m3_initfn, .class_init = arm_v7m_class_init }, @@ -2495,6 +2505,7 @@ static const ARMCPUInfo arm_cpus[] = { { .name = "cortex-a8", .initfn = cortex_a8_initfn }, { .name = "cortex-a9", .initfn = cortex_a9_initfn }, { .name = "cortex-a15", .initfn = cortex_a15_initfn }, +#ifdef CONFIG_TCG { .name = "ti925t", .initfn = ti925t_initfn }, { .name = "sa1100", .initfn = sa1100_initfn }, { .name = "sa1110", .initfn = sa1110_initfn }, @@ -2511,6 +2522,7 @@ static const ARMCPUInfo arm_cpus[] = { { .name = "pxa270-b1", .initfn = pxa270b1_initfn }, { .name = "pxa270-c0", .initfn = pxa270c0_initfn }, { .name = "pxa270-c5", .initfn = pxa270c5_initfn }, +#endif #ifndef TARGET_AARCH64 { .name = "max", .initfn = arm_max_initfn }, #endif From patchwork Mon Jul 1 19:49:39 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: =?utf-8?q?Philippe_Mathieu-Daud=C3=A9?= X-Patchwork-Id: 11026983 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 D410A112C for ; Tue, 2 Jul 2019 01:27:08 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id BAB202874B for ; Tue, 2 Jul 2019 01:27:08 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id AC33F2876C; Tue, 2 Jul 2019 01:27:08 +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.2 required=2.0 tests=BAYES_00,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 4212F2874B for ; Tue, 2 Jul 2019 01:27:08 +0000 (UTC) Received: from localhost ([::1]:46830 helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.86_2) (envelope-from ) id 1hi7Zb-0006gv-JA for patchwork-qemu-devel@patchwork.kernel.org; Mon, 01 Jul 2019 21:27:07 -0400 Received: from eggs.gnu.org ([2001:470:142:3::10]:47114) by lists.gnu.org with esmtp (Exim 4.86_2) (envelope-from ) id 1hi3xK-0000gV-EM for qemu-devel@nongnu.org; Mon, 01 Jul 2019 17:35:23 -0400 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1hi3xI-0002Hl-4Q for qemu-devel@nongnu.org; Mon, 01 Jul 2019 17:35:22 -0400 Received: from mx1.redhat.com ([209.132.183.28]:46562) by eggs.gnu.org with esmtps (TLS1.0:DHE_RSA_AES_256_CBC_SHA1:32) (Exim 4.71) (envelope-from ) id 1hi3xC-00029M-8Y; Mon, 01 Jul 2019 17:35:14 -0400 Received: from smtp.corp.redhat.com (int-mx01.intmail.prod.int.phx2.redhat.com [10.5.11.11]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mx1.redhat.com (Postfix) with ESMTPS id A7BFE3082E72; Mon, 1 Jul 2019 19:50:17 +0000 (UTC) Received: from x1w.redhat.com (unknown [10.40.205.170]) by smtp.corp.redhat.com (Postfix) with ESMTPS id B8E0D379F; Mon, 1 Jul 2019 19:50:14 +0000 (UTC) From: =?utf-8?q?Philippe_Mathieu-Daud=C3=A9?= To: qemu-devel@nongnu.org Date: Mon, 1 Jul 2019 21:49:39 +0200 Message-Id: <20190701194942.10092-6-philmd@redhat.com> In-Reply-To: <20190701194942.10092-1-philmd@redhat.com> References: <20190701194942.10092-1-philmd@redhat.com> MIME-Version: 1.0 X-Scanned-By: MIMEDefang 2.79 on 10.5.11.11 X-Greylist: Sender IP whitelisted, not delayed by milter-greylist-4.5.16 (mx1.redhat.com [10.5.110.46]); Mon, 01 Jul 2019 19:50:17 +0000 (UTC) X-detected-operating-system: by eggs.gnu.org: GNU/Linux 2.2.x-3.x [generic] X-Received-From: 209.132.183.28 Subject: [Qemu-devel] [PATCH v4 5/8] RFC target/arm: Do not build pre-ARMv7 cpus when using KVM 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: , Cc: Yang Zhong , Peter Maydell , Andrew Jones , Samuel Ortiz , Rob Bradford , =?utf-8?q?Philippe_Mathieu-Daud?= =?utf-8?q?=C3=A9?= , Thomas Huth , qemu-arm@nongnu.org, Paolo Bonzini , =?utf-8?q?Alex_Be?= =?utf-8?q?nn=C3=A9e?= Errors-To: qemu-devel-bounces+patchwork-qemu-devel=patchwork.kernel.org@nongnu.org Sender: "Qemu-devel" X-Virus-Scanned: ClamAV using ClamSMTP A KVM-only build won't be able to run pre-ARMv7 cpus, disable them. If KVM is not enabled, they are enabled by default. Signed-off-by: Philippe Mathieu-Daudé --- Sadly this does not work with --enable-tcg --enable-kvm dual config. --- default-configs/arm-softmmu.mak | 33 ++++++++++++++++----------------- hw/arm/Kconfig | 26 ++++++++++++++++++++++++++ 2 files changed, 42 insertions(+), 17 deletions(-) diff --git a/default-configs/arm-softmmu.mak b/default-configs/arm-softmmu.mak index 1f2e0e7fde..081d507c87 100644 --- a/default-configs/arm-softmmu.mak +++ b/default-configs/arm-softmmu.mak @@ -9,34 +9,33 @@ CONFIG_ARM_V7M=y CONFIG_ARM_VIRT=y CONFIG_CUBIEBOARD=y CONFIG_EXYNOS4=y -CONFIG_HIGHBANK=y -CONFIG_INTEGRATOR=y CONFIG_FSL_IMX31=y -CONFIG_MUSICPAL=y CONFIG_MUSCA=y -CONFIG_CHEETAH=y -CONFIG_SX1=y -CONFIG_NSERIES=y CONFIG_STELLARIS=y CONFIG_REALVIEW=y -CONFIG_VERSATILE=y CONFIG_VEXPRESS=y CONFIG_ZYNQ=y -CONFIG_MAINSTONE=y -CONFIG_GUMSTIX=y -CONFIG_SPITZ=y -CONFIG_TOSA=y -CONFIG_Z2=y -CONFIG_COLLIE=y -CONFIG_ASPEED_SOC=y CONFIG_NETDUINO2=y CONFIG_MPS2=y CONFIG_RASPI=y -CONFIG_DIGIC=y CONFIG_SABRELITE=y CONFIG_EMCRAFT_SF2=y -CONFIG_MICROBIT=y -CONFIG_FSL_IMX25=y CONFIG_FSL_IMX7=y CONFIG_FSL_IMX6UL=y CONFIG_SEMIHOSTING=y +#CONFIG_CHEETAH=y +#CONFIG_SX1=y +#CONFIG_DIGIC=y +#CONFIG_INTEGRATOR=y +#CONFIG_MUSICPAL=y +#CONFIG_MAINSTONE=y +#CONFIG_GUMSTIX=y +#CONFIG_SPITZ=y +#CONFIG_TOSA=y +#CONFIG_COLLIE=y +#CONFIG_VERSATILE=y +#CONFIG_FSL_IMX25=y +#CONFIG_ASPEED_SOC=y +#CONFIG_NSERIES=y +#CONFIG_HIGHBANK=n +#CONFIG_MICROBIT=n diff --git a/hw/arm/Kconfig b/hw/arm/Kconfig index ab65ecd216..b4d31c6026 100644 --- a/hw/arm/Kconfig +++ b/hw/arm/Kconfig @@ -1,3 +1,18 @@ +config ARM_V4 + default y + depends on !KVM + bool + +config ARM_V5 + default y + depends on !KVM + bool + +config ARM_V6 + default y + depends on !KVM + bool + config ARM_VIRT bool imply PCI_DEVICES @@ -23,6 +38,7 @@ config ARM_VIRT config CHEETAH bool + select ARM_V4 select OMAP select TSC210X @@ -32,6 +48,7 @@ config CUBIEBOARD config DIGIC bool + select ARM_V5 select PTIMER select PFLASH_CFI02 @@ -61,6 +78,7 @@ config HIGHBANK config INTEGRATOR bool + select ARM_V5 select ARM_TIMER select INTEGRATOR_DEBUG select PL011 # UART @@ -84,6 +102,7 @@ config MUSCA config MUSICPAL bool + select ARM_V5 select BITBANG_I2C select MARVELL_88W8618 select PTIMER @@ -97,6 +116,7 @@ config NETDUINO2 config NSERIES bool + select ARM_V6 select OMAP select TMP105 # tempature sensor select BLIZZARD # LCD/TV controller @@ -119,6 +139,7 @@ config OMAP config PXA2XX bool + select ARM_V5 select FRAMEBUFFER select I2C select SERIAL @@ -229,10 +250,12 @@ config COLLIE config SX1 bool + select ARM_V4 select OMAP config VERSATILE bool + select ARM_V5 select ARM_TIMER # sp804 select PFLASH_CFI01 select LSI_SCSI_PCI @@ -321,6 +344,7 @@ config XLNX_VERSAL config FSL_IMX25 bool + select ARM_V5 select IMX select IMX_FEC select IMX_I2C @@ -328,6 +352,7 @@ config FSL_IMX25 config FSL_IMX31 bool + select ARM_V6 select SERIAL select IMX select IMX_I2C @@ -343,6 +368,7 @@ config FSL_IMX6 config ASPEED_SOC bool + select ARM_V5 select DS1338 select FTGMAC100 select I2C From patchwork Mon Jul 1 19:49:40 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: =?utf-8?q?Philippe_Mathieu-Daud=C3=A9?= X-Patchwork-Id: 11026987 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 E236714F6 for ; Tue, 2 Jul 2019 01:27:54 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id CA2D92874B for ; Tue, 2 Jul 2019 01:27:54 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id BB38D2876C; Tue, 2 Jul 2019 01:27:54 +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.2 required=2.0 tests=BAYES_00,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 999762874B for ; Tue, 2 Jul 2019 01:27:53 +0000 (UTC) Received: from localhost ([::1]:46838 helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.86_2) (envelope-from ) id 1hi7aK-0007LN-QQ for patchwork-qemu-devel@patchwork.kernel.org; Mon, 01 Jul 2019 21:27:52 -0400 Received: from eggs.gnu.org ([2001:470:142:3::10]:44739) by lists.gnu.org with esmtp (Exim 4.86_2) (envelope-from ) id 1hi3rd-0005Iv-DI for qemu-devel@nongnu.org; Mon, 01 Jul 2019 17:29:30 -0400 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1hi3rc-0008Rg-Ct for qemu-devel@nongnu.org; Mon, 01 Jul 2019 17:29:29 -0400 Received: from mx1.redhat.com ([209.132.183.28]:36895) by eggs.gnu.org with esmtps (TLS1.0:DHE_RSA_AES_256_CBC_SHA1:32) (Exim 4.71) (envelope-from ) id 1hi3rZ-0008No-DL; Mon, 01 Jul 2019 17:29:25 -0400 Received: from smtp.corp.redhat.com (int-mx01.intmail.prod.int.phx2.redhat.com [10.5.11.11]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mx1.redhat.com (Postfix) with ESMTPS id 1F18630BBE98; Mon, 1 Jul 2019 19:50:21 +0000 (UTC) Received: from x1w.redhat.com (unknown [10.40.205.170]) by smtp.corp.redhat.com (Postfix) with ESMTPS id 2B6CF379F; Mon, 1 Jul 2019 19:50:17 +0000 (UTC) From: =?utf-8?q?Philippe_Mathieu-Daud=C3=A9?= To: qemu-devel@nongnu.org Date: Mon, 1 Jul 2019 21:49:40 +0200 Message-Id: <20190701194942.10092-7-philmd@redhat.com> In-Reply-To: <20190701194942.10092-1-philmd@redhat.com> References: <20190701194942.10092-1-philmd@redhat.com> MIME-Version: 1.0 X-Scanned-By: MIMEDefang 2.79 on 10.5.11.11 X-Greylist: Sender IP whitelisted, not delayed by milter-greylist-4.5.16 (mx1.redhat.com [10.5.110.49]); Mon, 01 Jul 2019 19:50:21 +0000 (UTC) X-detected-operating-system: by eggs.gnu.org: GNU/Linux 2.2.x-3.x [generic] X-Received-From: 209.132.183.28 Subject: [Qemu-devel] [PATCH v4 6/8] RFC target/arm: Restrict R and M profiles 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: , Cc: Yang Zhong , Peter Maydell , Andrew Jones , Samuel Ortiz , Rob Bradford , =?utf-8?q?Philippe_Mathieu-Daud?= =?utf-8?q?=C3=A9?= , Thomas Huth , qemu-arm@nongnu.org, Paolo Bonzini , =?utf-8?q?Alex_Be?= =?utf-8?q?nn=C3=A9e?= Errors-To: qemu-devel-bounces+patchwork-qemu-devel=patchwork.kernel.org@nongnu.org Sender: "Qemu-devel" X-Virus-Scanned: ClamAV using ClamSMTP KVM is only able to run A profile cpus. Signed-off-by: Philippe Mathieu-Daudé --- target/arm/cpu.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/target/arm/cpu.c b/target/arm/cpu.c index 290ef16e52..a0934a47ee 100644 --- a/target/arm/cpu.c +++ b/target/arm/cpu.c @@ -453,7 +453,9 @@ bool arm_cpu_exec_interrupt(CPUState *cs, int interrupt_request) return ret; } -#if !defined(CONFIG_USER_ONLY) || !defined(TARGET_AARCH64) +/* CPU models. These are not needed for the AArch64 linux-user build. */ +#if (!defined(CONFIG_USER_ONLY) || !defined(TARGET_AARCH64)) \ + && defined(CONFIG_TCG) static bool arm_v7m_cpu_exec_interrupt(CPUState *cs, int interrupt_request) { CPUClass *cc = CPU_GET_CLASS(cs); @@ -1855,8 +1857,6 @@ static void cortex_m0_initfn(Object *obj) cpu->midr = 0x410cc200; } -#endif - static void cortex_m3_initfn(Object *obj) { ARMCPU *cpu = ARM_CPU(obj); @@ -2010,6 +2010,8 @@ static void cortex_r5f_initfn(Object *obj) cpu->isar.mvfr1 = 0x00000011; } +#endif + static const ARMCPRegInfo cortexa8_cp_reginfo[] = { { .name = "L2LOCKDOWN", .cp = 15, .crn = 9, .crm = 0, .opc1 = 1, .opc2 = 0, .access = PL1_RW, .type = ARM_CP_CONST, .resetvalue = 0 }, @@ -2491,7 +2493,6 @@ static const ARMCPUInfo arm_cpus[] = { { .name = "arm1176", .initfn = arm1176_initfn }, { .name = "arm11mpcore", .initfn = arm11mpcore_initfn }, { .name = "cortex-m0", .initfn = cortex_m0_initfn, -#endif .class_init = arm_v7m_class_init }, { .name = "cortex-m3", .initfn = cortex_m3_initfn, .class_init = arm_v7m_class_init }, @@ -2501,6 +2502,7 @@ static const ARMCPUInfo arm_cpus[] = { .class_init = arm_v7m_class_init }, { .name = "cortex-r5", .initfn = cortex_r5_initfn }, { .name = "cortex-r5f", .initfn = cortex_r5f_initfn }, +#endif { .name = "cortex-a7", .initfn = cortex_a7_initfn }, { .name = "cortex-a8", .initfn = cortex_a8_initfn }, { .name = "cortex-a9", .initfn = cortex_a9_initfn }, From patchwork Mon Jul 1 19:49:41 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: =?utf-8?q?Philippe_Mathieu-Daud=C3=A9?= X-Patchwork-Id: 11026931 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 749F8746 for ; Mon, 1 Jul 2019 22:22:48 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 5DDDF286CC for ; Mon, 1 Jul 2019 22:22:48 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id 4F2F6287D2; Mon, 1 Jul 2019 22:22:48 +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.2 required=2.0 tests=BAYES_00,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 E11ED286CC for ; Mon, 1 Jul 2019 22:22:47 +0000 (UTC) Received: from localhost ([::1]:46050 helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.86_2) (envelope-from ) id 1hi4hB-0002MQ-JK for patchwork-qemu-devel@patchwork.kernel.org; Mon, 01 Jul 2019 18:22:47 -0400 Received: from eggs.gnu.org ([2001:470:142:3::10]:56425) by lists.gnu.org with esmtp (Exim 4.86_2) (envelope-from ) id 1hi3XZ-00083Q-6I for qemu-devel@nongnu.org; Mon, 01 Jul 2019 17:08:46 -0400 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1hi3XY-0006UB-4W for qemu-devel@nongnu.org; Mon, 01 Jul 2019 17:08:45 -0400 Received: from mx1.redhat.com ([209.132.183.28]:11475) by eggs.gnu.org with esmtps (TLS1.0:DHE_RSA_AES_256_CBC_SHA1:32) (Exim 4.71) (envelope-from ) id 1hi3XT-0006RA-OL; Mon, 01 Jul 2019 17:08:39 -0400 Received: from smtp.corp.redhat.com (int-mx01.intmail.prod.int.phx2.redhat.com [10.5.11.11]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mx1.redhat.com (Postfix) with ESMTPS id B80E6223884; Mon, 1 Jul 2019 19:50:26 +0000 (UTC) Received: from x1w.redhat.com (unknown [10.40.205.170]) by smtp.corp.redhat.com (Postfix) with ESMTPS id 99A719089; Mon, 1 Jul 2019 19:50:21 +0000 (UTC) From: =?utf-8?q?Philippe_Mathieu-Daud=C3=A9?= To: qemu-devel@nongnu.org Date: Mon, 1 Jul 2019 21:49:41 +0200 Message-Id: <20190701194942.10092-8-philmd@redhat.com> In-Reply-To: <20190701194942.10092-1-philmd@redhat.com> References: <20190701194942.10092-1-philmd@redhat.com> MIME-Version: 1.0 X-Scanned-By: MIMEDefang 2.79 on 10.5.11.11 X-Greylist: Sender IP whitelisted, not delayed by milter-greylist-4.5.16 (mx1.redhat.com [10.5.110.39]); Mon, 01 Jul 2019 19:50:26 +0000 (UTC) X-detected-operating-system: by eggs.gnu.org: GNU/Linux 2.2.x-3.x [generic] X-Received-From: 209.132.183.28 Subject: [Qemu-devel] [PATCH v4 7/8] RFC target/arm: Do not build A/M-profile cpus when using KVM 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: , Cc: Yang Zhong , Peter Maydell , Andrew Jones , Samuel Ortiz , Rob Bradford , =?utf-8?q?Philippe_Mathieu-Daud?= =?utf-8?q?=C3=A9?= , Thomas Huth , qemu-arm@nongnu.org, Paolo Bonzini , =?utf-8?q?Alex_Be?= =?utf-8?q?nn=C3=A9e?= Errors-To: qemu-devel-bounces+patchwork-qemu-devel=patchwork.kernel.org@nongnu.org Sender: "Qemu-devel" X-Virus-Scanned: ClamAV using ClamSMTP A KVM-only build won't be able to run A or M-profile cpus, disable them. If KVM is not enabled, they are enabled by default. Signed-off-by: Philippe Mathieu-Daudé --- Sadly this does not work with --enable-tcg --enable-kvm dual config. --- default-configs/arm-softmmu.mak | 14 ++++++-------- hw/arm/Kconfig | 16 +++++++++++++--- 2 files changed, 19 insertions(+), 11 deletions(-) diff --git a/default-configs/arm-softmmu.mak b/default-configs/arm-softmmu.mak index 081d507c87..3995c4bb65 100644 --- a/default-configs/arm-softmmu.mak +++ b/default-configs/arm-softmmu.mak @@ -1,25 +1,18 @@ # Default configuration for arm-softmmu -# TODO: ARM_V7M is currently always required - make this more flexible! -CONFIG_ARM_V7M=y - # CONFIG_PCI_DEVICES=n # CONFIG_TEST_DEVICES=n +CONFIG_ARM_V7M=n CONFIG_ARM_VIRT=y CONFIG_CUBIEBOARD=y CONFIG_EXYNOS4=y CONFIG_FSL_IMX31=y -CONFIG_MUSCA=y -CONFIG_STELLARIS=y CONFIG_REALVIEW=y CONFIG_VEXPRESS=y CONFIG_ZYNQ=y -CONFIG_NETDUINO2=y -CONFIG_MPS2=y CONFIG_RASPI=y CONFIG_SABRELITE=y -CONFIG_EMCRAFT_SF2=y CONFIG_FSL_IMX7=y CONFIG_FSL_IMX6UL=y CONFIG_SEMIHOSTING=y @@ -38,4 +31,9 @@ CONFIG_SEMIHOSTING=y #CONFIG_ASPEED_SOC=y #CONFIG_NSERIES=y #CONFIG_HIGHBANK=n +#CONFIG_MUSCA=n +#CONFIG_STELLARIS=n +#CONFIG_NETDUINO2=n +#CONFIG_MPS2=n +#CONFIG_EMCRAFT_SF2=n #CONFIG_MICROBIT=n diff --git a/hw/arm/Kconfig b/hw/arm/Kconfig index b4d31c6026..f8242068ad 100644 --- a/hw/arm/Kconfig +++ b/hw/arm/Kconfig @@ -13,6 +13,18 @@ config ARM_V6 depends on !KVM bool +# ARM Microcontroller profile +config ARM_V7M + default y + depends on !KVM + bool + +# ARM Realtime profile +config ARM_V7R + default y + depends on !KVM + bool + config ARM_VIRT bool imply PCI_DEVICES @@ -295,9 +307,6 @@ config ZYNQ select XILINX_SPIPS select ZYNQ_DEVCFG -config ARM_V7M - bool - config ALLWINNER_A10 bool select AHCI @@ -323,6 +332,7 @@ config STM32F205_SOC config XLNX_ZYNQMP_ARM bool + select ARM_V7R select AHCI select ARM_GIC select CADENCE From patchwork Mon Jul 1 19:49:42 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: =?utf-8?q?Philippe_Mathieu-Daud=C3=A9?= X-Patchwork-Id: 11026773 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 D85AD13B1 for ; Mon, 1 Jul 2019 21:43:57 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id C878E28765 for ; Mon, 1 Jul 2019 21:43:57 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id B6C7E28786; Mon, 1 Jul 2019 21:43:57 +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.2 required=2.0 tests=BAYES_00,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 6251028765 for ; Mon, 1 Jul 2019 21:43:57 +0000 (UTC) Received: from localhost ([::1]:45766 helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.86_2) (envelope-from ) id 1hi45c-0002K1-F6 for patchwork-qemu-devel@patchwork.kernel.org; Mon, 01 Jul 2019 17:43:56 -0400 Received: from eggs.gnu.org ([2001:470:142:3::10]:54764) by lists.gnu.org with esmtp (Exim 4.86_2) (envelope-from ) id 1hi3WG-0006jM-Fd for qemu-devel@nongnu.org; Mon, 01 Jul 2019 17:07:25 -0400 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1hi3WF-0005bX-F6 for qemu-devel@nongnu.org; Mon, 01 Jul 2019 17:07:24 -0400 Received: from mx1.redhat.com ([209.132.183.28]:33562) by eggs.gnu.org with esmtps (TLS1.0:DHE_RSA_AES_256_CBC_SHA1:32) (Exim 4.71) (envelope-from ) id 1hi3WC-0005Tw-AM; Mon, 01 Jul 2019 17:07:20 -0400 Received: from smtp.corp.redhat.com (int-mx01.intmail.prod.int.phx2.redhat.com [10.5.11.11]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mx1.redhat.com (Postfix) with ESMTPS id 3BC63308A963; Mon, 1 Jul 2019 19:50:32 +0000 (UTC) Received: from x1w.redhat.com (unknown [10.40.205.170]) by smtp.corp.redhat.com (Postfix) with ESMTPS id 34BF8379F; Mon, 1 Jul 2019 19:50:26 +0000 (UTC) From: =?utf-8?q?Philippe_Mathieu-Daud=C3=A9?= To: qemu-devel@nongnu.org Date: Mon, 1 Jul 2019 21:49:42 +0200 Message-Id: <20190701194942.10092-9-philmd@redhat.com> In-Reply-To: <20190701194942.10092-1-philmd@redhat.com> References: <20190701194942.10092-1-philmd@redhat.com> MIME-Version: 1.0 X-Scanned-By: MIMEDefang 2.79 on 10.5.11.11 X-Greylist: Sender IP whitelisted, not delayed by milter-greylist-4.5.16 (mx1.redhat.com [10.5.110.41]); Mon, 01 Jul 2019 19:50:32 +0000 (UTC) X-detected-operating-system: by eggs.gnu.org: GNU/Linux 2.2.x-3.x [generic] X-Received-From: 209.132.183.28 Subject: [Qemu-devel] [PATCH v4 8/8] target/arm: Do not build TCG objects when TCG is off 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: , Cc: Yang Zhong , Peter Maydell , Andrew Jones , Samuel Ortiz , Rob Bradford , =?utf-8?q?Philippe_Mathieu-Daud?= =?utf-8?q?=C3=A9?= , Thomas Huth , qemu-arm@nongnu.org, Paolo Bonzini , =?utf-8?q?Alex_Be?= =?utf-8?q?nn=C3=A9e?= Errors-To: qemu-devel-bounces+patchwork-qemu-devel=patchwork.kernel.org@nongnu.org Sender: "Qemu-devel" X-Virus-Scanned: ClamAV using ClamSMTP We can now safely turn all TCG dependent build off when CONFIG_TCG is off. This allows building ARM binaries with --disable-tcg. Signed-off-by: Samuel Ortiz Signed-off-by: Philippe Mathieu-Daudé --- v3: complete rewrite of patch content, removed R-b tags --- target/arm/Makefile.objs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/target/arm/Makefile.objs b/target/arm/Makefile.objs index 89cd7c36e3..c8bca74a63 100644 --- a/target/arm/Makefile.objs +++ b/target/arm/Makefile.objs @@ -32,6 +32,8 @@ 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 +ifeq ($(CONFIG_TCG),y) + obj-y += tlb_helper.o debug_helper.o obj-y += translate.o op_helper.o obj-y += crypto_helper.o @@ -43,3 +45,5 @@ obj-$(CONFIG_SOFTMMU) += psci.o obj-$(TARGET_AARCH64) += translate-a64.o helper-a64.o obj-$(TARGET_AARCH64) += translate-sve.o sve_helper.o obj-$(TARGET_AARCH64) += pauth_helper.o + +endif # CONFIG_TCG