Received: by 2002:a05:6358:489b:b0:bb:da1:e618 with SMTP id x27csp318628rwn; Fri, 16 Sep 2022 21:37:19 -0700 (PDT) X-Google-Smtp-Source: AMsMyM6U9/3xw9W+jEATCqDvYizYUW23wmVgb9N+eCKbyz57inDeCVitEq9vsou3qoSREakTh0J5 X-Received: by 2002:a65:620c:0:b0:431:25fe:277 with SMTP id d12-20020a65620c000000b0043125fe0277mr7244957pgv.413.1663389438908; Fri, 16 Sep 2022 21:37:18 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1663389438; cv=none; d=google.com; s=arc-20160816; b=PZCjjFe1+AnLRnDkC7tNw8pmvrvlh82rbQK4ZGHxU5LXfmAKNA3IltNUEc5L6URlmu biKNoc+W7xOzXCyfBvz28P00lcwmrecEMs3I4qukReJsitAppBo38uNTYd28oyI0hk16 rtX1Gc0y91okkmhLGOtLVSM6GofFRfDYZzUI6TpKlEDitZocUZQv5ja4Klnij0dGySpu RgdRHcHMJCARBvNHC4xZmrwnWJSX7cO2djuLvcBP+Kz9MExHvXtYP2K5yoXFVZfLjyBY E10l9LhUUONcIaSmYs58HfxCieQKMrWMrqcMSEcFT3mrVBq1uQWczdgyJCfQBrSyxIZ3 sjsA== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=list-id:precedence:content-transfer-encoding:mime-version :references:in-reply-to:message-id:date:subject:cc:to:from :dkim-signature; bh=rGV4hoCzuh2XydCSG3/ESEIqpwhMPWTSbp0BVXkN4AA=; b=L9ljqNYZgxUkw5jzSPyAO0nZwkkhMc1ZKKqyW8y4zxugLj6KuWaRNm4NJFTVegTHjy A6G2+LRBUVEj70AAHv1MwvbOkIn+TwEOgkbII1Fe9QExk7vZMqOQY8SpeQx8lX7WCIiR Ak3sntabnWpn9O7CERluhebsXJV77nyxRY9NQU7iPMkeQ/54vdvmlTo2Lja8HYQJRRwU 8+bHtr3oV/KLmHgeUTTgwxKgoRhBuSvWyxK46ccUz3qlJv8NVlr/O7nZcD3BWoKbPDer 071bv30Q1jdOAAFDRHkF8ZSqqr/enuGEJf3xyM2IMjjEImrT5p6F9/3ma1m1Ih+H7Twz w5Fw== ARC-Authentication-Results: i=1; mx.google.com; dkim=pass header.i=@gmail.com header.s=20210112 header.b=kHGZjmNA; spf=pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 2620:137:e000::1:20 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=pass (p=NONE sp=QUARANTINE dis=NONE) header.from=gmail.com Return-Path: Received: from out1.vger.email (out1.vger.email. [2620:137:e000::1:20]) by mx.google.com with ESMTP id pj6-20020a17090b4f4600b001facd1e581dsi4807627pjb.14.2022.09.16.21.37.05; Fri, 16 Sep 2022 21:37:18 -0700 (PDT) Received-SPF: pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 2620:137:e000::1:20 as permitted sender) client-ip=2620:137:e000::1:20; Authentication-Results: mx.google.com; dkim=pass header.i=@gmail.com header.s=20210112 header.b=kHGZjmNA; spf=pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 2620:137:e000::1:20 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=pass (p=NONE sp=QUARANTINE dis=NONE) header.from=gmail.com Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S229624AbiIQEPX (ORCPT + 99 others); Sat, 17 Sep 2022 00:15:23 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:59514 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S229623AbiIQEPP (ORCPT ); Sat, 17 Sep 2022 00:15:15 -0400 Received: from mail-qv1-xf29.google.com (mail-qv1-xf29.google.com [IPv6:2607:f8b0:4864:20::f29]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 2E43232EE6 for ; Fri, 16 Sep 2022 21:15:13 -0700 (PDT) Received: by mail-qv1-xf29.google.com with SMTP id w4so18111707qvp.2 for ; Fri, 16 Sep 2022 21:15:13 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20210112; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date; bh=rGV4hoCzuh2XydCSG3/ESEIqpwhMPWTSbp0BVXkN4AA=; b=kHGZjmNAer1fBsYQJIoa7h6Q4g3rdDVMDRj5LQvaECOuECd3jEhWFmDRN/wsTFrNBr HUZqxvOGxrig4G5YwRsVClaQP7Czo+mOqvqKzmZquYqOdFNMfz+mLzto/QfJAqJTR6s4 An2F0vUHmSWM9/P0nA7XKLUxduNscKB2og5oKWf/QsNKOUs6bu5TrdIS/dvvL/4/jLd6 OyfmkKP353Ds7tjNRKiymy4sc2obUPuFrgSYXsAjnwjvZQUXKZM18K7ID8TFZVB3HNcm zAgM71UdFjy7QN8DQ8CYbERi7A9tA6L7UWbg2UF15+WI0DxuP8D5pN/hwYDZV3E9PUvM qDzA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date; bh=rGV4hoCzuh2XydCSG3/ESEIqpwhMPWTSbp0BVXkN4AA=; b=69UlnQQSFEDJLPeji0it8bHrZlTIF/gNQGMqWl8RVCM9khYsYCOeYG+5vAm5CWZ790 wJd1OqyFE2pbdhTkSDeMTblP+z46OdyaVmIJ0omICCS3LL2UoaSdQWwZEeJAVcQjfE2R WgedK0MIXvQbJo579e2FmKW57AtHJlfCyEsttIB/JA2z9THcuOsyjrObZU1LPvqqwXYu To7ImbaRp3GHRGh10biI2s1lHQgGd3/b8az+ficD5k+1ilQ7thhh+mIFXObZJriU/hJI gxIytwZXj1CHPtsRg6qg70ETPJ+9vErUS75aqhf3Su6aC+NyiM4I/lhj+CsU0DTb11MT R+pw== X-Gm-Message-State: ACrzQf3fA0IlxY2xGYljc2sUAY1sEiEUULqovhWC+H5ebAwoW/K6SyDx Q8AZ+Imb/UQQq2gbKnE1ZIE= X-Received: by 2002:a05:6214:194b:b0:4ac:814e:938c with SMTP id q11-20020a056214194b00b004ac814e938cmr6476179qvk.67.1663388112293; Fri, 16 Sep 2022 21:15:12 -0700 (PDT) Received: from Dell-Inspiron-15.. (c-73-149-35-171.hsd1.ma.comcast.net. [73.149.35.171]) by smtp.gmail.com with ESMTPSA id do11-20020a05620a2b0b00b006bbb07ebd83sm7345206qkb.108.2022.09.16.21.15.10 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 16 Sep 2022 21:15:11 -0700 (PDT) From: Ben Wolsieffer To: linux-arm-kernel@lists.infradead.org Cc: Ben Wolsieffer , Russell King , Arnd Bergmann , Marc Zyngier , Vladimir Murzin , Mark Rutland , Ard Biesheuvel , "Russell King (Oracle)" , "Steven Rostedt (Google)" , Nick Desaulniers , Nicolas Pitre , linux-kernel@vger.kernel.org Subject: [PATCH RESEND 1/2] ARM: v7m: handle faults Date: Sat, 17 Sep 2022 00:13:51 -0400 Message-Id: <20220917041403.4191780-2-benwolsieffer@gmail.com> X-Mailer: git-send-email 2.37.2 In-Reply-To: <20220917041403.4191780-1-benwolsieffer@gmail.com> References: <20220917041403.4191780-1-benwolsieffer@gmail.com> MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Spam-Status: No, score=-2.1 required=5.0 tests=BAYES_00,DKIM_SIGNED, DKIM_VALID,DKIM_VALID_AU,DKIM_VALID_EF,FREEMAIL_FROM, RCVD_IN_DNSWL_NONE,SPF_HELO_NONE,SPF_PASS autolearn=ham autolearn_force=no version=3.4.6 X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on lindbergh.monkeyblade.net Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Currently, any fault on an ARMv7M system will trigger the invalid entry exception handler, bringing down the entire system. This patch installs real handlers for the hard fault, memmanage, bus fault and usage fault exceptions. For kernel space faults, an oops is triggered, while user space faults kill the offending process with an appropriate signal. The fault status registers are parsed to construct an appropriate message and signal. This is based on a patch from Emcraft Systems' kernel tree [1], but I have significantly reworked it. [1] https://github.com/EmcraftSystems/linux-upstream/commit/2882de1d86bd536c855feee582d44722434c2ac9 Signed-off-by: Ben Wolsieffer --- arch/arm/include/asm/v7m.h | 29 +++++++ arch/arm/kernel/Makefile | 2 +- arch/arm/kernel/entry-v7m.S | 68 +++++++++++++++- arch/arm/kernel/traps-v7m.c | 156 ++++++++++++++++++++++++++++++++++++ 4 files changed, 250 insertions(+), 5 deletions(-) create mode 100644 arch/arm/kernel/traps-v7m.c diff --git a/arch/arm/include/asm/v7m.h b/arch/arm/include/asm/v7m.h index 4512f7e1918f..b8c636a0578d 100644 --- a/arch/arm/include/asm/v7m.h +++ b/arch/arm/include/asm/v7m.h @@ -38,6 +38,35 @@ #define V7M_SCB_SHCSR_BUSFAULTENA (1 << 17) #define V7M_SCB_SHCSR_MEMFAULTENA (1 << 16) +#define V7M_SCB_CFSR 0x28 + +#define V7M_SCB_CFSR_DIVBYZERO (1 << 25) +#define V7M_SCB_CFSR_UNALIGNED (1 << 24) +#define V7M_SCB_CFSR_NOCP (1 << 19) +#define V7M_SCB_CFSR_INVPC (1 << 18) +#define V7M_SCB_CFSR_INVSTATE (1 << 17) +#define V7M_SCB_CFSR_UNDEFINSTR (1 << 16) +#define V7M_SCB_CFSR_BFARVALID (1 << 15) +#define V7M_SCB_CFSR_LSPERR (1 << 13) +#define V7M_SCB_CFSR_STKERR (1 << 12) +#define V7M_SCB_CFSR_UNSTKERR (1 << 11) +#define V7M_SCB_CFSR_IMPRECISERR (1 << 10) +#define V7M_SCB_CFSR_PRECISERR (1 << 9) +#define V7M_SCB_CFSR_IBUSERR (1 << 8) +#define V7M_SCB_CFSR_MMARVALID (1 << 7) +#define V7M_SCB_CFSR_MLSPERR (1 << 5) +#define V7M_SCB_CFSR_MSTKERR (1 << 4) +#define V7M_SCB_CFSR_MUNSTKERR (1 << 3) +#define V7M_SCB_CFSR_DACCVIOL (1 << 1) +#define V7M_SCB_CFSR_IACCVIOL (1 << 0) + +#define V7M_SCB_HFSR 0x2c +#define V7M_SCB_HFSR_FORCED (1 << 30) +#define V7M_SCB_HFSR_VECTTBL (1 << 1) + +#define V7M_SCB_MMAR 0x34 +#define V7M_SCB_BFAR 0x38 + #define V7M_xPSR_FRAMEPTRALIGN 0x00000200 #define V7M_xPSR_EXCEPTIONNO V7M_SCB_ICSR_VECTACTIVE diff --git a/arch/arm/kernel/Makefile b/arch/arm/kernel/Makefile index 553866751e1a..5d3e66ebc77d 100644 --- a/arch/arm/kernel/Makefile +++ b/arch/arm/kernel/Makefile @@ -34,7 +34,7 @@ obj-$(CONFIG_ATAGS_PROC) += atags_proc.o obj-$(CONFIG_DEPRECATED_PARAM_STRUCT) += atags_compat.o ifeq ($(CONFIG_CPU_V7M),y) -obj-y += entry-v7m.o v7m.o +obj-y += entry-v7m.o v7m.o traps-v7m.o else obj-y += entry-armv.o endif diff --git a/arch/arm/kernel/entry-v7m.S b/arch/arm/kernel/entry-v7m.S index de8a60363c85..62909731cd1f 100644 --- a/arch/arm/kernel/entry-v7m.S +++ b/arch/arm/kernel/entry-v7m.S @@ -84,6 +84,66 @@ __irq_entry: bx lr ENDPROC(__irq_entry) +__hardfault_entry: + v7m_exception_entry + + @ + @ Invoke the Hard Fault handler + @ routine called with r0 = struct pt_regs * + mov r0, sp + bl do_hardfault + + @ execute the pending work, including reschedule + get_thread_info tsk + mov why, #0 + b ret_to_user_from_irq +ENDPROC(__hardfault_entry) + +__memmanage_entry: + v7m_exception_entry + + @ + @ Invoke the Mem Manage handler + @ routine called with r0 = struct pt_regs * + mov r0, sp + bl do_memmanage + + @ execute the pending work, including reschedule + get_thread_info tsk + mov why, #0 + b ret_to_user_from_irq +ENDPROC(__memmanage_entry) + +__busfault_entry: + v7m_exception_entry + + @ + @ Invoke the Bus Fault handler + @ routine called with r0 = struct pt_regs * + mov r0, sp + bl do_busfault + + @ execute the pending work, including reschedule + get_thread_info tsk + mov why, #0 + b ret_to_user_from_irq +ENDPROC(__busfault_entry) + +__usagefault_entry: + v7m_exception_entry + + @ + @ Invoke the Bus Fault handler + @ routine called with r0 = struct pt_regs * + mov r0, sp + bl do_usagefault + + @ execute the pending work, including reschedule + get_thread_info tsk + mov why, #0 + b ret_to_user_from_irq +ENDPROC(__usagefault_entry) + __pendsv_entry: v7m_exception_entry @@ -138,10 +198,10 @@ ENTRY(vector_table) .long 0 @ 0 - Reset stack pointer .long __invalid_entry @ 1 - Reset .long __invalid_entry @ 2 - NMI - .long __invalid_entry @ 3 - HardFault - .long __invalid_entry @ 4 - MemManage - .long __invalid_entry @ 5 - BusFault - .long __invalid_entry @ 6 - UsageFault + .long __hardfault_entry @ 3 - HardFault + .long __memmanage_entry @ 4 - MemManage + .long __busfault_entry @ 5 - BusFault + .long __usagefault_entry @ 6 - UsageFault .long __invalid_entry @ 7 - Reserved .long __invalid_entry @ 8 - Reserved .long __invalid_entry @ 9 - Reserved diff --git a/arch/arm/kernel/traps-v7m.c b/arch/arm/kernel/traps-v7m.c new file mode 100644 index 000000000000..5fd9943448e9 --- /dev/null +++ b/arch/arm/kernel/traps-v7m.c @@ -0,0 +1,156 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (C) 2011 Dmitry Cherukhin, Emcraft Systems + * Copyright (C) 2022 Ben Wolsieffer, Hefring Engineering + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include + +#include +#include +#include +#include + +enum fault { + FAULT_HARDFALT = 3, + FAULT_MEMMANAGE = 4, + FAULT_BUSFAULT = 5, + FAULT_USAGEFAULT = 6 +}; + +struct exception { + const char *name; + int fault; + int test_bit; + int signo; + int si_code; + unsigned int user_debug; +}; + +static const struct exception unknown_exception = + {"unknown exception", 0, 0, SIGSEGV, SEGV_MAPERR, UDBG_SEGV}; + +static const struct exception exceptions[] = { + {"escalated to hard fault", FAULT_HARDFALT, V7M_SCB_HFSR_FORCED, SIGSEGV, SEGV_MAPERR, UDBG_SEGV}, + {"vector read error", FAULT_HARDFALT, V7M_SCB_HFSR_VECTTBL, SIGSEGV, SEGV_MAPERR, UDBG_SEGV}, + {"divide by 0", FAULT_USAGEFAULT, V7M_SCB_CFSR_DIVBYZERO, SIGSEGV, SEGV_MAPERR, UDBG_SEGV}, + {"illegal unaligned access", FAULT_USAGEFAULT, V7M_SCB_CFSR_UNALIGNED, SIGBUS, BUS_ADRALN, UDBG_BUS}, + {"no coprocessor", FAULT_USAGEFAULT, V7M_SCB_CFSR_NOCP, SIGILL, ILL_COPROC, UDBG_UNDEFINED}, + {"return to invalid PC", FAULT_USAGEFAULT, V7M_SCB_CFSR_INVPC, SIGSEGV, SEGV_MAPERR, UDBG_SEGV}, + {"invalid ISA state", FAULT_USAGEFAULT, V7M_SCB_CFSR_INVSTATE, SIGSEGV, SEGV_MAPERR, UDBG_SEGV}, + {"undefined instruction", FAULT_USAGEFAULT, V7M_SCB_CFSR_UNDEFINSTR, SIGILL, ILL_ILLOPC, UDBG_UNDEFINED}, + {"floating point state error", FAULT_BUSFAULT, V7M_SCB_CFSR_LSPERR, SIGBUS, BUS_ADRERR, UDBG_BUS}, + {"exception stack push error", FAULT_BUSFAULT, V7M_SCB_CFSR_STKERR, SIGBUS, BUS_ADRERR, UDBG_BUS}, + {"exception stack pop error", FAULT_BUSFAULT, V7M_SCB_CFSR_UNSTKERR, SIGBUS, BUS_ADRERR, UDBG_BUS}, + {"imprecise data bus error", FAULT_BUSFAULT, V7M_SCB_CFSR_IMPRECISERR, SIGBUS, BUS_ADRERR, UDBG_BUS}, + {"precise data bus error", FAULT_BUSFAULT, V7M_SCB_CFSR_PRECISERR, SIGBUS, BUS_ADRERR, UDBG_BUS}, + {"pre-fetch error", FAULT_BUSFAULT, V7M_SCB_CFSR_IBUSERR, SIGBUS, BUS_ADRERR, UDBG_BUS}, + {"floating point state error", FAULT_MEMMANAGE, V7M_SCB_CFSR_MLSPERR, SIGSEGV, SEGV_MAPERR, UDBG_SEGV}, + {"exception stack push error", FAULT_MEMMANAGE, V7M_SCB_CFSR_MSTKERR, SIGSEGV, SEGV_MAPERR, UDBG_SEGV}, + {"exception stack pop error", FAULT_MEMMANAGE, V7M_SCB_CFSR_MUNSTKERR, SIGSEGV, SEGV_MAPERR, UDBG_SEGV}, + {"data access violation", FAULT_MEMMANAGE, V7M_SCB_CFSR_DACCVIOL, SIGSEGV, SEGV_ACCERR, UDBG_SEGV}, + {"instruction access violation", FAULT_MEMMANAGE, V7M_SCB_CFSR_IACCVIOL, SIGSEGV, SEGV_ACCERR, UDBG_SEGV}, + {NULL} +}; + +/* + * Common routine for high-level exception handlers. + * @param regs state of registers when the exception occurs + * @param fault IPSR, the fault number + */ +static void traps_v7m_common(struct pt_regs *regs, int fault) +{ + unsigned long status; + unsigned long hstatus; + unsigned long cstatus; + unsigned long addr; + size_t i; + const struct exception *exc = &unknown_exception; + + + hstatus = readl(BASEADDR_V7M_SCB + V7M_SCB_HFSR); + cstatus = readl(BASEADDR_V7M_SCB + V7M_SCB_CFSR); + + if (cstatus & V7M_SCB_CFSR_MMARVALID && (fault == FAULT_MEMMANAGE || + (fault == FAULT_HARDFALT && hstatus & V7M_SCB_HFSR_FORCED))) { + addr = readl(BASEADDR_V7M_SCB + V7M_SCB_MMAR); + } else if (cstatus & V7M_SCB_CFSR_BFARVALID && (fault == FAULT_BUSFAULT || + (fault == FAULT_HARDFALT && hstatus & V7M_SCB_HFSR_FORCED))) { + addr = readl(BASEADDR_V7M_SCB + V7M_SCB_BFAR); + } else { + addr = instruction_pointer(regs); + } + + writel(hstatus, BASEADDR_V7M_SCB + V7M_SCB_HFSR); + writel(cstatus, BASEADDR_V7M_SCB + V7M_SCB_CFSR); + + for (i = 0; exceptions[i].name != NULL; ++i) { + if (fault != exceptions[i].fault) + continue; + + status = fault == FAULT_HARDFALT ? hstatus : cstatus; + if (!(status & exceptions[i].test_bit)) + continue; + + exc = &exceptions[i]; + break; + } + +#ifdef CONFIG_DEBUG_USER + if (user_mode(regs) && (user_debug & exc->user_debug)) { + pr_info("%s (%d): %s: addr=0x%px\n", + current->comm, task_pid_nr(current), exc->name, (void *)addr); + __show_regs(regs); + } +#endif + + arm_notify_die(exc->name, regs, exc->signo, exc->si_code, + (void __user *)addr, 0, fault); +} + +/* + * High-level exception handler for exception 3 (Hard Fault). + * @param regs state of registers when the exception occurred + */ +asmlinkage void do_hardfault(struct pt_regs *regs) +{ + traps_v7m_common(regs, FAULT_HARDFALT); +} + +/* + * High-level exception handler for exception 4 (Mem Manage). + * @param regs state of registers when the exception occurred + */ +asmlinkage void do_memmanage(struct pt_regs *regs) +{ + traps_v7m_common(regs, FAULT_MEMMANAGE); +} + +/* + * High-level exception handler for exception 5 (Bus Fault). + * @param regs state of registers when the exception occurred + */ +asmlinkage void do_busfault(struct pt_regs *regs) +{ + traps_v7m_common(regs, FAULT_BUSFAULT); +} + +/* + * High-level exception handler for exception 6 (Usage Fault). + * @param regs state of registers when the exception occurred + */ +asmlinkage void do_usagefault(struct pt_regs *regs) +{ + traps_v7m_common(regs, FAULT_USAGEFAULT); +} -- 2.37.2