Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

RM-2901: Added support for the ARMv7-M VFP Extension #23

Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions arch/arm/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
25 changes: 25 additions & 0 deletions arch/arm/boot/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand All @@ -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)'; \
Expand All @@ -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 $@
Expand Down
240 changes: 240 additions & 0 deletions arch/arm/kernel/traps-v7m.c
Original file line number Diff line number Diff line change
@@ -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 <linux/module.h>
#include <linux/sched.h>
#include <linux/mm.h>
#include <linux/pagemap.h>
#include <linux/io.h>

#include <asm/cacheflush.h>
#include <asm/sections.h>
#include <asm/page.h>
#include <asm/setup.h>
#include <asm/mpu.h>
#include <asm/mach/arch.h>
#include <asm/system_misc.h>
#include <asm/exception.h>

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);
}

Loading