From de39cc80347b45e4b1ce35c0780b50a82a1b54f4 Mon Sep 17 00:00:00 2001 From: Sergei Miroshnichenko Date: Mon, 3 Aug 2015 16:21:38 +0300 Subject: [PATCH 1/8] RT #110056 Port uncompressed kernel feature from A2F --- arch/arm/boot/Makefile | 11 +++++++++++ init/Kconfig | 5 +++++ 2 files changed, 16 insertions(+) diff --git a/arch/arm/boot/Makefile b/arch/arm/boot/Makefile index 54a09f9464fbd9..4b3038a26c05be 100644 --- a/arch/arm/boot/Makefile +++ b/arch/arm/boot/Makefile @@ -63,8 +63,14 @@ $(obj)/Image: vmlinux FORCE $(obj)/compressed/vmlinux: $(obj)/Image FORCE $(Q)$(MAKE) $(build)=$(obj)/compressed $@ +ifeq ($(CONFIG_KERNEL_COMPRESS_NONE),y) +$(obj)/zImage: FORCE + @$(kecho) 'Kernel configured for no compression (CONFIG_COMPRESS_NONE=y)' + @false +else $(obj)/zImage: $(obj)/compressed/vmlinux FORCE $(call if_changed,objcopy) +endif endif @@ -86,9 +92,14 @@ if [ $(words $(UIMAGE_LOADADDR)) -ne 1 ]; then \ false; \ fi +ifeq ($(CONFIG_KERNEL_COMPRESS_NONE),y) +$(obj)/uImage: $(obj)/Image FORCE + $(call if_changed,uimage) +else $(obj)/uImage: $(obj)/zImage FORCE @$(check_for_multiple_loadaddr) $(call if_changed,uimage) +endif $(obj)/bootp/bootp: $(obj)/zImage initrd FORCE $(Q)$(MAKE) $(build)=$(obj)/bootp $@ diff --git a/init/Kconfig b/init/Kconfig index d19ed66aba3bae..a86ce1d706aad9 100644 --- a/init/Kconfig +++ b/init/Kconfig @@ -300,6 +300,11 @@ config KERNEL_LZO size is about 10% bigger than gzip; however its speed (both compression and decompression) is the fastest. +config KERNEL_COMPRESS_NONE + bool "NONE" + help + No compression of the kernel image. Fastest decompression time. + config KERNEL_LZ4 bool "LZ4" depends on HAVE_KERNEL_LZ4 From 4e9cac83a30f06c5c9824e547efa8f36cef85daa Mon Sep 17 00:00:00 2001 From: Yuri Tikhonov Date: Sat, 22 Aug 2015 20:07:35 +0300 Subject: [PATCH 2/8] RT112561. Adaptation for our 'project' build procedure - vmlinux.lds.S.good backup file - initramgs-list-min.stub file - set UIMAGE_LOADADDR from .config - correct UIMAGE_ENTRYADDR for thumb2-only kernel Signed-off-by: Yuri Tikhonov --- arch/arm/boot/Makefile | 14 +++ arch/arm/kernel/vmlinux.lds.S.good | 179 +++++++++++++++++++++++++++++ initramfs-list-min.stub | 6 + 3 files changed, 199 insertions(+) create mode 100644 arch/arm/kernel/vmlinux.lds.S.good create mode 100644 initramfs-list-min.stub diff --git a/arch/arm/boot/Makefile b/arch/arm/boot/Makefile index 4b3038a26c05be..43515635335d68 100644 --- a/arch/arm/boot/Makefile +++ b/arch/arm/boot/Makefile @@ -84,6 +84,20 @@ else endif endif +ifeq ($(UIMAGE_LOADADDR),) + UIMAGE_LOADADDR=$(shell /bin/bash -c 'printf "0x%08x" \ + $$[$(CONFIG_DRAM_BASE) + 0x008000]') +endif + +ifeq ($(UIMAGE_ENTRYADDR),) + ifeq ($(CONFIG_THUMB2_KERNEL),y) + # Set bit 0 to 1 so that "mov pc, rx" switches to Thumb-2 mode + UIMAGE_ENTRYADDR?=$(shell echo $(UIMAGE_LOADADDR) | sed -e "s/.$$/1/") + else + UIMAGE_ENTRYADDR?=$(UIMAGE_LOADADDR) + endif +endif + check_for_multiple_loadaddr = \ if [ $(words $(UIMAGE_LOADADDR)) -ne 1 ]; then \ echo 'multiple (or no) load addresses: $(UIMAGE_LOADADDR)'; \ diff --git a/arch/arm/kernel/vmlinux.lds.S.good b/arch/arm/kernel/vmlinux.lds.S.good new file mode 100644 index 00000000000000..20c4f6d20c7a4d --- /dev/null +++ b/arch/arm/kernel/vmlinux.lds.S.good @@ -0,0 +1,179 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* ld script to make ARM Linux kernel + * taken from the i386 version by Russell King + * Written by Martin Mares + */ + +#ifdef CONFIG_XIP_KERNEL +#include "vmlinux-xip.lds.S" +#else + +#include +#include +#include +#include +#include +#include +#include + +OUTPUT_ARCH(arm) +ENTRY(stext) + +#ifndef __ARMEB__ +jiffies = jiffies_64; +#else +jiffies = jiffies_64 + 4; +#endif + +SECTIONS +{ + /* + * XXX: The linker does not define how output sections are + * assigned to input sections when there are multiple statements + * matching the same input section name. There is no documented + * order of matching. + * + * unwind exit sections must be discarded before the rest of the + * unwind sections get included. + */ + /DISCARD/ : { + ARM_DISCARD +#ifndef CONFIG_SMP_ON_UP + *(.alt.smp.init) +#endif +#ifndef CONFIG_ARM_UNWIND + *(.ARM.exidx) *(.ARM.exidx.*) + *(.ARM.extab) *(.ARM.extab.*) +#endif + } + + . = KERNEL_OFFSET + TEXT_OFFSET; + .head.text : { + _text = .; + HEAD_TEXT + } + +#ifdef CONFIG_STRICT_KERNEL_RWX + . = ALIGN(1< Date: Fri, 19 May 2023 23:05:37 +0300 Subject: [PATCH 3/8] RM 6639: zImage is not needed if CONFIG_KERNEL_COMPRESS_NONE is defined --- arch/arm/Makefile | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/arch/arm/Makefile b/arch/arm/Makefile index fa45837b8065ca..237d4a5768b433 100644 --- a/arch/arm/Makefile +++ b/arch/arm/Makefile @@ -300,7 +300,12 @@ INSTALL_TARGETS = zinstall uinstall install PHONY += bzImage $(BOOT_TARGETS) $(INSTALL_TARGETS) +ifeq ($(CONFIG_KERNEL_COMPRESS_NONE),y) +bootpImage uImage: Image +else bootpImage uImage: zImage +endif + zImage: Image $(BOOT_TARGETS): vmlinux From 70c36e17f0bccaa111b9a13b153ee72a5b4bed35 Mon Sep 17 00:00:00 2001 From: Alexander Dyachenko Date: Fri, 19 May 2023 23:13:08 +0300 Subject: [PATCH 4/8] RM 6639: Restore UIMAGE_IN env variable to pass the uImage data files from the Emcraft 'project' build procedure --- scripts/Makefile.lib | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/scripts/Makefile.lib b/scripts/Makefile.lib index 0a8a4689c3ebbe..41ae0709a10c4f 100644 --- a/scripts/Makefile.lib +++ b/scripts/Makefile.lib @@ -425,13 +425,14 @@ UIMAGE_TYPE ?= kernel UIMAGE_LOADADDR ?= arch_must_set_this UIMAGE_ENTRYADDR ?= $(UIMAGE_LOADADDR) UIMAGE_NAME ?= 'Linux-$(KERNELRELEASE)' +UIMAGE_IN ?= $< quiet_cmd_uimage = UIMAGE $@ cmd_uimage = $(BASH) $(MKIMAGE) -A $(UIMAGE_ARCH) -O linux \ -C $(UIMAGE_COMPRESSION) $(UIMAGE_OPTS-y) \ -T $(UIMAGE_TYPE) \ -a $(UIMAGE_LOADADDR) -e $(UIMAGE_ENTRYADDR) \ - -n $(UIMAGE_NAME) -d $< $@ + -n $(UIMAGE_NAME) -d $(UIMAGE_IN) $@ # XZ # --------------------------------------------------------------------------- From b6a52938ee140b078be4cc8e4fab02c4f95590cd Mon Sep 17 00:00:00 2001 From: Alexander Dyachenko Date: Fri, 2 Jun 2023 01:29:36 +0300 Subject: [PATCH 5/8] RM 6641: Temporary implementation of dma_alloc_coherent() on STM32H7 SOM --- .../net/ethernet/stmicro/stmmac/stmmac_main.c | 31 +++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c index 2569673559df3a..5c1d5cdc838749 100644 --- a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c @@ -173,6 +173,12 @@ int stmmac_bus_clks_config(struct stmmac_priv *priv, bool enabled) } EXPORT_SYMBOL_GPL(stmmac_bus_clks_config); +/* temporary implementation of dma_alloc_coherent() on STM32H7 SOM */ +#define STM32H7_SOM_DMA +#ifdef STM32H7_SOM_DMA +static const char dma_rx_phy[16384] __attribute__ ((aligned (16))); +static const char dma_tx_phy[16384] __attribute__ ((aligned (16))); +#endif /** * stmmac_verify_args - verify the driver parameters. * Description: it checks the driver parameters and set a default in case of @@ -1908,6 +1914,7 @@ static void __free_dma_rx_desc_resources(struct stmmac_priv *priv, u32 queue) rx_q->buf_alloc_num = 0; rx_q->xsk_pool = NULL; +#ifndef STM32H7_SOM_DMA /* Free DMA regions of consistent memory previously allocated */ if (!priv->extend_desc) dma_free_coherent(priv->device, priv->dma_rx_size * @@ -1917,6 +1924,7 @@ static void __free_dma_rx_desc_resources(struct stmmac_priv *priv, u32 queue) dma_free_coherent(priv->device, priv->dma_rx_size * sizeof(struct dma_extended_desc), rx_q->dma_erx, rx_q->dma_rx_phy); +#endif if (xdp_rxq_info_is_reg(&rx_q->xdp_rxq)) xdp_rxq_info_unreg(&rx_q->xdp_rxq); @@ -1963,7 +1971,9 @@ static void __free_dma_tx_desc_resources(struct stmmac_priv *priv, u32 queue) size *= priv->dma_tx_size; +#ifndef STM32H7_SOM_DMA dma_free_coherent(priv->device, size, addr, tx_q->dma_tx_phy); +#endif kfree(tx_q->tx_skbuff_dma); kfree(tx_q->tx_skbuff); @@ -2025,6 +2035,12 @@ static int __alloc_dma_rx_desc_resources(struct stmmac_priv *priv, u32 queue) return -ENOMEM; if (priv->extend_desc) { +#ifdef STM32H7_SOM_DMA + rx_q->dma_erx = (void*) dma_rx_phy; + rx_q->dma_rx_phy = (dma_addr_t) dma_rx_phy; + if ((u32)dma_rx_phy > 0xd03f0000) + return -ENOMEM; +#else rx_q->dma_erx = dma_alloc_coherent(priv->device, priv->dma_rx_size * sizeof(struct dma_extended_desc), @@ -2032,8 +2048,15 @@ static int __alloc_dma_rx_desc_resources(struct stmmac_priv *priv, u32 queue) GFP_KERNEL); if (!rx_q->dma_erx) return -ENOMEM; +#endif } else { +#ifdef STM32H7_SOM_DMA + rx_q->dma_rx = (void*) dma_rx_phy; + rx_q->dma_rx_phy = (dma_addr_t) dma_rx_phy; + if ((u32)dma_rx_phy > 0xd03f0000) + return -ENOMEM; +#else rx_q->dma_rx = dma_alloc_coherent(priv->device, priv->dma_rx_size * sizeof(struct dma_desc), @@ -2041,6 +2064,7 @@ static int __alloc_dma_rx_desc_resources(struct stmmac_priv *priv, u32 queue) GFP_KERNEL); if (!rx_q->dma_rx) return -ENOMEM; +#endif } if (stmmac_xdp_is_enabled(priv) && @@ -2120,10 +2144,17 @@ static int __alloc_dma_tx_desc_resources(struct stmmac_priv *priv, u32 queue) size *= priv->dma_tx_size; +#ifdef STM32H7_SOM_DMA + addr = (void*) dma_tx_phy; + tx_q->dma_tx_phy = (dma_addr_t) dma_tx_phy; + if ((u32)dma_tx_phy > 0xd03f0000) + return -ENOMEM; +#else addr = dma_alloc_coherent(priv->device, size, &tx_q->dma_tx_phy, GFP_KERNEL); if (!addr) return -ENOMEM; +#endif if (priv->extend_desc) tx_q->dma_etx = addr; From 7e383dbf6ef2b5ea669bdf7f060f2622ab2e60a0 Mon Sep 17 00:00:00 2001 From: Alexander Dyachenko Date: Tue, 6 Jun 2023 23:47:16 +0300 Subject: [PATCH 6/8] RM 6661: Revert "RM 6641: Temporary implementation of dma_alloc_coherent() on STM32H7 SOM" This reverts commit b6a52938ee140b078be4cc8e4fab02c4f95590cd. --- .../net/ethernet/stmicro/stmmac/stmmac_main.c | 31 ------------------- 1 file changed, 31 deletions(-) diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c index 5c1d5cdc838749..2569673559df3a 100644 --- a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c @@ -173,12 +173,6 @@ int stmmac_bus_clks_config(struct stmmac_priv *priv, bool enabled) } EXPORT_SYMBOL_GPL(stmmac_bus_clks_config); -/* temporary implementation of dma_alloc_coherent() on STM32H7 SOM */ -#define STM32H7_SOM_DMA -#ifdef STM32H7_SOM_DMA -static const char dma_rx_phy[16384] __attribute__ ((aligned (16))); -static const char dma_tx_phy[16384] __attribute__ ((aligned (16))); -#endif /** * stmmac_verify_args - verify the driver parameters. * Description: it checks the driver parameters and set a default in case of @@ -1914,7 +1908,6 @@ static void __free_dma_rx_desc_resources(struct stmmac_priv *priv, u32 queue) rx_q->buf_alloc_num = 0; rx_q->xsk_pool = NULL; -#ifndef STM32H7_SOM_DMA /* Free DMA regions of consistent memory previously allocated */ if (!priv->extend_desc) dma_free_coherent(priv->device, priv->dma_rx_size * @@ -1924,7 +1917,6 @@ static void __free_dma_rx_desc_resources(struct stmmac_priv *priv, u32 queue) dma_free_coherent(priv->device, priv->dma_rx_size * sizeof(struct dma_extended_desc), rx_q->dma_erx, rx_q->dma_rx_phy); -#endif if (xdp_rxq_info_is_reg(&rx_q->xdp_rxq)) xdp_rxq_info_unreg(&rx_q->xdp_rxq); @@ -1971,9 +1963,7 @@ static void __free_dma_tx_desc_resources(struct stmmac_priv *priv, u32 queue) size *= priv->dma_tx_size; -#ifndef STM32H7_SOM_DMA dma_free_coherent(priv->device, size, addr, tx_q->dma_tx_phy); -#endif kfree(tx_q->tx_skbuff_dma); kfree(tx_q->tx_skbuff); @@ -2035,12 +2025,6 @@ static int __alloc_dma_rx_desc_resources(struct stmmac_priv *priv, u32 queue) return -ENOMEM; if (priv->extend_desc) { -#ifdef STM32H7_SOM_DMA - rx_q->dma_erx = (void*) dma_rx_phy; - rx_q->dma_rx_phy = (dma_addr_t) dma_rx_phy; - if ((u32)dma_rx_phy > 0xd03f0000) - return -ENOMEM; -#else rx_q->dma_erx = dma_alloc_coherent(priv->device, priv->dma_rx_size * sizeof(struct dma_extended_desc), @@ -2048,15 +2032,8 @@ static int __alloc_dma_rx_desc_resources(struct stmmac_priv *priv, u32 queue) GFP_KERNEL); if (!rx_q->dma_erx) return -ENOMEM; -#endif } else { -#ifdef STM32H7_SOM_DMA - rx_q->dma_rx = (void*) dma_rx_phy; - rx_q->dma_rx_phy = (dma_addr_t) dma_rx_phy; - if ((u32)dma_rx_phy > 0xd03f0000) - return -ENOMEM; -#else rx_q->dma_rx = dma_alloc_coherent(priv->device, priv->dma_rx_size * sizeof(struct dma_desc), @@ -2064,7 +2041,6 @@ static int __alloc_dma_rx_desc_resources(struct stmmac_priv *priv, u32 queue) GFP_KERNEL); if (!rx_q->dma_rx) return -ENOMEM; -#endif } if (stmmac_xdp_is_enabled(priv) && @@ -2144,17 +2120,10 @@ static int __alloc_dma_tx_desc_resources(struct stmmac_priv *priv, u32 queue) size *= priv->dma_tx_size; -#ifdef STM32H7_SOM_DMA - addr = (void*) dma_tx_phy; - tx_q->dma_tx_phy = (dma_addr_t) dma_tx_phy; - if ((u32)dma_tx_phy > 0xd03f0000) - return -ENOMEM; -#else addr = dma_alloc_coherent(priv->device, size, &tx_q->dma_tx_phy, GFP_KERNEL); if (!addr) return -ENOMEM; -#endif if (priv->extend_desc) tx_q->dma_etx = addr; From 61222f4bfc0cffa286077a251d74fa0b1b6a771d Mon Sep 17 00:00:00 2001 From: Alexander Dyachenko Date: Fri, 21 Jul 2023 15:15:58 +0000 Subject: [PATCH 7/8] RM 6754: Add a new SoC - STM32H753 --- arch/arm/mach-stm32/board-dt.c | 1 + 1 file changed, 1 insertion(+) diff --git a/arch/arm/mach-stm32/board-dt.c b/arch/arm/mach-stm32/board-dt.c index 9ff06f2fcbf44e..8e0840f6e19971 100644 --- a/arch/arm/mach-stm32/board-dt.c +++ b/arch/arm/mach-stm32/board-dt.c @@ -18,6 +18,7 @@ static const char *const stm32_compat[] __initconst = { "st,stm32f769", "st,stm32h743", "st,stm32h750", + "st,stm32h753", "st,stm32mp131", "st,stm32mp133", "st,stm32mp135", From 0b520567aaa8175794ef9129e876078d97e1aa50 Mon Sep 17 00:00:00 2001 From: Alexander Sanochkin Date: Sat, 29 Jul 2023 07:02:55 +0000 Subject: [PATCH 8/8] RM-2901: Added support for the ARMv7-M VFP Extension --- arch/arm/kernel/traps-v7m.c | 240 ++++++++++++++++++++++++++++++++++++ arch/arm/kernel/vfp-m.c | 163 ++++++++++++++++++++++++ 2 files changed, 403 insertions(+) create mode 100644 arch/arm/kernel/traps-v7m.c create mode 100644 arch/arm/kernel/vfp-m.c diff --git a/arch/arm/kernel/traps-v7m.c b/arch/arm/kernel/traps-v7m.c new file mode 100644 index 00000000000000..0babef5479297e --- /dev/null +++ b/arch/arm/kernel/traps-v7m.c @@ -0,0 +1,240 @@ +/* + * linux/arch/arm/kernel/traps-v7m.c + * + * Copyright (C) 2011 Dmitry Cherukhin, Emcraft Systems + * + * 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +struct nvic_regs { + unsigned long some_regs1[837]; /* +000 */ + unsigned long config_control; /* +d14 */ + unsigned long some_regs2[3]; /* +d18 */ + unsigned long system_handler_csr; /* +d24 */ + unsigned long local_fault_status; /* +d28 */ + unsigned long hard_fault_status; /* +d2c */ + unsigned long some_regs3[1]; /* +d30 */ + unsigned long mfar; /* +d34 */ + unsigned long bfar; /* +d38 */ + unsigned long some_regs4[21]; /* +d3c */ + unsigned long mpu_type; /* +d90 */ + unsigned long mpu_control; /* +d94 */ + unsigned long reg_number; /* +d98 */ + unsigned long reg_base; /* +d9c */ + unsigned long reg_attr; /* +da0 */ +}; + +#define NVIC_REGS_BASE 0xE000E000 +#define NVIC ((struct nvic_regs *)(NVIC_REGS_BASE)) + +enum config_control_bits { + UNALIGN_TRP = 0x00000008, + DIV_0_TRP = 0x00000010, +}; + +enum system_handler_csr_bits { + BUSFAULTENA = 0x00020000, + USGFAULTENA = 0x00040000, +}; + +enum local_fault_status_bits { + IBUSERR = 0x00000100, + PRECISERR = 0x00000200, + IMPRECISERR = 0x00000400, + UNSTKERR = 0x00000800, + STKERR = 0x00001000, + BFARVALID = 0x00008000, + UNDEFINSTR = 0x00010000, + INVSTATE = 0x00020000, + INVPC = 0x00040000, + NOCP = 0x00080000, + UNALIGNED = 0x01000000, + DIVBYZERO = 0x02000000, +}; + +enum hard_fault_status_bits { + VECTTBL = 0x00000002, + FORCED = 0x40000000, +}; + +enum interrupts { + HARDFAULT = 3, + BUSFAULT = 5, + USAGEFAULT = 6, +}; + +struct traps { + char *name; + int test_bit; + int handler; +}; + +static struct traps traps[] = { + {"Vector Read error", VECTTBL, HARDFAULT}, + {"uCode stack push error", STKERR, BUSFAULT}, + {"uCode stack pop error", UNSTKERR, BUSFAULT}, + {"Escalated to Hard Fault", FORCED, HARDFAULT}, + {"Pre-fetch error", IBUSERR, BUSFAULT}, + {"Precise data bus error", PRECISERR, BUSFAULT}, + {"Imprecise data bus error", IMPRECISERR, BUSFAULT}, + {"No Coprocessor", NOCP, USAGEFAULT}, + {"Undefined Instruction", UNDEFINSTR, USAGEFAULT}, + {"Invalid ISA state", INVSTATE, USAGEFAULT}, + {"Return to invalid PC", INVPC, USAGEFAULT}, + {"Illegal unaligned access", UNALIGNED, USAGEFAULT}, + {"Divide By 0", DIVBYZERO, USAGEFAULT}, + {NULL} +}; + +extern void show_regs(struct pt_regs * regs); + +/* + * The function initializes Bus fault and Usage fault exceptions, + * forbids unaligned data access and division by 0. + */ +void traps_v7m_init(void){ + writel(readl(&NVIC->system_handler_csr) | USGFAULTENA | BUSFAULTENA, + &NVIC->system_handler_csr); + writel( +#ifndef CONFIG_ARM_V7M_NO_UNALIGN_TRP + UNALIGN_TRP | +#endif + DIV_0_TRP | readl(&NVIC->config_control), + &NVIC->config_control); +} + +/* + * The function prints information about the reason of the exception + * @param name name of current executable (process or kernel) + * @param regs state of registers when the exception occurs + * @param in IPSR, the number of the exception + * @param addr address caused the interrupt, or current pc + * @param hstatus status register for hard fault + * @param lstatus status register for local fault + */ +static void traps_v7m_print_message(char *name, struct pt_regs *regs, + enum interrupts in, unsigned long addr, + unsigned long hstatus, unsigned long lstatus) +{ + int i; + printk("\n\n%s: fault at 0x%08lx [pc=0x%08lx, sp=0x%08lx]\n", + name, addr, instruction_pointer(regs), regs->ARM_sp); + for (i = 0; traps[i].name != NULL; ++i) { + if ((traps[i].handler == HARDFAULT ? hstatus : lstatus) + & traps[i].test_bit) { + printk("%s\n", traps[i].name); + } + } + printk("\n"); +} + +/* + * Common routine for high-level exception handlers. + * @param regs state of registers when the exception occurs + * @param in IPSR, the number of the exception + */ +void traps_v7m_common(struct pt_regs *regs, enum interrupts in) +{ + unsigned long hstatus; + unsigned long lstatus; + unsigned long addr; + + hstatus = readl(&NVIC->hard_fault_status); + lstatus = readl(&NVIC->local_fault_status); + + if (lstatus & BFARVALID && (in == BUSFAULT || + (in == HARDFAULT && hstatus & FORCED))) { + addr = readl(&NVIC->bfar); + } else { + addr = instruction_pointer(regs); + } + + writel(hstatus, &NVIC->hard_fault_status); + writel(lstatus, &NVIC->local_fault_status); + +#if defined(CONFIG_VFPM) + if (lstatus & NOCP) { + extern int vfpm_handle_exception(void); + if (vfpm_handle_exception()) { + return; + } + } +#endif + + if (user_mode(regs)) { +#if defined(CONFIG_DEBUG_USER) + extern unsigned int user_debug; + + if (user_debug & UDBG_SEGV) { + traps_v7m_print_message(current->comm, regs, in, addr, + hstatus, lstatus); + } +#endif + if (lstatus & UNDEFINSTR) { + send_sig(SIGTRAP, current, 0); + } + else { + send_sig(SIGSEGV, current, 0); + } + } else { + traps_v7m_print_message("KERNEL", regs, in, addr, + hstatus, lstatus); + show_regs(regs); + panic(0); + } +} + +/* + * High-level exception handler for exception 3 (Hard fault). + * @param regs state of registers when the exception occurs + */ +asmlinkage void __exception_irq_entry do_hardfault(struct pt_regs *regs) +{ + traps_v7m_common(regs, HARDFAULT); +} + +/* + * High-level exception handler for exception 5 (Bus fault). + * @param regs state of registers when the exception occurs + */ +asmlinkage void __exception_irq_entry do_busfault(struct pt_regs *regs) +{ + traps_v7m_common(regs, BUSFAULT); +} + +/* + * High-level exception handler for exception 6 (Usage fault). + * @param regs state of registers when the exception occurs + */ +asmlinkage void __exception_irq_entry do_usagefault(struct pt_regs *regs) +{ + traps_v7m_common(regs, USAGEFAULT); +} + diff --git a/arch/arm/kernel/vfp-m.c b/arch/arm/kernel/vfp-m.c new file mode 100644 index 00000000000000..90f6883583ea3d --- /dev/null +++ b/arch/arm/kernel/vfp-m.c @@ -0,0 +1,163 @@ +/* + * arch/arm/kernel/vfp-m.c + * + * Copyright (C) 2018 Christer Weinigel + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include +#include + +#include +#include +#include +#include + +#define FPCCR ((u32 *)0xe000ef34) /* Floating-point Context Control Register */ +#define FPCCR_ASPEN (1 << 31) /* Enable FPU stacking */ +#define FPCCR_LSPEN (1 << 30) /* Enable lazy FPU stacking */ +#define FPCCR_LSPACT (1 << 0) /* 1 => Deferred (lazy) FPU stacking is waiting to be saved */ + +#define FPCAR ((u32 *)0xe000ef38) /* Floating-point Context Address Register */ + +#define CPACR ((u32 *)0xe000ed88) /* Coprocessor Access Control Register */ +#define CPACR_CP0_FULL (0x3 << 20) /* Full access to coprocessor 0 */ +#define CPACR_CP1_FULL (0x3 << 22) /* Full access to coprocessor 1 */ + +#undef MVFR0 +#define MVFR0 ((u32 *)0xe000ef40) /* Media and VFP Feature Register 0 */ + +static struct thread_info *vfpm_last_thread; + +static void vfpm_disable_access(void) +{ + writel(readl(CPACR) & ~(CPACR_CP0_FULL | CPACR_CP1_FULL), CPACR); +} + +static void vfpm_enable_access(void) +{ + writel(readl(CPACR) | CPACR_CP0_FULL | CPACR_CP1_FULL, CPACR); +} + +static void vfpm_save_context(union vfp_state *vfp) +{ + u32 dummy; + asm volatile(" stc p11, cr0, [%0], #32*4" + : "=r" (dummy) : "0" (vfp->hard.fpregs) : "cc"); + asm(" fmrx %0, fpscr" + : "=r" (vfp->hard.fpscr) : : "cc"); +} + +static void vfpm_load_context(union vfp_state *vfp) +{ + u32 dummy; + asm volatile(" ldc p11, cr0, [%0], #32*4" + : "=r" (dummy) : "0" (vfp->hard.fpregs) : "cc"); + asm(" fmxr fpscr, %0" + : : "r" (vfp->hard.fpscr) : "cc"); +} + +static void vfpm_switch_to(struct thread_info *thread) +{ + vfpm_enable_access(); + + if (vfpm_last_thread != thread) { + if (vfpm_last_thread) + vfpm_save_context(&vfpm_last_thread->vfpstate); + + vfpm_load_context(&thread->vfpstate); + vfpm_last_thread = thread; + } +} + +int vfpm_handle_exception(void) +{ + struct thread_info *thread = current_thread_info(); + + if ((readl(MVFR0) & 0xf0) != 0x20) + return 0; + + WARN_ON(thread->vfpstate.hard.used); + + thread->vfpstate.hard.used = 1; + vfpm_switch_to(thread); + + return 1; +} + +static int vfpm_notifier(struct notifier_block *self, unsigned long cmd, + void *t) +{ + struct thread_info *thread = t; + struct thread_info *current_thread = current_thread_info(); + + switch (cmd) { + case THREAD_NOTIFY_FLUSH: + memset(&thread->vfpstate, 0, sizeof(thread->vfpstate)); + thread->vfpstate.hard.used = 0; + /* fallthrough */ + + case THREAD_NOTIFY_EXIT: + vfpm_disable_access(); + vfpm_last_thread = NULL; + break; + + case THREAD_NOTIFY_SWITCH: + if (thread->vfpstate.hard.used) + vfpm_switch_to(thread); + else + vfpm_disable_access(); + + break; + + case THREAD_NOTIFY_COPY: + if (current_thread->vfpstate.hard.used) { + vfpm_save_context(¤t_thread->vfpstate); + + thread->vfpstate.hard.used = 1; + vfpm_last_thread = thread; + } else + memset(&thread->vfpstate, 0, sizeof(thread->vfpstate)); + break; + } + + return NOTIFY_DONE; +} + +static struct notifier_block vfpm_notifier_block = { + .notifier_call = vfpm_notifier, +}; + +static int __init vfpm_init(void) +{ + /* check for single-precision VFP operations */ + if (((readl(MVFR0) & MVFR0_SP_MASK) >> MVFR0_SP_BIT) != 0x2) + return 0; + + printk(KERN_INFO "ARMv7-M VFP coprocessor found\n"); + if (((readl(MVFR0) & MVFR0_DP_MASK) >> MVFR0_DP_BIT) == 0x2) + pr_info("VFP: Double precision floating points are supported\n"); + + writel(0, FPCCR); /* disable state preservation */ + vfpm_disable_access(); + + elf_hwcap |= HWCAP_VFP; + thread_register_notifier(&vfpm_notifier_block); + + return 0; +} + +late_initcall(vfpm_init);