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 diff --git a/arch/arm/boot/Makefile b/arch/arm/boot/Makefile index 54a09f9464fbd9..43515635335d68 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 @@ -78,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)'; \ @@ -86,9 +106,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/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); 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<