From nobody Tue Apr 15 15:36:20 2025 Delivered-To: importer@patchew.org Received-SPF: pass (zoho.com: domain of gnu.org designates 208.118.235.17 as permitted sender) client-ip=208.118.235.17; envelope-from=qemu-devel-bounces+importer=patchew.org@nongnu.org; helo=lists.gnu.org; Authentication-Results: mx.zoho.com; spf=pass (zoho.com: domain of gnu.org designates 208.118.235.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; Return-Path: Received: from lists.gnu.org (lists.gnu.org [208.118.235.17]) by mx.zohomail.com with SMTPS id 1488219102271907.6090023369293; Mon, 27 Feb 2017 10:11:42 -0800 (PST) Received: from localhost ([::1]:55525 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1ciPlr-0007dA-Mp for importer@patchew.org; Mon, 27 Feb 2017 13:11:39 -0500 Received: from eggs.gnu.org ([2001:4830:134:3::10]:51970) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1ciPfe-0002LG-8s for qemu-devel@nongnu.org; Mon, 27 Feb 2017 13:05:17 -0500 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1ciPfc-0001s8-Fm for qemu-devel@nongnu.org; Mon, 27 Feb 2017 13:05:14 -0500 Received: from orth.archaic.org.uk ([2001:8b0:1d0::2]:48678) by eggs.gnu.org with esmtps (TLS1.0:RSA_AES_128_CBC_SHA1:16) (Exim 4.71) (envelope-from ) id 1ciPfc-0001qs-7x for qemu-devel@nongnu.org; Mon, 27 Feb 2017 13:05:12 -0500 Received: from pm215 by orth.archaic.org.uk with local (Exim 4.84_2) (envelope-from ) id 1ciPfb-0002QA-Ew for qemu-devel@nongnu.org; Mon, 27 Feb 2017 18:05:11 +0000 From: Peter Maydell To: qemu-devel@nongnu.org Date: Mon, 27 Feb 2017 18:04:51 +0000 Message-Id: <1488218699-31035-23-git-send-email-peter.maydell@linaro.org> X-Mailer: git-send-email 2.7.4 In-Reply-To: <1488218699-31035-1-git-send-email-peter.maydell@linaro.org> References: <1488218699-31035-1-git-send-email-peter.maydell@linaro.org> MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable X-detected-operating-system: by eggs.gnu.org: GNU/Linux 2.2.x-3.x [generic] X-Received-From: 2001:8b0:1d0::2 Subject: [Qemu-devel] [PULL 22/30] armv7m: Check exception return consistency X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.21 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: qemu-devel-bounces+importer=patchew.org@nongnu.org Sender: "Qemu-devel" X-ZohoMail: RSF_0 Z_629925259 SPT_0 Implement the exception return consistency checks described in the v7M pseudocode ExceptionReturn(). Inspired by a patch from Michael Davidsaver's series, but this is a reimplementation from scratch based on the ARM ARM pseudocode. Signed-off-by: Peter Maydell Reviewed-by: Alex Benn=C3=A9e --- target/arm/cpu.h | 12 +++++- hw/intc/armv7m_nvic.c | 12 +++++- target/arm/helper.c | 112 +++++++++++++++++++++++++++++++++++++++++++++-= ---- 3 files changed, 123 insertions(+), 13 deletions(-) diff --git a/target/arm/cpu.h b/target/arm/cpu.h index 68ad00c..045830a 100644 --- a/target/arm/cpu.h +++ b/target/arm/cpu.h @@ -1366,7 +1366,17 @@ static inline bool armv7m_nvic_can_take_pending_exce= ption(void *opaque) #endif void armv7m_nvic_set_pending(void *opaque, int irq); void armv7m_nvic_acknowledge_irq(void *opaque); -void armv7m_nvic_complete_irq(void *opaque, int irq); +/** + * armv7m_nvic_complete_irq: complete specified interrupt or exception + * @opaque: the NVIC + * @irq: the exception number to complete + * + * Returns: -1 if the irq was not active + * 1 if completing this irq brought us back to base (no active i= rqs) + * 0 if there is still an irq active after this one was completed + * (Ignoring -1, this is the same as the RETTOBASE value before completion= .) + */ +int armv7m_nvic_complete_irq(void *opaque, int irq); =20 /* Interface for defining coprocessor registers. * Registers are defined in tables of arm_cp_reginfo structs diff --git a/hw/intc/armv7m_nvic.c b/hw/intc/armv7m_nvic.c index 456480a..718b1a1 100644 --- a/hw/intc/armv7m_nvic.c +++ b/hw/intc/armv7m_nvic.c @@ -441,10 +441,11 @@ void armv7m_nvic_acknowledge_irq(void *opaque) nvic_irq_update(s); } =20 -void armv7m_nvic_complete_irq(void *opaque, int irq) +int armv7m_nvic_complete_irq(void *opaque, int irq) { NVICState *s =3D (NVICState *)opaque; VecInfo *vec; + int ret; =20 assert(irq > ARMV7M_EXCP_RESET && irq < s->num_irq); =20 @@ -452,6 +453,13 @@ void armv7m_nvic_complete_irq(void *opaque, int irq) =20 trace_nvic_complete_irq(irq); =20 + if (!vec->active) { + /* Tell the caller this was an illegal exception return */ + return -1; + } + + ret =3D nvic_rettobase(s); + vec->active =3D 0; if (vec->level) { /* Re-pend the exception if it's still held high; only @@ -462,6 +470,8 @@ void armv7m_nvic_complete_irq(void *opaque, int irq) } =20 nvic_irq_update(s); + + return ret; } =20 /* callback when external interrupt line is changed */ diff --git a/target/arm/helper.c b/target/arm/helper.c index be731dc..9081771 100644 --- a/target/arm/helper.c +++ b/target/arm/helper.c @@ -6068,22 +6068,99 @@ static void v7m_push_stack(ARMCPU *cpu) v7m_push(env, env->regs[0]); } =20 -static void do_v7m_exception_exit(CPUARMState *env) +static void do_v7m_exception_exit(ARMCPU *cpu) { + CPUARMState *env =3D &cpu->env; uint32_t type; uint32_t xpsr; - + bool ufault =3D false; + bool return_to_sp_process =3D false; + bool return_to_handler =3D false; + bool rettobase =3D false; + + /* We can only get here from an EXCP_EXCEPTION_EXIT, and + * arm_v7m_do_unassigned_access() enforces the architectural rule + * that jumps to magic addresses don't have magic behaviour unless + * we're in Handler mode (compare pseudocode BXWritePC()). + */ + assert(env->v7m.exception !=3D 0); + + /* 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. + */ type =3D env->regs[15]; + if (env->thumb) { + type |=3D 1; + } + + qemu_log_mask(CPU_LOG_INT, "Exception return: magic PC %" PRIx32 + " previous exception %d\n", + type, env->v7m.exception); + + if (extract32(type, 5, 23) !=3D extract32(-1, 5, 23)) { + qemu_log_mask(LOG_GUEST_ERROR, "M profile: zero high bits in excep= tion " + "exit PC value 0x%" PRIx32 " are UNPREDICTABLE\n", t= ype); + } + if (env->v7m.exception !=3D ARMV7M_EXCP_NMI) { /* Auto-clear FAULTMASK on return from other than NMI */ env->daif &=3D ~PSTATE_F; } - if (env->v7m.exception !=3D 0) { - armv7m_nvic_complete_irq(env->nvic, env->v7m.exception); + + switch (armv7m_nvic_complete_irq(env->nvic, env->v7m.exception)) { + case -1: + /* attempt to exit an exception that isn't active */ + ufault =3D 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 !=3D= 1" + * where we have 'rettobase =3D=3D false'.) + */ + rettobase =3D true; + break; + default: + g_assert_not_reached(); + } + + switch (type & 0xf) { + case 1: /* Return to Handler */ + return_to_handler =3D true; + break; + case 13: /* Return to Thread using Process stack */ + return_to_sp_process =3D true; + /* fall through */ + case 9: /* Return to Thread using Main stack */ + if (!rettobase && + !(env->v7m.ccr & R_V7M_CCR_NONBASETHRDENA_MASK)) { + ufault =3D true; + } + break; + default: + ufault =3D true; + } + + if (ufault) { + /* Bad exception return: instead of popping the exception + * stack, directly take a usage fault on the current stack. + */ + env->v7m.cfsr |=3D R_V7M_CFSR_INVPC_MASK; + armv7m_nvic_set_pending(env->nvic, ARMV7M_EXCP_USAGE); + v7m_exception_taken(cpu, type | 0xf0000000); + qemu_log_mask(CPU_LOG_INT, "...taking UsageFault on existing " + "stackframe: failed exception return integrity check= \n"); + return; } =20 /* Switch to the target stack. */ - switch_v7m_sp(env, (type & 4) !=3D 0); + switch_v7m_sp(env, return_to_sp_process); /* Pop registers. */ env->regs[0] =3D v7m_pop(env); env->regs[1] =3D v7m_pop(env); @@ -6107,11 +6184,24 @@ static void do_v7m_exception_exit(CPUARMState *env) /* Undo stack alignment. */ if (xpsr & 0x200) env->regs[13] |=3D 4; - /* ??? The exception return type specifies Thread/Handler mode. Howev= er - this is also implied by the xPSR value. Not sure what to do - if there is a mismatch. */ - /* ??? Likewise for mismatches between the CONTROL register and the st= ack - pointer. */ + + /* The restored xPSR exception field will be zero if we're + * resuming in Thread mode. If that doesn't match what the + * exception return type specified then this is a UsageFault. + */ + if (return_to_handler =3D=3D (env->v7m.exception =3D=3D 0)) { + /* Take an INVPC UsageFault by pushing the stack again. */ + armv7m_nvic_set_pending(env->nvic, ARMV7M_EXCP_USAGE); + env->v7m.cfsr |=3D R_V7M_CFSR_INVPC_MASK; + v7m_push_stack(cpu); + v7m_exception_taken(cpu, type | 0xf0000000); + qemu_log_mask(CPU_LOG_INT, "...taking UsageFault on new stackframe= : " + "failed exception return integrity check\n"); + return; + } + + /* Otherwise, we have a successful exception exit. */ + qemu_log_mask(CPU_LOG_INT, "...successful exception return\n"); } =20 static void arm_log_exception(int idx) @@ -6184,7 +6274,7 @@ void arm_v7m_cpu_do_interrupt(CPUState *cs) case EXCP_IRQ: break; case EXCP_EXCEPTION_EXIT: - do_v7m_exception_exit(env); + do_v7m_exception_exit(cpu); return; default: cpu_abort(cs, "Unhandled exception 0x%x\n", cs->exception_index); --=20 2.7.4