diff options
Diffstat (limited to 'arch/arm/kernel')
-rw-r--r-- | arch/arm/kernel/Makefile | 4 | ||||
-rw-r--r-- | arch/arm/kernel/entry-armv.S | 37 | ||||
-rw-r--r-- | arch/arm/kernel/entry-common.S | 11 | ||||
-rw-r--r-- | arch/arm/kernel/entry-header.S | 16 | ||||
-rw-r--r-- | arch/arm/kernel/head-common.S | 4 | ||||
-rw-r--r-- | arch/arm/kernel/init_task.c | 5 | ||||
-rw-r--r-- | arch/arm/kernel/kprobes.c | 19 | ||||
-rw-r--r-- | arch/arm/kernel/setup.c | 2 | ||||
-rw-r--r-- | arch/arm/kernel/smp.c | 23 | ||||
-rw-r--r-- | arch/arm/kernel/smp_twd.c | 4 | ||||
-rw-r--r-- | arch/arm/kernel/sys_arm.c | 1 | ||||
-rw-r--r-- | arch/arm/kernel/tcm.c | 246 | ||||
-rw-r--r-- | arch/arm/kernel/tcm.h | 17 | ||||
-rw-r--r-- | arch/arm/kernel/time.c | 1 | ||||
-rw-r--r-- | arch/arm/kernel/traps.c | 81 | ||||
-rw-r--r-- | arch/arm/kernel/vmlinux.lds.S | 57 |
16 files changed, 440 insertions, 88 deletions
diff --git a/arch/arm/kernel/Makefile b/arch/arm/kernel/Makefile index 3213c93..79087dd 100644 --- a/arch/arm/kernel/Makefile +++ b/arch/arm/kernel/Makefile @@ -2,7 +2,8 @@ # Makefile for the linux kernel. # -AFLAGS_head.o := -DTEXT_OFFSET=$(TEXT_OFFSET) +CPPFLAGS_vmlinux.lds := -DTEXT_OFFSET=$(TEXT_OFFSET) +AFLAGS_head.o := -DTEXT_OFFSET=$(TEXT_OFFSET) ifdef CONFIG_DYNAMIC_FTRACE CFLAGS_REMOVE_ftrace.o = -pg @@ -34,6 +35,7 @@ obj-$(CONFIG_OABI_COMPAT) += sys_oabi-compat.o obj-$(CONFIG_ARM_THUMBEE) += thumbee.o obj-$(CONFIG_KGDB) += kgdb.o obj-$(CONFIG_ARM_UNWIND) += unwind.o +obj-$(CONFIG_HAVE_TCM) += tcm.o obj-$(CONFIG_CRUNCH) += crunch.o crunch-bits.o AFLAGS_crunch-bits.o := -Wa,-mcpu=ep9312 diff --git a/arch/arm/kernel/entry-armv.S b/arch/arm/kernel/entry-armv.S index 3d727a8..322410b 100644 --- a/arch/arm/kernel/entry-armv.S +++ b/arch/arm/kernel/entry-armv.S @@ -272,7 +272,15 @@ __und_svc: @ @ r0 - instruction @ +#ifndef CONFIG_THUMB2_KERNEL ldr r0, [r2, #-4] +#else + ldrh r0, [r2, #-2] @ Thumb instruction at LR - 2 + and r9, r0, #0xf800 + cmp r9, #0xe800 @ 32-bit instruction if xx >= 0 + ldrhhs r9, [r2] @ bottom 16 bits + orrhs r0, r9, r0, lsl #16 +#endif adr r9, BSYM(1f) bl call_fpe @@ -303,22 +311,16 @@ __pabt_svc: tst r3, #PSR_I_BIT biceq r9, r9, #PSR_I_BIT - @ - @ set args, then call main handler - @ - @ r0 - address of faulting instruction - @ r1 - pointer to registers on stack - @ -#ifdef MULTI_PABORT mov r0, r2 @ pass address of aborted instruction. +#ifdef MULTI_PABORT ldr r4, .LCprocfns mov lr, pc ldr pc, [r4, #PROCESSOR_PABT_FUNC] #else - CPU_PABORT_HANDLER(r0, r2) + bl CPU_PABORT_HANDLER #endif msr cpsr_c, r9 @ Maybe enable interrupts - mov r1, sp @ regs + mov r2, sp @ regs bl do_PrefetchAbort @ call abort handler @ @@ -678,7 +680,9 @@ ENTRY(fp_enter) .word no_fp .previous -no_fp: mov pc, lr +ENTRY(no_fp) + mov pc, lr +ENDPROC(no_fp) __und_usr_unknown: enable_irq @@ -691,16 +695,16 @@ ENDPROC(__und_usr_unknown) __pabt_usr: usr_entry -#ifdef MULTI_PABORT mov r0, r2 @ pass address of aborted instruction. +#ifdef MULTI_PABORT ldr r4, .LCprocfns mov lr, pc ldr pc, [r4, #PROCESSOR_PABT_FUNC] #else - CPU_PABORT_HANDLER(r0, r2) + bl CPU_PABORT_HANDLER #endif enable_irq @ Enable interrupts - mov r1, sp @ regs + mov r2, sp @ regs bl do_PrefetchAbort @ call abort handler UNWIND(.fnend ) /* fall through */ @@ -734,13 +738,6 @@ ENTRY(__switch_to) #ifdef CONFIG_MMU ldr r6, [r2, #TI_CPU_DOMAIN] #endif -#if __LINUX_ARM_ARCH__ >= 6 -#ifdef CONFIG_CPU_32v6K - clrex -#else - strex r5, r4, [ip] @ Clear exclusive monitor -#endif -#endif #if defined(CONFIG_HAS_TLS_REG) mcr p15, 0, r3, c13, c0, 3 @ set TLS register #elif !defined(CONFIG_TLS_REG_EMUL) diff --git a/arch/arm/kernel/entry-common.S b/arch/arm/kernel/entry-common.S index 807cfeb..f0fe95b 100644 --- a/arch/arm/kernel/entry-common.S +++ b/arch/arm/kernel/entry-common.S @@ -126,7 +126,7 @@ ENTRY(__gnu_mcount_nc) cmp r0, r2 bne gnu_trace ldmia sp!, {r0-r3, ip, lr} - bx ip + mov pc, ip gnu_trace: ldr r1, [sp, #20] @ lr of instrumented routine @@ -135,7 +135,7 @@ gnu_trace: mov lr, pc mov pc, r2 ldmia sp!, {r0-r3, ip, lr} - bx ip + mov pc, ip ENTRY(mcount) stmdb sp!, {r0-r3, lr} @@ -425,13 +425,6 @@ sys_mmap2: #endif ENDPROC(sys_mmap2) -ENTRY(pabort_ifar) - mrc p15, 0, r0, cr6, cr0, 2 -ENTRY(pabort_noifar) - mov pc, lr -ENDPROC(pabort_ifar) -ENDPROC(pabort_noifar) - #ifdef CONFIG_OABI_COMPAT /* diff --git a/arch/arm/kernel/entry-header.S b/arch/arm/kernel/entry-header.S index a4eaf4f..ac34c0d 100644 --- a/arch/arm/kernel/entry-header.S +++ b/arch/arm/kernel/entry-header.S @@ -76,13 +76,27 @@ #ifndef CONFIG_THUMB2_KERNEL .macro svc_exit, rpsr msr spsr_cxsf, \rpsr +#if defined(CONFIG_CPU_32v6K) + clrex @ clear the exclusive monitor ldmia sp, {r0 - pc}^ @ load r0 - pc, cpsr +#elif defined (CONFIG_CPU_V6) + ldr r0, [sp] + strex r1, r2, [sp] @ clear the exclusive monitor + ldmib sp, {r1 - pc}^ @ load r1 - pc, cpsr +#else + ldmia sp, {r0 - pc}^ @ load r0 - pc, cpsr +#endif .endm .macro restore_user_regs, fast = 0, offset = 0 ldr r1, [sp, #\offset + S_PSR] @ get calling cpsr ldr lr, [sp, #\offset + S_PC]! @ get pc msr spsr_cxsf, r1 @ save in spsr_svc +#if defined(CONFIG_CPU_32v6K) + clrex @ clear the exclusive monitor +#elif defined (CONFIG_CPU_V6) + strex r1, r2, [sp] @ clear the exclusive monitor +#endif .if \fast ldmdb sp, {r1 - lr}^ @ get calling r1 - lr .else @@ -98,6 +112,7 @@ .endm #else /* CONFIG_THUMB2_KERNEL */ .macro svc_exit, rpsr + clrex @ clear the exclusive monitor ldr r0, [sp, #S_SP] @ top of the stack ldr r1, [sp, #S_PC] @ return address tst r0, #4 @ orig stack 8-byte aligned? @@ -110,6 +125,7 @@ .endm .macro restore_user_regs, fast = 0, offset = 0 + clrex @ clear the exclusive monitor mov r2, sp load_user_sp_lr r2, r3, \offset + S_SP @ calling sp, lr ldr r1, [sp, #\offset + S_PSR] @ get calling cpsr diff --git a/arch/arm/kernel/head-common.S b/arch/arm/kernel/head-common.S index 93ad576..885a721 100644 --- a/arch/arm/kernel/head-common.S +++ b/arch/arm/kernel/head-common.S @@ -13,6 +13,7 @@ #define ATAG_CORE 0x54410001 #define ATAG_CORE_SIZE ((2*4 + 3*4) >> 2) +#define ATAG_CORE_SIZE_EMPTY ((2*4) >> 2) .align 2 .type __switch_data, %object @@ -251,7 +252,8 @@ __vet_atags: bne 1f ldr r5, [r2, #0] @ is first tag ATAG_CORE? - subs r5, r5, #ATAG_CORE_SIZE + cmp r5, #ATAG_CORE_SIZE + cmpne r5, #ATAG_CORE_SIZE_EMPTY bne 1f ldr r5, [r2, #4] ldr r6, =ATAG_CORE diff --git a/arch/arm/kernel/init_task.c b/arch/arm/kernel/init_task.c index 3f47086..e7cbb50 100644 --- a/arch/arm/kernel/init_task.c +++ b/arch/arm/kernel/init_task.c @@ -24,9 +24,8 @@ static struct sighand_struct init_sighand = INIT_SIGHAND(init_sighand); * * The things we do for performance.. */ -union thread_union init_thread_union - __attribute__((__section__(".data.init_task"))) = - { INIT_THREAD_INFO(init_task) }; +union thread_union init_thread_union __init_task_data = + { INIT_THREAD_INFO(init_task) }; /* * Initial task structure. diff --git a/arch/arm/kernel/kprobes.c b/arch/arm/kernel/kprobes.c index f692efd..60c62c3 100644 --- a/arch/arm/kernel/kprobes.c +++ b/arch/arm/kernel/kprobes.c @@ -22,6 +22,7 @@ #include <linux/kernel.h> #include <linux/kprobes.h> #include <linux/module.h> +#include <linux/stop_machine.h> #include <linux/stringify.h> #include <asm/traps.h> #include <asm/cacheflush.h> @@ -83,10 +84,24 @@ void __kprobes arch_arm_kprobe(struct kprobe *p) flush_insns(p->addr, 1); } +/* + * The actual disarming is done here on each CPU and synchronized using + * stop_machine. This synchronization is necessary on SMP to avoid removing + * a probe between the moment the 'Undefined Instruction' exception is raised + * and the moment the exception handler reads the faulting instruction from + * memory. + */ +int __kprobes __arch_disarm_kprobe(void *p) +{ + struct kprobe *kp = p; + *kp->addr = kp->opcode; + flush_insns(kp->addr, 1); + return 0; +} + void __kprobes arch_disarm_kprobe(struct kprobe *p) { - *p->addr = p->opcode; - flush_insns(p->addr, 1); + stop_machine(__arch_disarm_kprobe, p, &cpu_online_map); } void __kprobes arch_remove_kprobe(struct kprobe *p) diff --git a/arch/arm/kernel/setup.c b/arch/arm/kernel/setup.c index d4d4f77..c6c57b6 100644 --- a/arch/arm/kernel/setup.c +++ b/arch/arm/kernel/setup.c @@ -45,6 +45,7 @@ #include "compat.h" #include "atags.h" +#include "tcm.h" #ifndef MEM_SIZE #define MEM_SIZE (16*1024*1024) @@ -749,6 +750,7 @@ void __init setup_arch(char **cmdline_p) #endif cpu_init(); + tcm_init(); /* * Set up various architecture-specific pointers diff --git a/arch/arm/kernel/smp.c b/arch/arm/kernel/smp.c index de885fd..57162af 100644 --- a/arch/arm/kernel/smp.c +++ b/arch/arm/kernel/smp.c @@ -36,6 +36,7 @@ #include <asm/tlbflush.h> #include <asm/ptrace.h> #include <asm/localtimer.h> +#include <asm/smp_plat.h> /* * as from 2.5, kernels no longer have an init_tasks structure @@ -153,7 +154,7 @@ int __cpuinit __cpu_up(unsigned int cpu) /* * __cpu_disable runs on the processor to be shutdown. */ -int __cpuexit __cpu_disable(void) +int __cpu_disable(void) { unsigned int cpu = smp_processor_id(); struct task_struct *p; @@ -189,7 +190,7 @@ int __cpuexit __cpu_disable(void) read_lock(&tasklist_lock); for_each_process(p) { if (p->mm) - cpu_clear(cpu, p->mm->cpu_vm_mask); + cpumask_clear_cpu(cpu, mm_cpumask(p->mm)); } read_unlock(&tasklist_lock); @@ -200,7 +201,7 @@ int __cpuexit __cpu_disable(void) * called on the thread which is asking for a CPU to be shutdown - * waits until shutdown has completed, or it is timed out. */ -void __cpuexit __cpu_die(unsigned int cpu) +void __cpu_die(unsigned int cpu) { if (!platform_cpu_kill(cpu)) printk("CPU%u: unable to kill\n", cpu); @@ -214,7 +215,7 @@ void __cpuexit __cpu_die(unsigned int cpu) * of the other hotplug-cpu capable cores, so presumably coming * out of idle fixes this. */ -void __cpuexit cpu_die(void) +void __ref cpu_die(void) { unsigned int cpu = smp_processor_id(); @@ -257,7 +258,7 @@ asmlinkage void __cpuinit secondary_start_kernel(void) atomic_inc(&mm->mm_users); atomic_inc(&mm->mm_count); current->active_mm = mm; - cpu_set(cpu, mm->cpu_vm_mask); + cpumask_set_cpu(cpu, mm_cpumask(mm)); cpu_switch_mm(mm->pgd, mm); enter_lazy_tlb(mm, current); local_flush_tlb_all(); @@ -586,12 +587,6 @@ struct tlb_args { unsigned long ta_end; }; -/* all SMP configurations have the extended CPUID registers */ -static inline int tlb_ops_need_broadcast(void) -{ - return ((read_cpuid_ext(CPUID_EXT_MMFR3) >> 12) & 0xf) < 2; -} - static inline void ipi_flush_tlb_all(void *ignored) { local_flush_tlb_all(); @@ -643,7 +638,7 @@ void flush_tlb_all(void) void flush_tlb_mm(struct mm_struct *mm) { if (tlb_ops_need_broadcast()) - on_each_cpu_mask(ipi_flush_tlb_mm, mm, 1, &mm->cpu_vm_mask); + on_each_cpu_mask(ipi_flush_tlb_mm, mm, 1, mm_cpumask(mm)); else local_flush_tlb_mm(mm); } @@ -654,7 +649,7 @@ void flush_tlb_page(struct vm_area_struct *vma, unsigned long uaddr) struct tlb_args ta; ta.ta_vma = vma; ta.ta_start = uaddr; - on_each_cpu_mask(ipi_flush_tlb_page, &ta, 1, &vma->vm_mm->cpu_vm_mask); + on_each_cpu_mask(ipi_flush_tlb_page, &ta, 1, mm_cpumask(vma->vm_mm)); } else local_flush_tlb_page(vma, uaddr); } @@ -677,7 +672,7 @@ void flush_tlb_range(struct vm_area_struct *vma, ta.ta_vma = vma; ta.ta_start = start; ta.ta_end = end; - on_each_cpu_mask(ipi_flush_tlb_range, &ta, 1, &vma->vm_mm->cpu_vm_mask); + on_each_cpu_mask(ipi_flush_tlb_range, &ta, 1, mm_cpumask(vma->vm_mm)); } else local_flush_tlb_range(vma, start, end); } diff --git a/arch/arm/kernel/smp_twd.c b/arch/arm/kernel/smp_twd.c index d8c88c6..a73a34d 100644 --- a/arch/arm/kernel/smp_twd.c +++ b/arch/arm/kernel/smp_twd.c @@ -166,10 +166,12 @@ void __cpuinit twd_timer_setup(struct clock_event_device *clk) clockevents_register_device(clk); } +#ifdef CONFIG_HOTPLUG_CPU /* * take a local timer down */ -void __cpuexit twd_timer_stop(void) +void twd_timer_stop(void) { __raw_writel(0, twd_base + TWD_TIMER_CONTROL); } +#endif diff --git a/arch/arm/kernel/sys_arm.c b/arch/arm/kernel/sys_arm.c index b3ec641..78ecaac 100644 --- a/arch/arm/kernel/sys_arm.c +++ b/arch/arm/kernel/sys_arm.c @@ -25,7 +25,6 @@ #include <linux/mman.h> #include <linux/fs.h> #include <linux/file.h> -#include <linux/utsname.h> #include <linux/ipc.h> #include <linux/uaccess.h> diff --git a/arch/arm/kernel/tcm.c b/arch/arm/kernel/tcm.c new file mode 100644 index 0000000..e503038 --- /dev/null +++ b/arch/arm/kernel/tcm.c @@ -0,0 +1,246 @@ +/* + * Copyright (C) 2008-2009 ST-Ericsson AB + * License terms: GNU General Public License (GPL) version 2 + * TCM memory handling for ARM systems + * + * Author: Linus Walleij <linus.walleij@stericsson.com> + * Author: Rickard Andersson <rickard.andersson@stericsson.com> + */ +#include <linux/init.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/stddef.h> +#include <linux/ioport.h> +#include <linux/genalloc.h> +#include <linux/string.h> /* memcpy */ +#include <asm/page.h> /* PAGE_SHIFT */ +#include <asm/cputype.h> +#include <asm/mach/map.h> +#include <mach/memory.h> +#include "tcm.h" + +/* Scream and warn about misuse */ +#if !defined(ITCM_OFFSET) || !defined(ITCM_END) || \ + !defined(DTCM_OFFSET) || !defined(DTCM_END) +#error "TCM support selected but offsets not defined!" +#endif + +static struct gen_pool *tcm_pool; + +/* TCM section definitions from the linker */ +extern char __itcm_start, __sitcm_text, __eitcm_text; +extern char __dtcm_start, __sdtcm_data, __edtcm_data; + +/* + * TCM memory resources + */ +static struct resource dtcm_res = { + .name = "DTCM RAM", + .start = DTCM_OFFSET, + .end = DTCM_END, + .flags = IORESOURCE_MEM +}; + +static struct resource itcm_res = { + .name = "ITCM RAM", + .start = ITCM_OFFSET, + .end = ITCM_END, + .flags = IORESOURCE_MEM +}; + +static struct map_desc dtcm_iomap[] __initdata = { + { + .virtual = DTCM_OFFSET, + .pfn = __phys_to_pfn(DTCM_OFFSET), + .length = (DTCM_END - DTCM_OFFSET + 1), + .type = MT_UNCACHED + } +}; + +static struct map_desc itcm_iomap[] __initdata = { + { + .virtual = ITCM_OFFSET, + .pfn = __phys_to_pfn(ITCM_OFFSET), + .length = (ITCM_END - ITCM_OFFSET + 1), + .type = MT_UNCACHED + } +}; + +/* + * Allocate a chunk of TCM memory + */ +void *tcm_alloc(size_t len) +{ + unsigned long vaddr; + + if (!tcm_pool) + return NULL; + + vaddr = gen_pool_alloc(tcm_pool, len); + if (!vaddr) + return NULL; + + return (void *) vaddr; +} +EXPORT_SYMBOL(tcm_alloc); + +/* + * Free a chunk of TCM memory + */ +void tcm_free(void *addr, size_t len) +{ + gen_pool_free(tcm_pool, (unsigned long) addr, len); +} +EXPORT_SYMBOL(tcm_free); + + +static void __init setup_tcm_bank(u8 type, u32 offset, u32 expected_size) +{ + const int tcm_sizes[16] = { 0, -1, -1, 4, 8, 16, 32, 64, 128, + 256, 512, 1024, -1, -1, -1, -1 }; + u32 tcm_region; + int tcm_size; + + /* Read the special TCM region register c9, 0 */ + if (!type) + asm("mrc p15, 0, %0, c9, c1, 0" + : "=r" (tcm_region)); + else + asm("mrc p15, 0, %0, c9, c1, 1" + : "=r" (tcm_region)); + + tcm_size = tcm_sizes[(tcm_region >> 2) & 0x0f]; + if (tcm_size < 0) { + pr_err("CPU: %sTCM of unknown size!\n", + type ? "I" : "D"); + } else { + pr_info("CPU: found %sTCM %dk @ %08x, %senabled\n", + type ? "I" : "D", + tcm_size, + (tcm_region & 0xfffff000U), + (tcm_region & 1) ? "" : "not "); + } + + if (tcm_size != expected_size) { + pr_crit("CPU: %sTCM was detected %dk but expected %dk!\n", + type ? "I" : "D", + tcm_size, + expected_size); + /* Adjust to the expected size? what can we do... */ + } + + /* Force move the TCM bank to where we want it, enable */ + tcm_region = offset | (tcm_region & 0x00000ffeU) | 1; + + if (!type) + asm("mcr p15, 0, %0, c9, c1, 0" + : /* No output operands */ + : "r" (tcm_region)); + else + asm("mcr p15, 0, %0, c9, c1, 1" + : /* No output operands */ + : "r" (tcm_region)); + + pr_debug("CPU: moved %sTCM %dk to %08x, enabled\n", + type ? "I" : "D", + tcm_size, + (tcm_region & 0xfffff000U)); +} + +/* + * This initializes the TCM memory + */ +void __init tcm_init(void) +{ + u32 tcm_status = read_cpuid_tcmstatus(); + char *start; + char *end; + char *ram; + + /* Setup DTCM if present */ + if (tcm_status & (1 << 16)) { + setup_tcm_bank(0, DTCM_OFFSET, + (DTCM_END - DTCM_OFFSET + 1) >> 10); + request_resource(&iomem_resource, &dtcm_res); + iotable_init(dtcm_iomap, 1); + /* Copy data from RAM to DTCM */ + start = &__sdtcm_data; + end = &__edtcm_data; + ram = &__dtcm_start; + memcpy(start, ram, (end-start)); + pr_debug("CPU DTCM: copied data from %p - %p\n", start, end); + } + + /* Setup ITCM if present */ + if (tcm_status & 1) { + setup_tcm_bank(1, ITCM_OFFSET, + (ITCM_END - ITCM_OFFSET + 1) >> 10); + request_resource(&iomem_resource, &itcm_res); + iotable_init(itcm_iomap, 1); + /* Copy code from RAM to ITCM */ + start = &__sitcm_text; + end = &__eitcm_text; + ram = &__itcm_start; + memcpy(start, ram, (end-start)); + pr_debug("CPU ITCM: copied code from %p - %p\n", start, end); + } +} + +/* + * This creates the TCM memory pool and has to be done later, + * during the core_initicalls, since the allocator is not yet + * up and running when the first initialization runs. + */ +static int __init setup_tcm_pool(void) +{ + u32 tcm_status = read_cpuid_tcmstatus(); + u32 dtcm_pool_start = (u32) &__edtcm_data; + u32 itcm_pool_start = (u32) &__eitcm_text; + int ret; + + /* + * Set up malloc pool, 2^2 = 4 bytes granularity since + * the TCM is sometimes just 4 KiB. NB: pages and cache + * line alignments does not matter in TCM! + */ + tcm_pool = gen_pool_create(2, -1); + + pr_debug("Setting up TCM memory pool\n"); + + /* Add the rest of DTCM to the TCM pool */ + if (tcm_status & (1 << 16)) { + if (dtcm_pool_start < DTCM_END) { + ret = gen_pool_add(tcm_pool, dtcm_pool_start, + DTCM_END - dtcm_pool_start + 1, -1); + if (ret) { + pr_err("CPU DTCM: could not add DTCM " \ + "remainder to pool!\n"); + return ret; + } + pr_debug("CPU DTCM: Added %08x bytes @ %08x to " \ + "the TCM memory pool\n", + DTCM_END - dtcm_pool_start + 1, + dtcm_pool_start); + } + } + + /* Add the rest of ITCM to the TCM pool */ + if (tcm_status & 1) { + if (itcm_pool_start < ITCM_END) { + ret = gen_pool_add(tcm_pool, itcm_pool_start, + ITCM_END - itcm_pool_start + 1, -1); + if (ret) { + pr_err("CPU ITCM: could not add ITCM " \ + "remainder to pool!\n"); + return ret; + } + pr_debug("CPU ITCM: Added %08x bytes @ %08x to " \ + "the TCM memory pool\n", + ITCM_END - itcm_pool_start + 1, + itcm_pool_start); + } + } + return 0; +} + +core_initcall(setup_tcm_pool); diff --git a/arch/arm/kernel/tcm.h b/arch/arm/kernel/tcm.h new file mode 100644 index 0000000..8015ad4 --- /dev/null +++ b/arch/arm/kernel/tcm.h @@ -0,0 +1,17 @@ +/* + * Copyright (C) 2008-2009 ST-Ericsson AB + * License terms: GNU General Public License (GPL) version 2 + * TCM memory handling for ARM systems + * + * Author: Linus Walleij <linus.walleij@stericsson.com> + * Author: Rickard Andersson <rickard.andersson@stericsson.com> + */ + +#ifdef CONFIG_HAVE_TCM +void __init tcm_init(void); +#else +/* No TCM support, just blank inlines to be optimized out */ +inline void tcm_init(void) +{ +} +#endif diff --git a/arch/arm/kernel/time.c b/arch/arm/kernel/time.c index 4cdc4a0..d38cdf2 100644 --- a/arch/arm/kernel/time.c +++ b/arch/arm/kernel/time.c @@ -21,6 +21,7 @@ #include <linux/interrupt.h> #include <linux/time.h> #include <linux/init.h> +#include <linux/sched.h> #include <linux/smp.h> #include <linux/timex.h> #include <linux/errno.h> diff --git a/arch/arm/kernel/traps.c b/arch/arm/kernel/traps.c index 57eb0f6..f838f36 100644 --- a/arch/arm/kernel/traps.c +++ b/arch/arm/kernel/traps.c @@ -45,21 +45,21 @@ static int __init user_debug_setup(char *str) __setup("user_debug=", user_debug_setup); #endif -static void dump_mem(const char *str, unsigned long bottom, unsigned long top); +static void dump_mem(const char *, const char *, unsigned long, unsigned long); void dump_backtrace_entry(unsigned long where, unsigned long from, unsigned long frame) { #ifdef CONFIG_KALLSYMS - printk("[<%08lx>] ", where); - print_symbol("(%s) ", where); - printk("from [<%08lx>] ", from); - print_symbol("(%s)\n", from); + char sym1[KSYM_SYMBOL_LEN], sym2[KSYM_SYMBOL_LEN]; + sprint_symbol(sym1, where); + sprint_symbol(sym2, from); + printk("[<%08lx>] (%s) from [<%08lx>] (%s)\n", where, sym1, from, sym2); #else printk("Function entered at [<%08lx>] from [<%08lx>]\n", where, from); #endif if (in_exception_text(where)) - dump_mem("Exception stack", frame + 4, frame + 4 + sizeof(struct pt_regs)); + dump_mem("", "Exception stack", frame + 4, frame + 4 + sizeof(struct pt_regs)); } #ifndef CONFIG_ARM_UNWIND @@ -81,9 +81,10 @@ static int verify_stack(unsigned long sp) /* * Dump out the contents of some memory nicely... */ -static void dump_mem(const char *str, unsigned long bottom, unsigned long top) +static void dump_mem(const char *lvl, const char *str, unsigned long bottom, + unsigned long top) { - unsigned long p = bottom & ~31; + unsigned long first; mm_segment_t fs; int i; @@ -95,33 +96,37 @@ static void dump_mem(const char *str, unsigned long bottom, unsigned long top) fs = get_fs(); set_fs(KERNEL_DS); - printk("%s(0x%08lx to 0x%08lx)\n", str, bottom, top); + printk("%s%s(0x%08lx to 0x%08lx)\n", lvl, str, bottom, top); - for (p = bottom & ~31; p < top;) { - printk("%04lx: ", p & 0xffff); + for (first = bottom & ~31; first < top; first += 32) { + unsigned long p; + char str[sizeof(" 12345678") * 8 + 1]; - for (i = 0; i < 8; i++, p += 4) { - unsigned int val; + memset(str, ' ', sizeof(str)); + str[sizeof(str) - 1] = '\0'; - if (p < bottom || p >= top) - printk(" "); - else { - __get_user(val, (unsigned long *)p); - printk("%08x ", val); + for (p = first, i = 0; i < 8 && p < top; i++, p += 4) { + if (p >= bottom && p < top) { + unsigned long val; + if (__get_user(val, (unsigned long *)p) == 0) + sprintf(str + i * 9, " %08lx", val); + else + sprintf(str + i * 9, " ????????"); } } - printk ("\n"); + printk("%s%04lx:%s\n", lvl, first & 0xffff, str); } set_fs(fs); } -static void dump_instr(struct pt_regs *regs) +static void dump_instr(const char *lvl, struct pt_regs *regs) { unsigned long addr = instruction_pointer(regs); const int thumb = thumb_mode(regs); const int width = thumb ? 4 : 8; mm_segment_t fs; + char str[sizeof("00000000 ") * 5 + 2 + 1], *p = str; int i; /* @@ -132,7 +137,6 @@ static void dump_instr(struct pt_regs *regs) fs = get_fs(); set_fs(KERNEL_DS); - printk("Code: "); for (i = -4; i < 1; i++) { unsigned int val, bad; @@ -142,13 +146,14 @@ static void dump_instr(struct pt_regs *regs) bad = __get_user(val, &((u32 *)addr)[i]); if (!bad) - printk(i == 0 ? "(%0*x) " : "%0*x ", width, val); + p += sprintf(p, i == 0 ? "(%0*x) " : "%0*x ", + width, val); else { - printk("bad PC value."); + p += sprintf(p, "bad PC value"); break; } } - printk("\n"); + printk("%sCode: %s\n", lvl, str); set_fs(fs); } @@ -224,18 +229,19 @@ static void __die(const char *str, int err, struct thread_info *thread, struct p struct task_struct *tsk = thread->task; static int die_counter; - printk("Internal error: %s: %x [#%d]" S_PREEMPT S_SMP "\n", + printk(KERN_EMERG "Internal error: %s: %x [#%d]" S_PREEMPT S_SMP "\n", str, err, ++die_counter); + sysfs_printk_last_file(); print_modules(); __show_regs(regs); - printk("Process %s (pid: %d, stack limit = 0x%p)\n", - tsk->comm, task_pid_nr(tsk), thread + 1); + printk(KERN_EMERG "Process %.*s (pid: %d, stack limit = 0x%p)\n", + TASK_COMM_LEN, tsk->comm, task_pid_nr(tsk), thread + 1); if (!user_mode(regs) || in_interrupt()) { - dump_mem("Stack: ", regs->ARM_sp, + dump_mem(KERN_EMERG, "Stack: ", regs->ARM_sp, THREAD_SIZE + (unsigned long)task_stack_page(tsk)); dump_backtrace(regs, tsk); - dump_instr(regs); + dump_instr(KERN_EMERG, regs); } } @@ -250,13 +256,14 @@ NORET_TYPE void die(const char *str, struct pt_regs *regs, int err) oops_enter(); - console_verbose(); spin_lock_irq(&die_lock); + console_verbose(); bust_spinlocks(1); __die(str, err, thread, regs); bust_spinlocks(0); add_taint(TAINT_DIE); spin_unlock_irq(&die_lock); + oops_exit(); if (in_interrupt()) panic("Fatal exception in interrupt"); @@ -264,7 +271,6 @@ NORET_TYPE void die(const char *str, struct pt_regs *regs, int err) if (panic_on_oops) panic("Fatal exception"); - oops_exit(); do_exit(SIGSEGV); } @@ -349,7 +355,7 @@ asmlinkage void __exception do_undefinstr(struct pt_regs *regs) if (user_debug & UDBG_UNDEFINED) { printk(KERN_INFO "%s (%d): undefined instruction: pc=%p\n", current->comm, task_pid_nr(current), pc); - dump_instr(regs); + dump_instr(KERN_INFO, regs); } #endif @@ -400,7 +406,7 @@ static int bad_syscall(int n, struct pt_regs *regs) if (user_debug & UDBG_SYSCALL) { printk(KERN_ERR "[%d] %s: obsolete system call %08x.\n", task_pid_nr(current), current->comm, n); - dump_instr(regs); + dump_instr(KERN_ERR, regs); } #endif @@ -418,12 +424,14 @@ static int bad_syscall(int n, struct pt_regs *regs) static inline void do_cache_op(unsigned long start, unsigned long end, int flags) { + struct mm_struct *mm = current->active_mm; struct vm_area_struct *vma; if (end < start || flags) return; - vma = find_vma(current->active_mm, start); + down_read(&mm->mmap_sem); + vma = find_vma(mm, start); if (vma && vma->vm_start < end) { if (start < vma->vm_start) start = vma->vm_start; @@ -432,6 +440,7 @@ do_cache_op(unsigned long start, unsigned long end, int flags) flush_cache_user_range(vma, start, end); } + up_read(&mm->mmap_sem); } /* @@ -576,7 +585,7 @@ asmlinkage int arm_syscall(int no, struct pt_regs *regs) if (user_debug & UDBG_SYSCALL) { printk("[%d] %s: arm syscall %d\n", task_pid_nr(current), current->comm, no); - dump_instr(regs); + dump_instr("", regs); if (user_mode(regs)) { __show_regs(regs); c_backtrace(regs->ARM_fp, processor_mode(regs)); @@ -653,7 +662,7 @@ baddataabort(int code, unsigned long instr, struct pt_regs *regs) if (user_debug & UDBG_BADABORT) { printk(KERN_ERR "[%d] %s: bad data abort: code %d instr 0x%08lx\n", task_pid_nr(current), current->comm, code, instr); - dump_instr(regs); + dump_instr(KERN_ERR, regs); show_pte(current->mm, addr); } #endif diff --git a/arch/arm/kernel/vmlinux.lds.S b/arch/arm/kernel/vmlinux.lds.S index 5cc4812..aecf87df 100644 --- a/arch/arm/kernel/vmlinux.lds.S +++ b/arch/arm/kernel/vmlinux.lds.S @@ -199,6 +199,63 @@ SECTIONS } _edata_loc = __data_loc + SIZEOF(.data); +#ifdef CONFIG_HAVE_TCM + /* + * We align everything to a page boundary so we can + * free it after init has commenced and TCM contents have + * been copied to its destination. + */ + .tcm_start : { + . = ALIGN(PAGE_SIZE); + __tcm_start = .; + __itcm_start = .; + } + + /* + * Link these to the ITCM RAM + * Put VMA to the TCM address and LMA to the common RAM + * and we'll upload the contents from RAM to TCM and free + * the used RAM after that. + */ + .text_itcm ITCM_OFFSET : AT(__itcm_start) + { + __sitcm_text = .; + *(.tcm.text) + *(.tcm.rodata) + . = ALIGN(4); + __eitcm_text = .; + } + + /* + * Reset the dot pointer, this is needed to create the + * relative __dtcm_start below (to be used as extern in code). + */ + . = ADDR(.tcm_start) + SIZEOF(.tcm_start) + SIZEOF(.text_itcm); + + .dtcm_start : { + __dtcm_start = .; + } + + /* TODO: add remainder of ITCM as well, that can be used for data! */ + .data_dtcm DTCM_OFFSET : AT(__dtcm_start) + { + . = ALIGN(4); + __sdtcm_data = .; + *(.tcm.data) + . = ALIGN(4); + __edtcm_data = .; + } + + /* Reset the dot pointer or the linker gets confused */ + . = ADDR(.dtcm_start) + SIZEOF(.data_dtcm); + + /* End marker for freeing TCM copy in linked object */ + .tcm_end : AT(ADDR(.dtcm_start) + SIZEOF(.data_dtcm)){ + . = ALIGN(PAGE_SIZE); + __tcm_end = .; + } +#endif + .bss : { __bss_start = .; /* BSS */ *(.bss) |