2012-11-12 11:49:43

by Vineet Gupta

[permalink] [raw]
Subject: [RFC Patch v1 00/55] Addons to Synopsys ARC Linux kernel Port

From: Vineet Gupta <[email protected]>

Hi,

This is a follow-up on ARC Linux port, whose #1 was posted last week.
This is part #2 of the port containing optional features.

Kindly review and provide comments !

The v2 series incorporating the review comments will combine both #1 and #2.

Thx,
-Vineet

Mischa Jonker (1):
ARC: kgdb support

Vineet Gupta (23):
ARC: [optim] Cache "current" in Register r25
ARC: ptrace support
ARC: futex
ARC: oprofile support
ARC: ARCompact 2 levels IRQ (high/low priority) Handling
ARC: dynamic loadable module support
ARC: Low level event capture/logging
ARC: kernel diagnostics: show_regs() etc
ARC: SMP support
ARC: dwarf2 stack unwinder
ARC: stacktracing APIs based on dw2 unwinder
ARC: disassembly (needed by kprobes/kgdb/unaligned-access-emul)
ARC: kprobes support
ARC: unaligned access emulation
ARC: startup #2: Verbose Boot reporting / feature verification
ARC: [plat-arfpga] BVCI Latency Unit setup
perf, ARC: Enable building perf tools for ARC
ARC: perf support (software counters only)
modpost: Ignore ARC specific non-alloc section
ARC: Support for single cycle Close Coupled Mem (CCM)
ARC: Hostlink Pseudo-Driver for Metaware Debugger
ARC: [plat-arcfpga] defconfig
ARC: Add self to MAINTAINERS

MAINTAINERS | 6 +
arch/arc/Kconfig | 136 +++
arch/arc/Makefile | 19 +
arch/arc/configs/fpga_defconfig | 137 +++-
arch/arc/include/asm/Kbuild | 2 -
arch/arc/include/asm/arcregs.h | 122 +++-
arch/arc/include/asm/current.h | 32 +
arch/arc/include/asm/defines.h | 56 ++
arch/arc/include/asm/disasm.h | 116 +++
arch/arc/include/asm/entry.h | 189 +++++
arch/arc/include/asm/event-log-asm.h | 185 +++++
arch/arc/include/asm/event-log.h | 102 +++
arch/arc/include/asm/futex.h | 151 ++++
arch/arc/include/asm/irqflags.h | 6 +-
arch/arc/include/asm/kgdb.h | 61 ++
arch/arc/include/asm/kprobes.h | 62 ++
arch/arc/include/asm/linkage.h | 33 +
arch/arc/include/asm/mmu_context.h | 4 +
arch/arc/include/asm/module.h | 11 +
arch/arc/include/asm/mutex.h | 9 +
arch/arc/include/asm/perf_event.h | 19 +
arch/arc/include/asm/pgtable.h | 4 +
arch/arc/include/asm/processor.h | 11 +
arch/arc/include/asm/ptrace.h | 19 +-
arch/arc/include/asm/setup.h | 14 +
arch/arc/include/asm/smp.h | 108 +++
arch/arc/include/asm/unaligned.h | 29 +
arch/arc/include/asm/unwind.h | 163 ++++
arch/arc/kernel/Makefile | 19 +-
arch/arc/kernel/arc_hostlink.c | 114 +++
arch/arc/kernel/asm-offsets.c | 18 +
arch/arc/kernel/ctx_sw.c | 18 +
arch/arc/kernel/disasm.c | 538 ++++++++++++
arch/arc/kernel/entry.S | 259 ++++++-
arch/arc/kernel/event-log.c | 304 +++++++
arch/arc/kernel/head.S | 33 +
arch/arc/kernel/irq.c | 109 +++
arch/arc/kernel/kgdb.c | 205 +++++
arch/arc/kernel/kprobes.c | 525 ++++++++++++
arch/arc/kernel/module.c | 159 ++++
arch/arc/kernel/ptrace.c | 164 ++++-
arch/arc/kernel/setup.c | 281 +++++++-
arch/arc/kernel/signal.c | 3 +
arch/arc/kernel/smp.c | 295 +++++++
arch/arc/kernel/stacktrace.c | 215 +++++-
arch/arc/kernel/traps.c | 45 +
arch/arc/kernel/troubleshoot.c | 304 +++++++
arch/arc/kernel/unaligned.c | 225 +++++
arch/arc/kernel/unwind.c | 1329 ++++++++++++++++++++++++++++++
arch/arc/kernel/vmlinux.lds.S | 44 +-
arch/arc/mm/cache_arc700.c | 46 +
arch/arc/mm/tlb.c | 47 ++
arch/arc/mm/tlbex.S | 70 ++-
arch/arc/oprofile/Makefile | 9 +
arch/arc/oprofile/common.c | 22 +
arch/arc/plat-arcfpga/Kconfig | 57 ++
arch/arc/plat-arcfpga/Makefile | 1 +
arch/arc/plat-arcfpga/include/plat/irq.h | 10 +-
arch/arc/plat-arcfpga/include/plat/smp.h | 115 +++
arch/arc/plat-arcfpga/irq.c | 10 +
arch/arc/plat-arcfpga/platform.c | 56 ++
arch/arc/plat-arcfpga/smp.c | 192 +++++
scripts/mod/modpost.c | 1 +
tools/perf/perf.h | 6 +
64 files changed, 7621 insertions(+), 33 deletions(-)
create mode 100644 arch/arc/include/asm/current.h
create mode 100644 arch/arc/include/asm/defines.h
create mode 100644 arch/arc/include/asm/disasm.h
create mode 100644 arch/arc/include/asm/event-log-asm.h
create mode 100644 arch/arc/include/asm/event-log.h
create mode 100644 arch/arc/include/asm/futex.h
create mode 100644 arch/arc/include/asm/kgdb.h
create mode 100644 arch/arc/include/asm/kprobes.h
create mode 100644 arch/arc/include/asm/perf_event.h
create mode 100644 arch/arc/include/asm/unaligned.h
create mode 100644 arch/arc/include/asm/unwind.h
create mode 100644 arch/arc/kernel/arc_hostlink.c
create mode 100644 arch/arc/kernel/disasm.c
create mode 100644 arch/arc/kernel/event-log.c
create mode 100644 arch/arc/kernel/kgdb.c
create mode 100644 arch/arc/kernel/kprobes.c
create mode 100644 arch/arc/kernel/module.c
create mode 100644 arch/arc/kernel/smp.c
create mode 100644 arch/arc/kernel/unaligned.c
create mode 100644 arch/arc/kernel/unwind.c
create mode 100644 arch/arc/oprofile/Makefile
create mode 100644 arch/arc/oprofile/common.c
create mode 100644 arch/arc/plat-arcfpga/include/plat/smp.h
create mode 100644 arch/arc/plat-arcfpga/smp.c

--
1.7.4.1


2012-11-12 11:49:52

by Vineet Gupta

[permalink] [raw]
Subject: [RFC Patch v1 32/55] ARC: [optim] Cache "current" in Register r25

From: Vineet Gupta <[email protected]>

Signed-off-by: Vineet Gupta <[email protected]>
---
arch/arc/Kconfig | 7 ++++++
arch/arc/Makefile | 9 +++++++
arch/arc/include/asm/Kbuild | 1 -
arch/arc/include/asm/current.h | 32 +++++++++++++++++++++++++++
arch/arc/include/asm/entry.h | 45 ++++++++++++++++++++++++++++++++++++++
arch/arc/include/asm/processor.h | 3 ++
arch/arc/kernel/asm-offsets.c | 3 ++
arch/arc/kernel/ctx_sw.c | 7 ++++++
arch/arc/kernel/entry.S | 14 +++++++++++
9 files changed, 120 insertions(+), 1 deletions(-)
create mode 100644 arch/arc/include/asm/current.h

diff --git a/arch/arc/Kconfig b/arch/arc/Kconfig
index f3aacfc..71e55c0 100644
--- a/arch/arc/Kconfig
+++ b/arch/arc/Kconfig
@@ -277,6 +277,13 @@ config ARC_PLAT_SDRAM_SIZE

endmenu # "Platform Board Configuration"

+config ARC_CURR_IN_REG
+ bool "Dedicate Register r25 for current_task pointer"
+ default y
+ help
+ This reserved Register R25 to point to Current Task in
+ kernel mode. This saves memory access for each such access
+
config ARC_STACK_NONEXEC
bool "Make stack non-executable"
default n
diff --git a/arch/arc/Makefile b/arch/arc/Makefile
index 4d52a3b..eeb64dd 100644
--- a/arch/arc/Makefile
+++ b/arch/arc/Makefile
@@ -20,6 +20,15 @@ export PLATFORM
cflags-y += -Iarch/arc/plat-$(PLATFORM)/include
cflags-y += -mA7 -fno-common -pipe -fno-builtin -D__linux__

+ifdef CONFIG_ARC_CURR_IN_REG
+# For a global register defintion, make sure it gets passed to every file
+# We had a customer reported bug where some code built in kernel was NOT using
+# any kernel headers, and missing the r25 global register
+# Can't do unconditionally (like above) because of recursive include issues
+# due to <linux/thread_info.h>
+LINUXINCLUDE += -include ${src}/arch/arc/include/asm/current.h
+endif
+
atleast_gcc44 := $(call cc-ifversion, -gt, 0402, y)
cflags-$(atleast_gcc44) += -fsection-anchors

diff --git a/arch/arc/include/asm/Kbuild b/arch/arc/include/asm/Kbuild
index 148dfcf..a246390 100644
--- a/arch/arc/include/asm/Kbuild
+++ b/arch/arc/include/asm/Kbuild
@@ -11,7 +11,6 @@ generic-y += bugs.h
generic-y += bitsperlong.h
generic-y += clkdev.h
generic-y += cputime.h
-generic-y += current.h
generic-y += device.h
generic-y += div64.h
generic-y += emergency-restart.h
diff --git a/arch/arc/include/asm/current.h b/arch/arc/include/asm/current.h
new file mode 100644
index 0000000..87b9185
--- /dev/null
+++ b/arch/arc/include/asm/current.h
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2004, 2007-2010, 2011-2012 Synopsys, Inc. (http://www.synopsys.com)
+ *
+ * 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.
+ *
+ * Vineetg: May 16th, 2008
+ * - Current macro is now implemented as "global register" r25
+ */
+
+#ifndef _ASM_ARC_CURRENT_H
+#define _ASM_ARC_CURRENT_H
+
+#ifdef __KERNEL__
+
+#ifndef __ASSEMBLY__
+
+#ifdef CONFIG_ARC_CURR_IN_REG
+
+register struct task_struct *curr_arc asm("r25");
+#define current (curr_arc)
+
+#else
+#include <asm-generic/current.h>
+#endif /* ! CONFIG_ARC_CURR_IN_REG */
+
+#endif /* ! __ASSEMBLY__ */
+
+#endif /* __KERNEL__ */
+
+#endif /* _ASM_ARC_CURRENT_H */
diff --git a/arch/arc/include/asm/entry.h b/arch/arc/include/asm/entry.h
index 3e0a9f4..6113e1a 100644
--- a/arch/arc/include/asm/entry.h
+++ b/arch/arc/include/asm/entry.h
@@ -13,6 +13,8 @@
* was being "CLEARED" rather then "SET". Actually "SET" clears ZOL context
*
* Vineetg: May 5th 2008
+ * -Modified CALLEE_REG save/restore macros to handle the fact that
+ * r25 contains the kernel current task ptr
* - Defined Stack Switching Macro to be reused in all intr/excp hdlrs
* - Shaved off 11 instructions from RESTORE_ALL_INT1 by using the
* address Write back load ld.ab instead of seperate ld/add instn
@@ -28,6 +30,7 @@
#include <asm/asm-offsets.h>
#include <asm/arcregs.h>
#include <asm/ptrace.h>
+#include <asm/processor.h> /* For VMALLOC_START */
#include <asm/thread_info.h> /* For THREAD_SIZE */

/* Note on the LD/ST addr modes with addr reg wback
@@ -106,7 +109,14 @@
st.a r22, [sp, -4]
st.a r23, [sp, -4]
st.a r24, [sp, -4]
+
+#ifdef CONFIG_ARC_CURR_IN_REG
+ ; Retrieve orig r25 and save it on stack
+ ld r12, [r25, TASK_THREAD + THREAD_USER_R25]
+ st.a r12, [sp, -4]
+#else
st.a r25, [sp, -4]
+#endif

/* move up by 1 word to "create" callee_regs->"stack_place_holder" */
sub sp, sp, 4
@@ -131,8 +141,12 @@
st.a r22, [sp, -4]
st.a r23, [sp, -4]
st.a r24, [sp, -4]
+#ifdef CONFIG_ARC_CURR_IN_REG
+ sub sp, sp, 8
+#else
st.a r25, [sp, -4]
sub sp, sp, 4
+#endif
.endm

/*--------------------------------------------------------------
@@ -148,8 +162,14 @@
*-------------------------------------------------------------*/
.macro RESTORE_CALLEE_SAVED_KERNEL

+
+#ifdef CONFIG_ARC_CURR_IN_REG
+ add sp, sp, 8 /* skip callee_reg gutter and user r25 placeholder */
+#else
add sp, sp, 4 /* skip "callee_regs->stack_place_holder" */
ld.ab r25, [sp, 4]
+#endif
+
ld.ab r24, [sp, 4]
ld.ab r23, [sp, 4]
ld.ab r22, [sp, 4]
@@ -200,6 +220,7 @@
*
* Entry : r9 contains pre-IRQ/exception/trap status32
* Exit : SP is set to kernel mode stack pointer
+ * If CURR_IN_REG, r25 set to "current" task pointer
* Clobbers: r9
*-------------------------------------------------------------*/

@@ -224,6 +245,16 @@

GET_CURR_TASK_ON_CPU r9

+#ifdef CONFIG_ARC_CURR_IN_REG
+
+ /* If current task pointer cached in r25, time to
+ * -safekeep USER r25 in task->thread_struct->user_r25
+ * -load r25 with current task ptr
+ */
+ st.as r25, [r9, (TASK_THREAD + THREAD_USER_R25)/4]
+ mov r25, r9
+#endif
+
/* With current tsk in r9, get it's kernel mode stack base */
GET_TSK_STACK_BASE r9, r9

@@ -479,17 +510,31 @@

.macro SET_CURR_TASK_ON_CPU tsk, tmp
st \tsk, [@_current_task]
+#ifdef CONFIG_ARC_CURR_IN_REG
+ mov r25, \tsk
+#endif
.endm

/* ------------------------------------------------------------------
* Get the ptr to some field of Current Task at @off in task struct
+ * -Uses r25 for Current task ptr if that is enabled
*/

+#ifdef CONFIG_ARC_CURR_IN_REG
+
+.macro GET_CURR_TASK_FIELD_PTR off, reg
+ add \reg, r25, \off
+.endm
+
+#else
+
.macro GET_CURR_TASK_FIELD_PTR off, reg
GET_CURR_TASK_ON_CPU \reg
add \reg, \reg, \off
.endm

+#endif /* CONFIG_ARC_CURR_IN_REG */
+
#endif /* __ASSEMBLY__ */

#endif /* __ASM_ARC_ENTRY_H */
diff --git a/arch/arc/include/asm/processor.h b/arch/arc/include/asm/processor.h
index 38ea5fb..2fe9f22 100644
--- a/arch/arc/include/asm/processor.h
+++ b/arch/arc/include/asm/processor.h
@@ -29,6 +29,9 @@ struct thread_struct {
unsigned long callee_reg; /* pointer to callee regs */
unsigned long fault_address; /* dbls as brkpt holder as well */
unsigned long cause_code; /* Exception Cause Code (ECR) */
+#ifdef CONFIG_ARC_CURR_IN_REG
+ unsigned long user_r25;
+#endif
#ifdef CONFIG_ARC_FPU_SAVE_RESTORE
struct arc_fpu fpu;
#endif
diff --git a/arch/arc/kernel/asm-offsets.c b/arch/arc/kernel/asm-offsets.c
index 7f3f611..b0e7254 100644
--- a/arch/arc/kernel/asm-offsets.c
+++ b/arch/arc/kernel/asm-offsets.c
@@ -23,6 +23,9 @@ int main(void)

DEFINE(THREAD_KSP, offsetof(struct thread_struct, ksp));
DEFINE(THREAD_CALLEE_REG, offsetof(struct thread_struct, callee_reg));
+#ifdef CONFIG_ARC_CURR_IN_REG
+ DEFINE(THREAD_USER_R25, offsetof(struct thread_struct, user_r25));
+#endif
DEFINE(THREAD_FAULT_ADDR,
offsetof(struct thread_struct, fault_address));

diff --git a/arch/arc/kernel/ctx_sw.c b/arch/arc/kernel/ctx_sw.c
index 647e37a..fbf739c 100644
--- a/arch/arc/kernel/ctx_sw.c
+++ b/arch/arc/kernel/ctx_sw.c
@@ -24,6 +24,9 @@ __switch_to(struct task_struct *prev_task, struct task_struct *next_task)
unsigned int prev = (unsigned int)prev_task;
unsigned int next = (unsigned int)next_task;
int num_words_to_skip = 1;
+#ifdef CONFIG_ARC_CURR_IN_REG
+ num_words_to_skip++;
+#endif

__asm__ __volatile__(
/* FP/BLINK save generated by gcc (standard function prologue */
@@ -39,7 +42,9 @@ __switch_to(struct task_struct *prev_task, struct task_struct *next_task)
"st.a r22, [sp, -4] \n\t"
"st.a r23, [sp, -4] \n\t"
"st.a r24, [sp, -4] \n\t"
+#ifndef CONFIG_ARC_CURR_IN_REG
"st.a r25, [sp, -4] \n\t"
+#endif
"sub sp, sp, %4 \n\t" /* create gutter at top */

/* set ksp of outgoing task in tsk->thread.ksp */
@@ -62,7 +67,9 @@ __switch_to(struct task_struct *prev_task, struct task_struct *next_task)

"add sp, sp, %4 \n\t" /* skip gutter at top */

+#ifndef CONFIG_ARC_CURR_IN_REG
"ld.ab r25, [sp, 4] \n\t"
+#endif
"ld.ab r24, [sp, 4] \n\t"
"ld.ab r23, [sp, 4] \n\t"
"ld.ab r22, [sp, 4] \n\t"
diff --git a/arch/arc/kernel/entry.S b/arch/arc/kernel/entry.S
index deb2652..35406da 100644
--- a/arch/arc/kernel/entry.S
+++ b/arch/arc/kernel/entry.S
@@ -36,6 +36,9 @@
* was being "CLEARED" rather then "SET". Since it is Loop INHIBIT Bit,
* setting it and not clearing it clears ZOL context
*
+ * Vineetg: May 16th, 2008
+ * - r25 now contains the Current Task when in kernel
+ *
* Vineetg: Dec 22, 2007
* Minor Surgery of Low Level ISR to make it SMP safe
* - MMU_SCRATCH0 Reg used for freeing up r9 in Level 1 ISR
@@ -540,6 +543,17 @@ restore_regs :
; XXX can this be optimised out
IRQ_DISABLE_SAVE r9, r10 ;@r10 has prisitine (pre-disable) copy

+#ifdef CONFIG_ARC_CURR_IN_REG
+ ; Restore User R25
+ ; Earlier this used to be only for returning to user mode
+ ; However with 2 levels of IRQ this can also happen even if
+ ; in kernel mode
+ ld r9, [sp, PT_sp]
+ brhs r9, VMALLOC_START, 8f
+ RESTORE_USER_R25
+8:
+#endif
+
; Restore REG File. In case multiple Events outstanding,
; use the same priorty as rtie: EXCPN, L2 IRQ, L1 IRQ, None
; Note that we use realtime STATUS32 (not pt_regs->status32) to
--
1.7.4.1

2012-11-12 11:50:00

by Vineet Gupta

[permalink] [raw]
Subject: [RFC Patch v1 33/55] ARC: ptrace support

From: Vineet Gupta <[email protected]>

Signed-off-by: Vineet Gupta <[email protected]>
---
arch/arc/Kconfig | 1 +
arch/arc/include/asm/ptrace.h | 18 ++++-
arch/arc/kernel/Makefile | 3 +
arch/arc/kernel/entry.S | 69 +++++++++++++++++
arch/arc/kernel/ptrace.c | 164 ++++++++++++++++++++++++++++++++++++++++-
5 files changed, 251 insertions(+), 4 deletions(-)

diff --git a/arch/arc/Kconfig b/arch/arc/Kconfig
index 71e55c0..198c7d0 100644
--- a/arch/arc/Kconfig
+++ b/arch/arc/Kconfig
@@ -20,6 +20,7 @@ config ARC
select GENERIC_KERNEL_THREAD
select GENERIC_PENDING_IRQ if SMP
select GENERIC_SMP_IDLE_THREAD
+ select HAVE_ARCH_TRACEHOOK
select HAVE_GENERIC_HARDIRQS
select HOTPLUG if !INITRAMFS_SOURCE=""
select MODULES_USE_ELF_RELA
diff --git a/arch/arc/include/asm/ptrace.h b/arch/arc/include/asm/ptrace.h
index bc2229f..bb621e8 100644
--- a/arch/arc/include/asm/ptrace.h
+++ b/arch/arc/include/asm/ptrace.h
@@ -42,6 +42,8 @@
#define PT_orig_r0 96
#define PT_orig_r8 100

+#ifdef __KERNEL__
+
#ifndef __ASSEMBLY__

/* THE pt_regs: Defines how regs are saved during entry into kernel */
@@ -115,7 +117,6 @@ struct user_regs_struct {
/* return 1 if user mode or 0 if kernel mode */
#define user_mode(regs) (regs->status32 & STATUS_U_MASK)

-
/* return 1 if in syscall, 0 if Intr or Exception */
#define in_syscall(regs) (((regs->orig_r8) >= 0 && \
(regs->orig_r8 <= NR_syscalls)) ? 1 : 0)
@@ -128,6 +129,19 @@ struct user_regs_struct {
(struct pt_regs *)(pg_start + THREAD_SIZE - 4) - 1; \
})

-#endif /* __ASSEMBLY__ */
+#define in_brkpt_trap(regs) (((regs->orig_r8) == (NR_syscalls + 2)) ? 1 : 0)
+
+#define user_stack_pointer(regs)\
+({ unsigned int sp; \
+ if (user_mode(regs)) \
+ sp = (regs)->sp;\
+ else \
+ sp = -1; \
+ sp; \
+})
+
+#endif /* !__ASSEMBLY__ */
+
+#endif /* __KERNEL__ */

#endif /* __ASM_PTRACE_H */
diff --git a/arch/arc/kernel/Makefile b/arch/arc/kernel/Makefile
index 47f6b33..4ddef9a 100644
--- a/arch/arc/kernel/Makefile
+++ b/arch/arc/kernel/Makefile
@@ -5,6 +5,9 @@
# it under the terms of the GNU General Public License version 2 as
# published by the Free Software Foundation.

+# Pass UTS_MACHINE for user_regset definition
+CFLAGS_ptrace.o += -DUTS_MACHINE='"$(UTS_MACHINE)"'
+
obj-y := arcksyms.o setup.o irq.o time.o reset.o ptrace.o entry.o process.o \
signal.o traps.o sys.o troubleshoot.o stacktrace.o

diff --git a/arch/arc/kernel/entry.S b/arch/arc/kernel/entry.S
index 35406da..93cde5e 100644
--- a/arch/arc/kernel/entry.S
+++ b/arch/arc/kernel/entry.S
@@ -10,6 +10,14 @@
* TBD:
* -remove the low level wrappers for execve/fork/vfork
* needed before task_pt_regs was invented. This saves a branch per call
+ * and more importantly allows clean invocation of post-syscall ptrace hook
+ *
+ * vineetg: Feb 2011 (ptrace low level code fixes)
+ * -traced syscall return code (r0) was not saved into pt_regs for restoring
+ * into user reg-file when traded task rets to user space.
+ * -syscalls needing arch-wrappers (mainly for passing sp as pt_regs)
+ * were not invoking post-syscall trace hook (jumping directly into
+ * ret_from_system_call)
*
* vineetg: Nov 2010:
* -Vector table jumps (@8 bytes) converted into branches (@4 bytes)
@@ -351,6 +359,50 @@ ARC_ENTRY EV_Extension
b ret_from_exception
ARC_EXIT EV_Extension

+;######################### System Call Tracing #########################
+
+tracesys:
+ ; save EFA in case tracer wants the PC of traced task
+ ; using ERET won't work since next-PC has already committed
+ lr r12, [efa]
+ GET_CURR_TASK_FIELD_PTR TASK_THREAD, r11
+ st r12, [r11, THREAD_FAULT_ADDR]
+
+ ; PRE Sys Call Ptrace hook
+ mov r0, sp ; pt_regs needed
+ bl @syscall_trace_entry
+
+ ; Tracing code now returns the syscall num (orig or modif)
+ mov r8, r0
+
+ ; Do the Sys Call as we normally would.
+ ; Validate the Sys Call number
+ cmp r8, NR_syscalls
+ mov.hi r0, -ENOSYS
+ bhi tracesys_exit
+
+ ; Restore the sys-call args. Mere invocation of the hook abv could have
+ ; clobbered them (since they are in scratch regs). The tracer could also
+ ; have deliberately changed the syscall args: r0-r7
+ ld r0, [sp, PT_r0]
+ ld r1, [sp, PT_r1]
+ ld r2, [sp, PT_r2]
+ ld r3, [sp, PT_r3]
+ ld r4, [sp, PT_r4]
+ ld r5, [sp, PT_r5]
+ ld r6, [sp, PT_r6]
+ ld r7, [sp, PT_r7]
+ ld.as r9, [sys_call_table, r8]
+ jl [r9] ; Entry into Sys Call Handler
+
+tracesys_exit:
+ st r0, [sp, PT_r0] ; sys call return value in pt_regs
+
+ ;POST Sys Call Ptrace Hook
+ bl @syscall_trace_exit
+ b ret_from_exception ; NOT ret_from_system_call at is saves r0 which
+ ; we'd done before calling post hook above
+
;################### Break Point TRAP ##########################

; ======= (5b) Trap is due to Break-Point =========
@@ -416,6 +468,11 @@ ARC_ENTRY EV_Trap
; Before doing anything, return from CPU Exception Mode
FAKE_RET_FROM_EXCPN r11

+ ; If syscall tracing ongoing, invoke pre-pos-hooks
+ GET_CURR_THR_INFO_FLAGS r10
+ btst r10, TIF_SYSCALL_TRACE
+ bnz tracesys ; this never comes back
+
;============ This is normal System Call case ==========
; Sys-call num shd not exceed the total system calls avail
cmp r8, NR_syscalls
@@ -621,6 +678,10 @@ ARC_ENTRY sys_fork_wrapper
bl @sys_fork
DISCARD_CALLEE_SAVED_USER

+ GET_CURR_THR_INFO_FLAGS r10
+ btst r10, TIF_SYSCALL_TRACE
+ bnz tracesys_exit
+
b ret_from_system_call
ARC_EXIT sys_fork_wrapper

@@ -631,6 +692,10 @@ ARC_ENTRY sys_vfork_wrapper
bl @sys_vfork
DISCARD_CALLEE_SAVED_USER

+ GET_CURR_THR_INFO_FLAGS r10
+ btst r10, TIF_SYSCALL_TRACE
+ bnz tracesys_exit
+
b ret_from_system_call
ARC_EXIT sys_vfork_wrapper

@@ -645,5 +710,9 @@ ARC_ENTRY sys_clone_wrapper
bl @sys_clone
DISCARD_CALLEE_SAVED_USER

+ GET_CURR_THR_INFO_FLAGS r10
+ btst r10, TIF_SYSCALL_TRACE
+ bnz tracesys_exit
+
b ret_from_system_call
ARC_EXIT sys_clone_wrapper
diff --git a/arch/arc/kernel/ptrace.c b/arch/arc/kernel/ptrace.c
index 1cf944a..93c7420 100644
--- a/arch/arc/kernel/ptrace.c
+++ b/arch/arc/kernel/ptrace.c
@@ -7,6 +7,122 @@
*/

#include <linux/ptrace.h>
+#include <linux/tracehook.h>
+#include <linux/regset.h>
+#include <linux/unistd.h>
+#include <linux/elf.h>
+
+static struct callee_regs *task_callee_regs(struct task_struct *tsk)
+{
+ struct callee_regs *tmp = (struct callee_regs *)tsk->thread.callee_reg;
+ return tmp;
+}
+
+static int genregs_get(struct task_struct *target,
+ const struct user_regset *regset,
+ unsigned int pos, unsigned int count,
+ void *kbuf, void __user *ubuf)
+{
+ const struct pt_regs *ptregs = task_pt_regs(target);
+ const struct callee_regs *cregs = task_callee_regs(target);
+ int ret = 0;
+ unsigned int stop_pc_val;
+
+#define REG_O_CHUNK(START, END, PTR) \
+ if (!ret) \
+ ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf, PTR, \
+ offsetof(struct user_regs_struct, START), \
+ offsetof(struct user_regs_struct, END));
+
+#define REG_O_ONE(LOC, PTR) \
+ if (!ret) \
+ ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf, PTR, \
+ offsetof(struct user_regs_struct, LOC), \
+ offsetof(struct user_regs_struct, LOC) + 4);
+
+ REG_O_CHUNK(scratch, callee, ptregs);
+ REG_O_CHUNK(callee, efa, cregs);
+ REG_O_CHUNK(efa, stop_pc, &target->thread.fault_address);
+
+ if (!ret) {
+ if (in_brkpt_trap(ptregs)) {
+ stop_pc_val = target->thread.fault_address;
+ pr_debug("\t\tstop_pc (brk-pt)\n");
+ } else {
+ stop_pc_val = ptregs->ret;
+ pr_debug("\t\tstop_pc (others)\n");
+ }
+
+ REG_O_ONE(stop_pc, &stop_pc_val);
+ }
+
+ return ret;
+}
+
+static int genregs_set(struct task_struct *target,
+ const struct user_regset *regset,
+ unsigned int pos, unsigned int count,
+ const void *kbuf, const void __user *ubuf)
+{
+ const struct pt_regs *ptregs = task_pt_regs(target);
+ const struct callee_regs *cregs = task_callee_regs(target);
+ int ret = 0;
+
+#define REG_IN_CHUNK(FIRST, NEXT, PTR) \
+ if (!ret) \
+ ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf, \
+ (void *)(PTR), \
+ offsetof(struct user_regs_struct, FIRST), \
+ offsetof(struct user_regs_struct, NEXT));
+
+#define REG_IN_ONE(LOC, PTR) \
+ if (!ret) \
+ ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf, \
+ (void *)(PTR), \
+ offsetof(struct user_regs_struct, LOC), \
+ offsetof(struct user_regs_struct, LOC) + 4);
+
+#define REG_IGNORE_ONE(LOC) \
+ if (!ret) \
+ ret = user_regset_copyin_ignore(&pos, &count, &kbuf, &ubuf, \
+ offsetof(struct user_regs_struct, LOC), \
+ offsetof(struct user_regs_struct, LOC) + 4);
+
+ /* TBD: disallow updates to STATUS32, orig_r8 etc*/
+ REG_IN_CHUNK(scratch, callee, ptregs); /* pt_regs[bta..orig_r8] */
+ REG_IN_CHUNK(callee, efa, cregs); /* callee_regs[r25..r13] */
+ REG_IGNORE_ONE(efa); /* efa update invalid */
+ REG_IN_ONE(stop_pc, &ptregs->ret); /* stop_pc: PC update */
+
+ return ret;
+}
+
+enum arc_getset {
+ REGSET_GENERAL,
+};
+
+static const struct user_regset arc_regsets[] = {
+ [REGSET_GENERAL] = {
+ .core_note_type = NT_PRSTATUS,
+ .n = ELF_NGREG,
+ .size = sizeof(unsigned long),
+ .align = sizeof(unsigned long),
+ .get = genregs_get,
+ .set = genregs_set,
+ }
+};
+
+static const struct user_regset_view user_arc_view = {
+ .name = UTS_MACHINE,
+ .e_machine = EM_ARCOMPACT,
+ .regsets = arc_regsets,
+ .n = ARRAY_SIZE(arc_regsets)
+};
+
+const struct user_regset_view *task_user_regset_view(struct task_struct *task)
+{
+ return &user_arc_view;
+}

void ptrace_disable(struct task_struct *child)
{
@@ -16,11 +132,55 @@ long arch_ptrace(struct task_struct *child, long request,
unsigned long addr, unsigned long data)
{
int ret = -EIO;
+ unsigned int count, pos;
+ unsigned int __user *u_addr;
+ void *kbuf;
+
+ pr_debug("REQ=%ld: ADDR =0x%lx, DATA=0x%lx)\n", request, addr, data);
+
+ switch (request) {
+
+ case PTRACE_PEEKUSR:
+ pos = addr; /* offset in struct user_regs_struct */
+ count = 4; /* 1 register only */
+ u_addr = (unsigned int __user *)data;
+ kbuf = NULL;
+ ret = genregs_get(child, NULL, pos, count, kbuf, u_addr);
+ break;
+
+ case PTRACE_POKEUSR:
+ pos = addr; /* offset in struct user_regs_struct */
+ count = 4; /* 1 register only */
+
+ /* Ideally @data would have abeen a user space buffer, from
+ * where, we do a copy_from_user.
+ * However this request only involves one word, which courtesy
+ * our ABI can be passed in a reg.
+ * regset interface however expects some buffer to copyin from
+ */
+ kbuf = &data;
+ u_addr = NULL;
+
+ ret = genregs_set(child, NULL, pos, count, kbuf, u_addr);
+ break;
+
+ default:
+ ret = ptrace_request(child, request, addr, data);
+ break;
+ }
+
return ret;
}

+asmlinkage int syscall_trace_entry(struct pt_regs *regs)
+{
+ if (tracehook_report_syscall_entry(regs))
+ return ULONG_MAX;

-const struct user_regset_view *task_user_regset_view(struct task_struct *task)
+ return regs->r8;
+}
+
+asmlinkage void syscall_trace_exit(struct pt_regs *regs)
{
- return (const struct user_regset_view *)NULL;
+ tracehook_report_syscall_exit(regs, 0);
}
--
1.7.4.1

2012-11-12 11:50:09

by Vineet Gupta

[permalink] [raw]
Subject: [RFC Patch v1 34/55] ARC: futex

From: Vineet Gupta <[email protected]>

Signed-off-by: Vineet Gupta <[email protected]>
---
arch/arc/include/asm/futex.h | 151 ++++++++++++++++++++++++++++++++++++++++++
1 files changed, 151 insertions(+), 0 deletions(-)
create mode 100644 arch/arc/include/asm/futex.h

diff --git a/arch/arc/include/asm/futex.h b/arch/arc/include/asm/futex.h
new file mode 100644
index 0000000..4dc64dd
--- /dev/null
+++ b/arch/arc/include/asm/futex.h
@@ -0,0 +1,151 @@
+/*
+ * Copyright (C) 2004, 2007-2010, 2011-2012 Synopsys, Inc. (http://www.synopsys.com)
+ *
+ * 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.
+ *
+ * Vineetg: August 2010: From Android kernel work
+ */
+
+#ifndef _ASM_FUTEX_H
+#define _ASM_FUTEX_H
+
+#include <linux/futex.h>
+#include <linux/preempt.h>
+#include <linux/uaccess.h>
+#include <asm/errno.h>
+
+#define __futex_atomic_op(insn, ret, oldval, uaddr, oparg)\
+ \
+ __asm__ __volatile__( \
+ "1: ld %1, [%2] \n" \
+ insn "\n" \
+ "2: st %0, [%2] \n" \
+ " mov %0, 0 \n" \
+ "3: \n" \
+ " .section .fixup,\"ax\" \n" \
+ " .align 4 \n" \
+ "4: mov %0, %4 \n" \
+ " b 3b \n" \
+ " .previous \n" \
+ " .section __ex_table,\"a\" \n" \
+ " .align 4 \n" \
+ " .word 1b, 4b \n" \
+ " .word 2b, 4b \n" \
+ " .previous \n" \
+ \
+ : "=&r" (ret), "=&r" (oldval) \
+ : "r" (uaddr), "r" (oparg), "ir" (-EFAULT) \
+ : "cc", "memory")
+
+static inline int futex_atomic_op_inuser(int encoded_op, u32 __user *uaddr)
+{
+ int op = (encoded_op >> 28) & 7;
+ int cmp = (encoded_op >> 24) & 15;
+ int oparg = (encoded_op << 8) >> 20;
+ int cmparg = (encoded_op << 20) >> 20;
+ int oldval = 0, ret;
+
+ if (encoded_op & (FUTEX_OP_OPARG_SHIFT << 28))
+ oparg = 1 << oparg;
+
+ if (!access_ok(VERIFY_WRITE, uaddr, sizeof(int)))
+ return -EFAULT;
+
+ pagefault_disable(); /* implies preempt_disable() */
+
+ switch (op) {
+ case FUTEX_OP_SET:
+ __futex_atomic_op("mov %0, %3", ret, oldval, uaddr, oparg);
+ break;
+ case FUTEX_OP_ADD:
+ __futex_atomic_op("add %0, %1, %3", ret, oldval, uaddr, oparg);
+ break;
+ case FUTEX_OP_OR:
+ __futex_atomic_op("or %0, %1, %3", ret, oldval, uaddr, oparg);
+ break;
+ case FUTEX_OP_ANDN:
+ __futex_atomic_op("bic %0, %1, %3", ret, oldval, uaddr, oparg);
+ break;
+ case FUTEX_OP_XOR:
+ __futex_atomic_op("xor %0, %1, %3", ret, oldval, uaddr, oparg);
+ break;
+ default:
+ ret = -ENOSYS;
+ }
+
+ pagefault_enable(); /* subsumes preempt_enable() */
+
+ if (!ret) {
+ switch (cmp) {
+ case FUTEX_OP_CMP_EQ:
+ ret = (oldval == cmparg);
+ break;
+ case FUTEX_OP_CMP_NE:
+ ret = (oldval != cmparg);
+ break;
+ case FUTEX_OP_CMP_LT:
+ ret = (oldval < cmparg);
+ break;
+ case FUTEX_OP_CMP_GE:
+ ret = (oldval >= cmparg);
+ break;
+ case FUTEX_OP_CMP_LE:
+ ret = (oldval <= cmparg);
+ break;
+ case FUTEX_OP_CMP_GT:
+ ret = (oldval > cmparg);
+ break;
+ default:
+ ret = -ENOSYS;
+ }
+ }
+ return ret;
+}
+
+/* Compare-xchg with preemption disabled.
+ * Notes:
+ * -Best-Effort: Exchg happens only if compare succeeds.
+ * If compare fails, returns; leaving retry/looping to upper layers
+ * -successful cmp-xchg: return orig value in @addr (same as cmp val)
+ * -Compare fails: return orig value in @addr
+ * -user access r/w fails: return -EFAULT
+ */
+static inline int
+futex_atomic_cmpxchg_inatomic(u32 *uval, u32 __user *uaddr, u32 oldval,
+ u32 newval)
+{
+ u32 val;
+
+ if (!access_ok(VERIFY_WRITE, uaddr, sizeof(int)))
+ return -EFAULT;
+
+ pagefault_disable(); /* implies preempt_disable() */
+
+ /* TBD : can use llock/scond */
+ __asm__ __volatile__(
+ "1: ld %0, [%3] \n"
+ " brne %0, %1, 3f \n"
+ "2: st %2, [%3] \n"
+ "3: \n"
+ " .section .fixup,\"ax\" \n"
+ "4: mov %0, %4 \n"
+ " b 3b \n"
+ " .previous \n"
+ " .section __ex_table,\"a\" \n"
+ " .align 4 \n"
+ " .word 1b, 4b \n"
+ " .word 2b, 4b \n"
+ " .previous\n"
+ : "=&r"(val)
+ : "r"(oldval), "r"(newval), "r"(uaddr), "ir"(-EFAULT)
+ : "cc", "memory");
+
+ pagefault_enable(); /* subsumes preempt_enable() */
+
+ *uval = val;
+ return val;
+}
+
+#endif
--
1.7.4.1

2012-11-12 11:50:17

by Vineet Gupta

[permalink] [raw]
Subject: [RFC Patch v1 35/55] ARC: oprofile support

From: Vineet Gupta <[email protected]>

Signed-off-by: Vineet Gupta <[email protected]>
---
arch/arc/Kconfig | 1 +
arch/arc/Makefile | 2 ++
arch/arc/oprofile/Makefile | 9 +++++++++
arch/arc/oprofile/common.c | 22 ++++++++++++++++++++++
4 files changed, 34 insertions(+), 0 deletions(-)
create mode 100644 arch/arc/oprofile/Makefile
create mode 100644 arch/arc/oprofile/common.c

diff --git a/arch/arc/Kconfig b/arch/arc/Kconfig
index 198c7d0..4557c8d 100644
--- a/arch/arc/Kconfig
+++ b/arch/arc/Kconfig
@@ -22,6 +22,7 @@ config ARC
select GENERIC_SMP_IDLE_THREAD
select HAVE_ARCH_TRACEHOOK
select HAVE_GENERIC_HARDIRQS
+ select HAVE_OPROFILE
select HOTPLUG if !INITRAMFS_SOURCE=""
select MODULES_USE_ELF_RELA

diff --git a/arch/arc/Makefile b/arch/arc/Makefile
index eeb64dd..7ef6767 100644
--- a/arch/arc/Makefile
+++ b/arch/arc/Makefile
@@ -97,6 +97,8 @@ ifneq ($(platform-y),)
core-y += arch/arc/plat-$(PLATFORM)/
endif

+drivers-$(CONFIG_OPROFILE) += arch/arc/oprofile/
+
libs-y += arch/arc/lib/ $(LIBGCC)

#default target for make without any arguements.
diff --git a/arch/arc/oprofile/Makefile b/arch/arc/oprofile/Makefile
new file mode 100644
index 0000000..ce417a6
--- /dev/null
+++ b/arch/arc/oprofile/Makefile
@@ -0,0 +1,9 @@
+obj-$(CONFIG_OPROFILE) += oprofile.o
+
+DRIVER_OBJS = $(addprefix ../../../drivers/oprofile/, \
+ oprof.o cpu_buffer.o buffer_sync.o \
+ event_buffer.o oprofile_files.o \
+ oprofilefs.o oprofile_stats.o \
+ timer_int.o )
+
+oprofile-y := $(DRIVER_OBJS) common.o
diff --git a/arch/arc/oprofile/common.c b/arch/arc/oprofile/common.c
new file mode 100644
index 0000000..917ae16
--- /dev/null
+++ b/arch/arc/oprofile/common.c
@@ -0,0 +1,22 @@
+/*
+ * Copyright (C) 2004, 2007-2010, 2011-2012 Synopsys, Inc. (http://www.synopsys.com)
+ *
+ * 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.
+ *
+ * Based on orig code from @author John Levon <[email protected]>
+ */
+
+#include <linux/oprofile.h>
+#include <linux/perf_event.h>
+
+int __init oprofile_arch_init(struct oprofile_operations *ops)
+{
+ return oprofile_perf_init(ops);
+}
+
+void oprofile_arch_exit(void)
+{
+ oprofile_perf_exit();
+}
--
1.7.4.1

2012-11-12 11:50:29

by Vineet Gupta

[permalink] [raw]
Subject: [RFC Patch v1 36/55] ARC: ARCompact 2 levels IRQ (high/low priority) Handling

From: Vineet Gupta <[email protected]>

There is a bit of hack/kludge right now where we disable preemption if a
L2 (High prio) IRQ is taken while L1 (Low prio) is active.

Need to revisit this

Signed-off-by: Vineet Gupta <[email protected]>
---
arch/arc/Kconfig | 17 ++++++
arch/arc/include/asm/entry.h | 95 +++++++++++++++++++++++++++++++
arch/arc/include/asm/irqflags.h | 6 ++-
arch/arc/kernel/entry.S | 117 +++++++++++++++++++++++++++++++++++++++
arch/arc/kernel/irq.c | 104 ++++++++++++++++++++++++++++++++++
arch/arc/plat-arcfpga/Kconfig | 13 ++++
6 files changed, 351 insertions(+), 1 deletions(-)

diff --git a/arch/arc/Kconfig b/arch/arc/Kconfig
index 4557c8d..e096545 100644
--- a/arch/arc/Kconfig
+++ b/arch/arc/Kconfig
@@ -210,6 +210,23 @@ config ARC_PAGE_SIZE_4K

endchoice

+config ARC_COMPACT_IRQ_LEVELS
+ bool "ARCompact IRQ Priorities: High(2)/Low(1)"
+ default n
+
+if ARC_COMPACT_IRQ_LEVELS
+
+config ARC_IRQ3_LV2
+ bool
+
+config ARC_IRQ5_LV2
+ bool
+
+config ARC_IRQ6_LV2
+ bool
+
+endif
+
config ARC_FPU_SAVE_RESTORE
bool "Enable FPU state persistence across context switch"
default n
diff --git a/arch/arc/include/asm/entry.h b/arch/arc/include/asm/entry.h
index 6113e1a..43dbf6f 100644
--- a/arch/arc/include/asm/entry.h
+++ b/arch/arc/include/asm/entry.h
@@ -5,6 +5,12 @@
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
+ * Vineetg: March 2009 (Supporting 2 levels of Interrupts)
+ * Stack switching code can no longer reliably rely on the fact that
+ * if we are NOT in user mode, stack is switched to kernel mode.
+ * e.g. L2 IRQ interrupted a L1 ISR which had not yet completed
+ * it's prologue including stack switching from user mode
+ *
* Vineetg: Aug 28th 2008: Bug #94984
* -Zero Overhead Loop Context shd be cleared when entering IRQ/EXcp/Trap
* Normally CPU does this automatically, however when doing FAKE rtie,
@@ -233,6 +239,33 @@
* assume SP is kernel mode SP. _NO_ need to do any stack switching
*/

+#ifdef CONFIG_ARC_COMPACT_IRQ_LEVELS
+ /* However....
+ * If Level 2 Interrupts enabled, we may end up with a corner case:
+ * 1. User Task executing
+ * 2. L1 IRQ taken, ISR starts (CPU auto-switched to KERNEL mode)
+ * 3. But before it could switch SP from USER to KERNEL stack
+ * a L2 IRQ "Interrupts" L1
+ * Thay way although L2 IRQ happened in Kernel mode, stack is still
+ * not switched.
+ * To handle this, we may need to switch stack even if in kernel mode
+ * provided SP has values in range of USER mode stack ( < 0x7000_0000 )
+ */
+ brlo sp, VMALLOC_START, 88f
+
+ /* TODO: vineetg:
+ * We need to be a bit more cautious here. What if a kernel bug in
+ * L1 ISR, caused SP to go whaco (some small value which looks like
+ * USER stk) and then we take L2 ISR.
+ * Above brlo alone would treat it as a valid L1-L2 sceanrio
+ * instead of shouting alound
+ * The only feasible way is to make sure this L2 happened in
+ * L1 prelogue ONLY i.e. ilink2 is less than a pre-set marker in
+ * L1 ISR before it switches stack
+ */
+
+#endif
+
/* Save Pre Intr/Exception KERNEL MODE SP on kernel stack
* safe-keeping not really needed, but it keeps the epilogue code
* (SP restore) simpler/uniform.
@@ -463,6 +496,42 @@
sub sp, sp, 4
.endm

+.macro SAVE_ALL_INT2
+
+ /* TODO-vineetg: SMP we can't use global nor can we use
+ * SCRATCH0 as we do for int1 because while int1 is using
+ * it, int2 can come
+ */
+ /* retsore original r9 , saved in sys_saved_r9 */
+ ld r9, [@int2_saved_reg]
+
+ /* now we are ready to save the remaining context :) */
+ st -2, [sp, 8] /* orig_r8, -2 for interrupt level 2 */
+ st 0, [sp, 4] /* orig_r0 , N/A for IRQ */
+ SAVE_CALLER_SAVED
+ st.a r26, [sp, -4] /* gp */
+ st.a fp, [sp, -4]
+ st.a blink, [sp, -4]
+ st.a ilink2, [sp, -4]
+ lr r9, [status32_l2]
+ st.a r9, [sp, -4]
+ st.a lp_count, [sp, -4]
+ lr r9, [lp_end]
+ st.a r9, [sp, -4]
+ lr r9, [lp_start]
+ st.a r9, [sp, -4]
+ lr r9, [bta_l2]
+ st.a r9, [sp, -4]
+
+#ifdef PT_REGS_CANARY
+ mov r9, 0xdeadbee2
+ st r9, [sp, -4]
+#endif
+
+ /* move up by 1 word to "create" pt_regs->"stack_place_holder" */
+ sub sp, sp, 4
+.endm
+
/*--------------------------------------------------------------
* Restore all registers used by interrupt handlers.
*
@@ -497,6 +566,32 @@
/* orig_r0 and orig_r8 skipped automatically */
.endm

+.macro RESTORE_ALL_INT2
+ add sp, sp, 4 /* hop over unused "pt_regs->stack_place_holder" */
+
+ ld.ab r9, [sp, 4]
+ sr r9, [bta_l2]
+ ld.ab r9, [sp, 4]
+ sr r9, [lp_start]
+ ld.ab r9, [sp, 4]
+ sr r9, [lp_end]
+ ld.ab r9, [sp, 4]
+ mov lp_count, r9
+ ld.ab r9, [sp, 4]
+ sr r9, [status32_l2]
+ ld.ab r9, [sp, 4]
+ mov ilink2, r9
+ ld.ab blink, [sp, 4]
+ ld.ab fp, [sp, 4]
+ ld.ab r26, [sp, 4] /* gp */
+ RESTORE_CALLER_SAVED
+
+ ld sp, [sp] /* restore original sp */
+ /* orig_r0 and orig_r8 skipped automatically */
+
+.endm
+
+
/* Get CPU-ID of this core */
.macro GET_CPU_ID reg
lr \reg, [identity]
diff --git a/arch/arc/include/asm/irqflags.h b/arch/arc/include/asm/irqflags.h
index 752e55d..cc8312d 100644
--- a/arch/arc/include/asm/irqflags.h
+++ b/arch/arc/include/asm/irqflags.h
@@ -95,7 +95,11 @@ static inline long arch_local_save_flags(void)
*/
static inline int arch_irqs_disabled_flags(unsigned long flags)
{
- return !(flags & (STATUS_E1_MASK));
+ return !(flags & (STATUS_E1_MASK
+#ifdef CONFIG_ARC_COMPACT_IRQ_LEVELS
+ | STATUS_E2_MASK
+#endif
+ ));
}

static inline int arch_irqs_disabled(void)
diff --git a/arch/arc/kernel/entry.S b/arch/arc/kernel/entry.S
index 93cde5e..13a0052 100644
--- a/arch/arc/kernel/entry.S
+++ b/arch/arc/kernel/entry.S
@@ -36,6 +36,8 @@
* exception. Thus FAKE RTIE needed in low level Priv-Violation handler.
* Instr Error could also cause similar scenario, so same there as well.
*
+ * Vineetg: March 2009 (Supporting 2 levels of Interrupts)
+ *
* Vineetg: Aug 28th 2008: Bug #94984
* -Zero Overhead Loop Context shd be cleared when entering IRQ/EXcp/Trap
* Normally CPU does this automatically, however when doing FAKE rtie,
@@ -101,13 +103,25 @@ VECTOR mem_service ; 0x8, Mem exception (0x1)
VECTOR instr_service ; 0x10, Instrn Error (0x2)

; ******************** Device ISRs **********************
+#ifdef CONFIG_ARC_IRQ3_LV2
+VECTOR handle_interrupt_level2
+#else
VECTOR handle_interrupt_level1
+#endif

VECTOR handle_interrupt_level1

+#ifdef CONFIG_ARC_IRQ5_LV2
+VECTOR handle_interrupt_level2
+#else
VECTOR handle_interrupt_level1
+#endif

+#ifdef CONFIG_ARC_IRQ6_LV2
+VECTOR handle_interrupt_level2
+#else
VECTOR handle_interrupt_level1
+#endif

.rept 25
VECTOR handle_interrupt_level1 ; Other devices
@@ -144,6 +158,17 @@ VECTOR reserved ; Reserved Exceptions
int1_saved_reg:
.zero 4

+/* Each Interrupt level needs it's own scratch */
+#ifdef CONFIG_ARC_COMPACT_IRQ_LEVELS
+
+ .section .data ; NOT .global
+ .type int2_saved_reg, @object
+ .size int2_saved_reg, 4
+int2_saved_reg:
+ .zero 4
+
+#endif
+
; ---------------------------------------------
.section .text, "ax",@progbits

@@ -157,6 +182,55 @@ reserved: ; processor restart

;##################### Interrupt Handling ##############################

+#ifdef CONFIG_ARC_COMPACT_IRQ_LEVELS
+; ---------------------------------------------
+; Level 2 ISR: Can interrupt a Level 1 ISR
+; ---------------------------------------------
+ARC_ENTRY handle_interrupt_level2
+
+ ; TODO-vineetg for SMP this wont work
+ ; free up r9 as scratchpad
+ st r9, [@int2_saved_reg]
+
+ ;Which mode (user/kernel) was the system in when intr occured
+ lr r9, [status32_l2]
+
+ SWITCH_TO_KERNEL_STK
+ SAVE_ALL_INT2
+
+ ;------------------------------------------------------
+ ; if L2 IRQ interrupted a L1 ISR, disable preemption
+ ;------------------------------------------------------
+
+ ld r9, [sp, PT_status32] ; get statu32_l2 (saved in pt_regs)
+ bbit0 r9, STATUS_A1_BIT, 1f ; L1 not active when L2 IRQ, so normal
+
+ ; A1 is set in status32_l2
+ ; bump thread_info->preempt_count (Disable preemption)
+ GET_CURR_THR_INFO_FROM_SP r10
+ ld r9, [r10, THREAD_INFO_PREEMPT_COUNT]
+ add r9, r9, 1
+ st r9, [r10, THREAD_INFO_PREEMPT_COUNT]
+
+1:
+ ;------------------------------------------------------
+ ; setup params for Linux common ISR and invoke it
+ ;------------------------------------------------------
+ lr r0, [icause2]
+ and r0, r0, 0x1f
+
+ bl.d @arch_do_IRQ
+ mov r1, sp
+
+ mov r8,0x2
+ sr r8, [AUX_IRQ_LV12] ; clear bit in Sticky Status Reg
+
+ b ret_from_exception
+
+ARC_EXIT handle_interrupt_level2
+
+#endif
+
; ---------------------------------------------
; Level 1 ISR
; ---------------------------------------------
@@ -625,6 +699,49 @@ restore_regs :

not_exception:

+#ifdef CONFIG_ARC_COMPACT_IRQ_LEVELS
+
+ bbit0 r10, STATUS_A2_BIT, not_level2_interrupt
+
+ ;------------------------------------------------------------------
+ ; if L2 IRQ interrupted a L1 ISR, we'd disbaled preemption earlier
+ ; so that sched doesnt move to new task, causing L1 to be delayed
+ ; undeterministically. Now that we've achieved that, lets reset
+ ; things to what they were, before returning from L2 context
+ ;----------------------------------------------------------------
+
+ ld r9, [sp, PT_orig_r8] ; get orig_r8 to make sure it is
+ brne r9, -2, 149f ; infact a L2 ISR ret path
+
+ ld r9, [sp, PT_status32] ; get statu32_l2 (saved in pt_regs)
+ bbit0 r9, STATUS_A1_BIT, 149f ; L1 not active when L2 IRQ, so normal
+
+ ; A1 is set in status32_l2
+ ; decrement thread_info->preempt_count (re-enable preemption)
+ GET_CURR_THR_INFO_FROM_SP r10
+ ld r9, [r10, THREAD_INFO_PREEMPT_COUNT]
+
+ ; paranoid check, given A1 was active when A2 happened, preempt count
+ ; must not be 0 beccause we would have incremented it.
+ ; If this does happen we simply HALT as it means a BUG !!!
+ cmp r9, 0
+ bnz 2f
+ flag 1
+
+2:
+ sub r9, r9, 1
+ st r9, [r10, THREAD_INFO_PREEMPT_COUNT]
+
+149:
+ ;return from level 2
+ RESTORE_ALL_INT2
+debug_marker_l2:
+ rtie
+
+not_level2_interrupt:
+
+#endif
+
bbit0 r10, STATUS_A1_BIT, not_level1_interrupt

;return from level 1
diff --git a/arch/arc/kernel/irq.c b/arch/arc/kernel/irq.c
index 03a125c..afde5a7 100644
--- a/arch/arc/kernel/irq.c
+++ b/arch/arc/kernel/irq.c
@@ -21,6 +21,7 @@
* what it does ?
* -setup Vector Table Base Reg - in case Linux not linked at 0x8000_0000
* -Disable all IRQs (on CPU side)
+ * -Optionally, setup the High priority Interrupts as Level 2 IRQs
*/
void __init arc_init_IRQ(void)
{
@@ -30,6 +31,24 @@ void __init arc_init_IRQ(void)

/* Disable all IRQs: enable them as devices request */
write_aux_reg(AUX_IENABLE, 0);
+
+ /* setup any high priority Interrupts (Level2 in ARCompact jargon) */
+#ifdef CONFIG_ARC_COMPACT_IRQ_LEVELS
+#ifdef CONFIG_ARC_IRQ3_LV2
+ level_mask |= (1 << 3);
+#endif
+#ifdef CONFIG_ARC_IRQ5_LV2
+ level_mask |= (1 << 5);
+#endif
+#ifdef CONFIG_ARC_IRQ6_LV2
+ level_mask |= (1 << 6);
+#endif
+
+ if (level_mask) {
+ pr_info("Level-2 interrupts bitset %x\n", level_mask);
+ write_aux_reg(AUX_IRQ_LEV, level_mask);
+ }
+#endif
}

/*
@@ -87,6 +106,90 @@ int __init get_hw_config_num_irq(void)
return 0;
}

+/*
+ * arch_local_irq_enable - Enable interrupts.
+ *
+ * 1. Explicitly called to re-enable interrupts
+ * 2. Implicitly called from spin_unlock_irq, write_unlock_irq etc
+ * which maybe in hard ISR itself
+ *
+ * Semantics of this function change depending on where it is called from:
+ *
+ * -If called from hard-ISR, it must not invert interrupt priorities
+ * e.g. suppose TIMER is high priority (Level 2) IRQ
+ * Time hard-ISR, timer_interrupt( ) calls spin_unlock_irq several times.
+ * Here local_irq_enable( ) shd not re-enable lower priority interrupts
+ * -If called from soft-ISR, it must re-enable all interrupts
+ * soft ISR are low prioity jobs which can be very slow, thus all IRQs
+ * must be enabled while they run.
+ * Now hardware context wise we may still be in L2 ISR (not done rtie)
+ * still we must re-enable both L1 and L2 IRQs
+ * Another twist is prev scenario with flow being
+ * L1 ISR ==> interrupted by L2 ISR ==> L2 soft ISR
+ * here we must not re-enable Ll as prev Ll Interrupt's h/w context will get
+ * over-written (this is deficiency in ARC700 Interrupt mechanism)
+ */
+
+#ifdef CONFIG_ARC_COMPACT_IRQ_LEVELS /* Complex version for 2 IRQ levels */
+
+void arch_local_irq_enable(void)
+{
+
+ unsigned long flags;
+ flags = arch_local_save_flags();
+
+ /* Allow both L1 and L2 at the onset */
+ flags |= (STATUS_E1_MASK | STATUS_E2_MASK);
+
+ /* Called from hard ISR (between irq_enter and irq_exit) */
+ if (in_irq()) {
+
+ /* If in L2 ISR, don't re-enable any further IRQs as this can
+ * cause IRQ priorities to get upside down. e.g. it could allow
+ * L1 be taken while in L2 hard ISR which is wrong not only in
+ * theory, it can also cause the dreaded L1-L2-L1 scenario
+ */
+ if (flags & STATUS_A2_MASK)
+ flags &= ~(STATUS_E1_MASK | STATUS_E2_MASK);
+
+ /* Even if in L1 ISR, allowe Higher prio L2 IRQs */
+ else if (flags & STATUS_A1_MASK)
+ flags &= ~(STATUS_E1_MASK);
+ }
+
+ /* called from soft IRQ, ideally we want to re-enable all levels */
+
+ else if (in_softirq()) {
+
+ /* However if this is case of L1 interrupted by L2,
+ * re-enabling both may cause whaco L1-L2-L1 scenario
+ * because ARC700 allows level 1 to interrupt an active L2 ISR
+ * Thus we disable both
+ * However some code, executing in soft ISR wants some IRQs
+ * to be enabled so we re-enable L2 only
+ *
+ * How do we determine L1 intr by L2
+ * -A2 is set (means in L2 ISR)
+ * -E1 is set in this ISR's pt_regs->status32 which is
+ * saved copy of status32_l2 when l2 ISR happened
+ */
+ struct pt_regs *pt = get_irq_regs();
+ if ((flags & STATUS_A2_MASK) && pt &&
+ (pt->status32 & STATUS_A1_MASK)) {
+ /*flags &= ~(STATUS_E1_MASK | STATUS_E2_MASK); */
+ flags &= ~(STATUS_E1_MASK);
+ }
+ }
+
+ arch_local_irq_restore(flags);
+}
+
+#else /* ! CONFIG_ARC_COMPACT_IRQ_LEVELS */
+
+/*
+ * Simpler version for only 1 level of interrupt
+ * Here we only Worry about Level 1 Bits
+ */
void arch_local_irq_enable(void)
{

@@ -104,4 +207,5 @@ void arch_local_irq_enable(void)

arch_local_irq_restore(flags);
}
+#endif
EXPORT_SYMBOL(arch_local_irq_enable);
diff --git a/arch/arc/plat-arcfpga/Kconfig b/arch/arc/plat-arcfpga/Kconfig
index 3b41a0e..3b399ca 100644
--- a/arch/arc/plat-arcfpga/Kconfig
+++ b/arch/arc/plat-arcfpga/Kconfig
@@ -30,4 +30,17 @@ config ARC_SERIAL_BAUD
help
Baud rate for the ARC UART

+# Platform specific IRQ wiring - tied to devices - feeds into ARC arch code
+config ARC_TIMER_LV2
+ bool "High Priority Timer0 IRQ"
+ depends on ARC_COMPACT_IRQ_LEVELS
+ select ARC_IRQ3_LV2
+
+# Timer HAS to be high priority, for any other high priority config
+config ARC_SERIAL_LV2
+ bool "High Priority Serial IRQ"
+ depends on ARC_SERIAL && ARC_TIMER_LV2
+ depends on ARC_COMPACT_IRQ_LEVELS
+ select ARC_IRQ5_LV2
+
endif
--
1.7.4.1

2012-11-12 11:50:37

by Vineet Gupta

[permalink] [raw]
Subject: [RFC Patch v1 37/55] ARC: dynamic loadable module support

From: Vineet Gupta <[email protected]>

Signed-off-by: Vineet Gupta <[email protected]>
---
arch/arc/include/asm/module.h | 4 ++
arch/arc/kernel/Makefile | 1 +
arch/arc/kernel/module.c | 117 +++++++++++++++++++++++++++++++++++++++++
3 files changed, 122 insertions(+), 0 deletions(-)
create mode 100644 arch/arc/kernel/module.c

diff --git a/arch/arc/include/asm/module.h b/arch/arc/include/asm/module.h
index 165d768..234b435 100644
--- a/arch/arc/include/asm/module.h
+++ b/arch/arc/include/asm/module.h
@@ -14,4 +14,8 @@

#include <asm-generic/module.h>

+#define MODULE_PROC_FAMILY "ARC700"
+
+#define MODULE_ARCH_VERMAGIC MODULE_PROC_FAMILY
+
#endif /* _ASM_ARC_MODULE_H */
diff --git a/arch/arc/kernel/Makefile b/arch/arc/kernel/Makefile
index 4ddef9a..9151bbe 100644
--- a/arch/arc/kernel/Makefile
+++ b/arch/arc/kernel/Makefile
@@ -11,6 +11,7 @@ CFLAGS_ptrace.o += -DUTS_MACHINE='"$(UTS_MACHINE)"'
obj-y := arcksyms.o setup.o irq.o time.o reset.o ptrace.o entry.o process.o \
signal.o traps.o sys.o troubleshoot.o stacktrace.o

+obj-$(CONFIG_MODULES) += arcksyms.o module.o
obj-$(CONFIG_ARC_FPU_SAVE_RESTORE) += fpu.o
CFLAGS_fpu.o += -mdpfp

diff --git a/arch/arc/kernel/module.c b/arch/arc/kernel/module.c
new file mode 100644
index 0000000..cc13df4
--- /dev/null
+++ b/arch/arc/kernel/module.c
@@ -0,0 +1,117 @@
+/*
+ * Copyright (C) 2004, 2007-2010, 2011-2012 Synopsys, Inc. (http://www.synopsys.com)
+ *
+ * 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.
+ */
+
+#include <linux/module.h>
+#include <linux/moduleloader.h>
+#include <linux/kernel.h>
+#include <linux/elf.h>
+#include <linux/vmalloc.h>
+#include <linux/slab.h>
+#include <linux/fs.h>
+#include <linux/string.h>
+
+static inline void arc_write_me(unsigned short *addr, unsigned long value)
+{
+ *addr = (value & 0xffff0000) >> 16;
+ *(addr + 1) = (value & 0xffff);
+}
+
+void *module_alloc(unsigned long size)
+{
+ if (size == 0)
+ return NULL;
+
+ return vmalloc(size);
+
+}
+
+void module_free(struct module *module, void *region)
+{
+ vfree(region);
+}
+
+int module_frob_arch_sections(Elf_Ehdr *hdr, Elf_Shdr *sechdrs,
+ char *secstr, struct module *mod)
+{
+ return 0;
+}
+
+void module_arch_cleanup(struct module *mod)
+{
+}
+
+int apply_relocate_add(Elf32_Shdr *sechdrs,
+ const char *strtab,
+ unsigned int symindex, /* sec index for sym tbl */
+ unsigned int relsec, /* sec index for relo sec */
+ struct module *module)
+{
+ int i, n;
+ Elf32_Rela *rel_entry = (void *)sechdrs[relsec].sh_addr;
+ Elf32_Sym *sym_entry, *sym_sec;
+ Elf32_Addr relocation;
+ Elf32_Addr location;
+ Elf32_Addr sec_to_patch;
+ int relo_type;
+
+ sec_to_patch = sechdrs[sechdrs[relsec].sh_info].sh_addr;
+ sym_sec = (Elf32_Sym *) sechdrs[symindex].sh_addr;
+ n = sechdrs[relsec].sh_size / sizeof(*rel_entry);
+
+ pr_debug("\n========== Module Sym reloc ===========================\n");
+ pr_debug("Section to fixup %x\n", sec_to_patch);
+ pr_debug("=========================================================\n");
+ pr_debug("rela->r_off | rela->addend | sym->st_value | ADDR | VALUE\n");
+ pr_debug("=========================================================\n");
+
+ /* Loop thru entries in relocation section */
+ for (i = 0; i < n; i++) {
+
+ /* This is where to make the change */
+ location = sec_to_patch + rel_entry[i].r_offset;
+
+ /* This is the symbol it is referring to. Note that all
+ undefined symbols have been resolved. */
+ sym_entry = sym_sec + ELF32_R_SYM(rel_entry[i].r_info);
+
+ relocation = sym_entry->st_value + rel_entry[i].r_addend;
+
+ pr_debug("\t%x\t\t%x\t\t%x %x %x [%s]\n",
+ rel_entry[i].r_offset, rel_entry[i].r_addend,
+ sym_entry->st_value, location, relocation,
+ strtab + sym_entry->st_name);
+
+ /* This assumes modules are built with -mlong-calls
+ * so any branches/jumps are absolute 32 bit jmps
+ * global data access again is abs 32 bit.
+ * Both of these are handled by same relocation type
+ */
+ relo_type = ELF32_R_TYPE(rel_entry[i].r_info);
+
+ if (likely(R_ARC_32_ME == relo_type))
+ arc_write_me((unsigned short *)location, relocation);
+ else if (R_ARC_32 == relo_type)
+ *((Elf32_Addr *) location) = relocation;
+ else
+ goto relo_err;
+
+ }
+ return 0;
+
+relo_err:
+ pr_err("%s: unknown relocation: %u\n",
+ module->name, ELF32_R_TYPE(rel_entry[i].r_info));
+ return -ENOEXEC;
+
+}
+
+int module_finalize(const Elf32_Ehdr *hdr, const Elf_Shdr *sechdrs,
+ struct module *mod)
+{
+ return 0;
+}
--
1.7.4.1

2012-11-12 11:50:46

by Vineet Gupta

[permalink] [raw]
Subject: [RFC Patch v1 39/55] ARC: kernel diagnostics: show_regs() etc

From: Vineet Gupta <[email protected]>

Signed-off-by: Vineet Gupta <[email protected]>
---
arch/arc/kernel/troubleshoot.c | 304 ++++++++++++++++++++++++++++++++++++++++
arch/arc/mm/tlbex.S | 20 +++
2 files changed, 324 insertions(+), 0 deletions(-)

diff --git a/arch/arc/kernel/troubleshoot.c b/arch/arc/kernel/troubleshoot.c
index 80bfe2a..8a816f2 100644
--- a/arch/arc/kernel/troubleshoot.c
+++ b/arch/arc/kernel/troubleshoot.c
@@ -6,12 +6,316 @@
*/

#include <linux/ptrace.h>
+#include <linux/module.h>
+#include <linux/mm.h>
+#include <linux/fs.h>
+#include <linux/kdev_t.h>
+#include <linux/fs_struct.h>
+#include <linux/proc_fs.h>
+#include <linux/file.h>
+#include <asm/arcregs.h>
+#include <asm/event-log.h>
+
+/*
+ * Common routine to print scratch regs (r0-r12) or callee regs (r13-r25)
+ * -Prints 3 regs per line and a CR.
+ * -To continue, callee regs right after scratch, special handling of CR
+ */
+static noinline void print_reg_file(long *reg_rev, int start_num)
+{
+ unsigned int i;
+ char buf[512];
+ int n = 0, len = sizeof(buf);
+
+ /* weird loop because pt_regs regs rev r12..r0, r25..r13 */
+ for (i = start_num; i < start_num + 13; i++) {
+ n += scnprintf(buf + n, len - n, "r%02u: 0x%08lx\t",
+ i, (unsigned long)*reg_rev);
+
+ if (((i + 1) % 3) == 0)
+ n += scnprintf(buf + n, len - n, "\n");
+
+ reg_rev--;
+ }
+
+ if (start_num != 0)
+ n += scnprintf(buf + n, len - n, "\n\n");
+
+ pr_info("%s", buf);
+}
+
+static void show_callee_regs(struct callee_regs *cregs)
+{
+ print_reg_file(&(cregs->r13), 13);
+}
+
+void print_task_path_n_nm(struct task_struct *task, char *buf)
+{
+ struct path path;
+ char *nm = NULL;
+ struct mm_struct *mm;
+ struct file *exe_file;
+ char comm_buf[TASK_COMM_LEN];
+
+ mm = get_task_mm(task);
+ if (!mm)
+ goto done;
+
+ exe_file = get_mm_exe_file(mm);
+ mmput(mm);
+
+ if (exe_file) {
+ path = exe_file->f_path;
+ path_get(&exe_file->f_path);
+ fput(exe_file);
+ nm = d_path(&path, buf, 255);
+ path_put(&path);
+ }
+
+done:
+ pr_info("task = %s '%s', TGID %u PID = %u\n", nm,
+ get_task_comm(comm_buf, task), task->tgid, task->pid);
+}
+EXPORT_SYMBOL(print_task_path_n_nm);
+
+static void show_faulting_vma(unsigned long address, char *buf)
+{
+ struct vm_area_struct *vma;
+ struct inode *inode;
+ unsigned long ino = 0;
+ dev_t dev = 0;
+ char *nm = buf;
+
+ vma = find_vma(current->active_mm, address);
+
+ /* check against the find_vma( ) behaviour which returns the next VMA
+ * if the container VMA is not found
+ */
+ if (vma && (vma->vm_start <= address)) {
+ struct file *file = vma->vm_file;
+ if (file) {
+ struct path *path = &file->f_path;
+ nm = d_path(path, buf, PAGE_SIZE - 1);
+ inode = vma->vm_file->f_path.dentry->d_inode;
+ dev = inode->i_sb->s_dev;
+ ino = inode->i_ino;
+ }
+ pr_info(" @off 0x%lx in [%s]\n"
+ " VMA: 0x%08lx to 0x%08lx\n\n",
+ address - vma->vm_start, nm, vma->vm_start, vma->vm_end);
+ } else
+ pr_info(" @No matching VMA found\n");
+}
+
+static void show_ecr_verbose(struct pt_regs *regs)
+{
+ unsigned int vec, cause_code, cause_reg;
+ unsigned long address;
+
+ cause_reg = current->thread.cause_code;
+ pr_info("\n[ECR]: 0x%08x => ", cause_reg);
+
+ /* For Data fault, this is data address not instruction addr */
+ address = current->thread.fault_address;
+
+ vec = cause_reg >> 16;
+ cause_code = (cause_reg >> 8) & 0xFF;
+
+ /* For DTLB Miss or ProtV, display the memory involved too */
+ if (vec == ECR_V_DTLB_MISS) {
+ pr_cont("Invalid (%s) @ 0x%08lx by insn @ 0x%08lx\n",
+ (cause_code == 0x01) ? "Read From" :
+ ((cause_code == 0x02) ? "Write to" : "EX"),
+ address, regs->ret);
+ } else if (vec == ECR_V_ITLB_MISS) {
+ pr_cont("Insn could not be fetched\n");
+ } else if (vec == ECR_V_MACH_CHK) {
+ pr_cont("%s\n", (cause_code == 0x0) ?
+ "Double Fault" : "Other Fatal Err");
+
+ } else if (vec == ECR_V_PROTV) {
+ if (cause_code == ECR_C_PROTV_INST_FETCH)
+ pr_cont("Execute from Non-exec Page\n");
+ else if (cause_code == ECR_C_PROTV_LOAD)
+ pr_cont("Read from Non-readable Page\n");
+ else if (cause_code == ECR_C_PROTV_STORE)
+ pr_cont("Write to Non-writable Page\n");
+ else if (cause_code == ECR_C_PROTV_XCHG)
+ pr_cont("Data exchange protection violation\n");
+ else if (cause_code == ECR_C_PROTV_MISALIG_DATA)
+ pr_cont("Misaligned r/w from 0x%08lx\n", address);
+ } else {
+ pr_cont("Check Programmer's Manual\n");
+ }
+}
+
+/************************************************************************
+ * API called by rest of kernel
+ ***********************************************************************/

void show_regs(struct pt_regs *regs)
{
+ struct task_struct *tsk = current;
+ struct callee_regs *cregs;
+ char *buf;
+
+ buf = (char *)__get_free_page(GFP_TEMPORARY);
+ if (!buf)
+ return;
+
+ print_task_path_n_nm(tsk, buf);
+
+ if (current->thread.cause_code)
+ show_ecr_verbose(regs);
+
+ pr_info("[EFA]: 0x%08lx\n", current->thread.fault_address);
+ pr_info("[ERET]: 0x%08lx (PC of Faulting Instr)\n", regs->ret);
+
+ show_faulting_vma(regs->ret, buf); /* faulting code, not data */
+
+ /* can't use print_vma_addr() yet as it doesn't check for
+ * non-inclusive vma
+ */
+
+ /* print special regs */
+ pr_info("status32: 0x%08lx\n", regs->status32);
+ pr_info(" SP: 0x%08lx\tFP: 0x%08lx\n", regs->sp, regs->fp);
+ pr_info("BTA: 0x%08lx\tBLINK: 0x%08lx\n",
+ regs->bta, regs->blink);
+ pr_info("LPS: 0x%08lx\tLPE: 0x%08lx\tLPC: 0x%08lx\n",
+ regs->lp_start, regs->lp_end, regs->lp_count);
+
+ /* print regs->r0 thru regs->r12
+ * Sequential printing was generating horrible code
+ */
+ print_reg_file(&(regs->r0), 0);
+
+ /* If Callee regs were saved, display them too */
+ cregs = (struct callee_regs *)current->thread.callee_reg;
+ if (cregs)
+ show_callee_regs(cregs);
+
+ free_page((unsigned long)buf);
}

void show_kernel_fault_diag(const char *str, struct pt_regs *regs,
unsigned long address, unsigned long cause_reg)
{
+ current->thread.fault_address = address;
+ current->thread.cause_code = cause_reg;
+
+ /* Caller and Callee regs */
+ show_regs(regs);
+
+ /* Show stack trace if this Fatality happened in kernel mode */
+ if (!user_mode(regs))
+ show_stacktrace(current, regs);
+
+ sort_snaps(1);
}
+
+#ifdef CONFIG_DEBUG_FS
+
+#include <linux/module.h>
+#include <linux/fs.h>
+#include <linux/mount.h>
+#include <linux/pagemap.h>
+#include <linux/init.h>
+#include <linux/namei.h>
+#include <linux/debugfs.h>
+
+static struct dentry *test_dentry;
+static struct dentry *test_dir;
+static struct dentry *test_u32_dentry;
+
+static u32 clr_on_read = 1;
+
+#ifdef CONFIG_ARC_DBG_TLB_MISS_COUNT
+u32 numitlb, numdtlb, num_pte_not_present;
+
+static int fill_display_data(char *kbuf)
+{
+ size_t num = 0;
+ num += sprintf(kbuf + num, "I-TLB Miss %x\n", numitlb);
+ num += sprintf(kbuf + num, "D-TLB Miss %x\n", numdtlb);
+ num += sprintf(kbuf + num, "PTE not present %x\n", num_pte_not_present);
+
+ if (clr_on_read)
+ numitlb = numdtlb = num_pte_not_present = 0;
+
+ return num;
+}
+
+static int tlb_stats_open(struct inode *inode, struct file *file)
+{
+ file->private_data = (void *)__get_free_page(GFP_KERNEL);
+ return 0;
+}
+
+/* called on user read(): display the couters */
+static ssize_t tlb_stats_output(struct file *file, /* file descriptor */
+ char __user *user_buf, /* user buffer */
+ size_t len, /* length of buffer */
+ loff_t *offset) /* offset in the file */
+{
+ size_t num;
+ char *kbuf = (char *)file->private_data;
+
+ num = fill_display_data(kbuf);
+
+ /* simple_read_from_buffer() is helper for copy to user space
+ It copies up to @2 (num) bytes from kernel buffer @4 (kbuf) at offset
+ @3 (offset) into the user space address starting at @1 (user_buf).
+ @5 (len) is max size of user buffer
+ */
+ return simple_read_from_buffer(user_buf, num, offset, kbuf, len);
+}
+
+/* called on user write : clears the counters */
+static ssize_t tlb_stats_clear(struct file *file, const char __user *user_buf,
+ size_t length, loff_t *offset)
+{
+ numitlb = numdtlb = num_pte_not_present = 0;
+ return length;
+}
+
+static int tlb_stats_close(struct inode *inode, struct file *file)
+{
+ free_page((unsigned long)(file->private_data));
+ return 0;
+}
+
+static const struct file_operations tlb_stats_file_ops = {
+ .read = tlb_stats_output,
+ .write = tlb_stats_clear,
+ .open = tlb_stats_open,
+ .release = tlb_stats_close
+};
+#endif
+
+static int __init arc_debugfs_init(void)
+{
+ test_dir = debugfs_create_dir("arc", NULL);
+
+#ifdef CONFIG_ARC_DBG_TLB_MISS_COUNT
+ test_dentry = debugfs_create_file("tlb_stats", 0444, test_dir, NULL,
+ &tlb_stats_file_ops);
+#endif
+
+ test_u32_dentry =
+ debugfs_create_u32("clr_on_read", 0444, test_dir, &clr_on_read);
+
+ return 0;
+}
+
+module_init(arc_debugfs_init);
+
+static void __exit arc_debugfs_exit(void)
+{
+ debugfs_remove(test_u32_dentry);
+ debugfs_remove(test_dentry);
+ debugfs_remove(test_dir);
+}
+module_exit(arc_debugfs_exit);
+
+#endif
diff --git a/arch/arc/mm/tlbex.S b/arch/arc/mm/tlbex.S
index 86804d3..a95d0f7 100644
--- a/arch/arc/mm/tlbex.S
+++ b/arch/arc/mm/tlbex.S
@@ -134,6 +134,14 @@ ex_saved_reg1:
lsr r0, r2, (PAGE_SHIFT - 2)
and r0, r0, ( (PTRS_PER_PTE - 1) << 2)
ld.aw r0, [r1, r0] ; get PTE and PTE ptr for fault addr
+#ifdef CONFIG_ARC_DBG_TLB_MISS_COUNT
+ and.f 0, r0, _PAGE_PRESENT
+ bz 1f
+ ld r2, [num_pte_not_present]
+ add r2, r2, 1
+ st r2, [num_pte_not_present]
+1:
+#endif

.endm

@@ -223,6 +231,12 @@ ARC_ENTRY EV_TLBMissI

TLBMISS_FREEUP_REGS

+#ifdef CONFIG_ARC_DBG_TLB_MISS_COUNT
+ ld r0, [@numitlb]
+ add r0, r0, 1
+ st r0, [@numitlb]
+#endif
+
;----------------------------------------------------------------
; Get the PTE corresponding to V-addr accessed
LOAD_FAULT_PTE
@@ -256,6 +270,12 @@ ARC_ENTRY EV_TLBMissD

TLBMISS_FREEUP_REGS

+#ifdef CONFIG_ARC_DBG_TLB_MISS_COUNT
+ ld r0, [@numdtlb]
+ add r0, r0, 1
+ st r0, [@numdtlb]
+#endif
+
;----------------------------------------------------------------
; Get the PTE corresponding to V-addr accessed
; If PTE exists, it will setup, r0 = PTE, r1 = Ptr to PTE
--
1.7.4.1

2012-11-12 11:50:57

by Vineet Gupta

[permalink] [raw]
Subject: [RFC Patch v1 40/55] ARC: SMP support

From: Vineet Gupta <[email protected]>

I would like to acknowledge people who have contributed to SMP port
-Rajeshwar Ranga <[email protected]> for orig 2.6.19 SMP port
-Noam Camus <[email protected]> for help with resurrecting 3.2 SMP port

Signed-off-by: Vineet Gupta <[email protected]>
---
arch/arc/Kconfig | 39 ++++-
arch/arc/Makefile | 3 +
arch/arc/include/asm/entry.h | 49 +++++
arch/arc/include/asm/mmu_context.h | 4 +
arch/arc/include/asm/mutex.h | 9 +
arch/arc/include/asm/pgtable.h | 4 +
arch/arc/include/asm/processor.h | 8 +
arch/arc/include/asm/smp.h | 108 +++++++++++
arch/arc/kernel/Makefile | 1 +
arch/arc/kernel/ctx_sw.c | 11 +
arch/arc/kernel/entry.S | 4 +
arch/arc/kernel/head.S | 33 ++++
arch/arc/kernel/irq.c | 5 +
arch/arc/kernel/setup.c | 4 +
arch/arc/kernel/smp.c | 295 ++++++++++++++++++++++++++++++
arch/arc/mm/tlb.c | 6 +
arch/arc/mm/tlbex.S | 38 ++++
arch/arc/plat-arcfpga/Kconfig | 12 ++
arch/arc/plat-arcfpga/Makefile | 1 +
arch/arc/plat-arcfpga/include/plat/irq.h | 10 +-
arch/arc/plat-arcfpga/include/plat/smp.h | 115 ++++++++++++
arch/arc/plat-arcfpga/irq.c | 10 +
arch/arc/plat-arcfpga/smp.c | 192 +++++++++++++++++++
23 files changed, 959 insertions(+), 2 deletions(-)
create mode 100644 arch/arc/kernel/smp.c
create mode 100644 arch/arc/plat-arcfpga/include/plat/smp.h
create mode 100644 arch/arc/plat-arcfpga/smp.c

diff --git a/arch/arc/Kconfig b/arch/arc/Kconfig
index 15d740c..8d72ada 100644
--- a/arch/arc/Kconfig
+++ b/arch/arc/Kconfig
@@ -118,9 +118,44 @@ config CPU_BIG_ENDIAN
help
Build kernel for Big Endian Mode of ARC CPU

+# If a plat can do IPI, same core(s) can do minimal SMP
+config ARC_HAS_IPI
+ bool
+
+config SMP
+ bool "Symmetric Multi-Processing (Incomplete)"
+ default y
+ depends on ARC_HAS_IPI
+ select USE_GENERIC_SMP_HELPERS
+ help
+ This enables support for systems with more than one CPU. If you have
+ a system with only one CPU, like most personal computers, say N. If
+ you have a system with more than one CPU, say Y.
+
+if SMP
+
+config ARC_HAS_COH_CACHES
+ def_bool n
+
+config ARC_HAS_COH_LLSC
+ def_bool n
+
+config ARC_HAS_REENTRANT_IRQ_LV2
+ def_bool n
+
+endif
+
+config NR_CPUS
+ int "Maximum number of CPUs (2-32)"
+ range 2 32
+ depends on SMP
+ default "2"
+
menuconfig ARC_CACHE
bool "Enable Cache Support"
default y
+ # if SMP, cache enabled ONLY if ARC implementation has cache coherency
+ depends on !SMP || ARC_HAS_COH_CACHES

if ARC_CACHE

@@ -213,6 +248,8 @@ endchoice
config ARC_COMPACT_IRQ_LEVELS
bool "ARCompact IRQ Priorities: High(2)/Low(1)"
default n
+ # if SMP, LV2 enabled ONLY if ARC implementation has LV2 re-entrancy
+ depends on !SMP || ARC_HAS_REENTRANT_IRQ_LV2

if ARC_COMPACT_IRQ_LEVELS

@@ -320,7 +357,7 @@ menuconfig ARC_DBG

config ARC_DBG_TLB_PARANOIA
bool "Paranoia Checks in Low Level TLB Handlers"
- depends on ARC_DBG
+ depends on ARC_DBG && !SMP
default n

config ARC_DBG_EVENT_TIMELINE
diff --git a/arch/arc/Makefile b/arch/arc/Makefile
index a533546..a86e284 100644
--- a/arch/arc/Makefile
+++ b/arch/arc/Makefile
@@ -127,3 +127,6 @@ archclean:
# Thus forcing all exten calls in this file to be long calls
export CFLAGS_decompress_inflate.o = -mmedium-calls
export CFLAGS_initramfs.o = -mmedium-calls
+ifdef CONFIG_SMP
+export CFLAGS_core.o = -mmedium-calls
+endif
diff --git a/arch/arc/include/asm/entry.h b/arch/arc/include/asm/entry.h
index 43dbf6f..a054e27 100644
--- a/arch/arc/include/asm/entry.h
+++ b/arch/arc/include/asm/entry.h
@@ -354,11 +354,19 @@
* to be saved again on kernel mode stack, as part of ptregs.
*-------------------------------------------------------------*/
.macro EXCPN_PROLOG_FREEUP_REG reg
+#ifdef CONFIG_SMP
+ sr \reg, [ARC_REG_SCRATCH_DATA0]
+#else
st \reg, [@ex_saved_reg1]
+#endif
.endm

.macro EXCPN_PROLOG_RESTORE_REG reg
+#ifdef CONFIG_SMP
+ lr \reg, [ARC_REG_SCRATCH_DATA0]
+#else
ld \reg, [@ex_saved_reg1]
+#endif
.endm

/*--------------------------------------------------------------
@@ -468,7 +476,11 @@
/* restore original r9 , saved in int1_saved_reg
* It will be saved on stack in macro: SAVE_CALLER_SAVED
*/
+#ifdef CONFIG_SMP
+ lr r9, [ARC_REG_SCRATCH_DATA0]
+#else
ld r9, [@int1_saved_reg]
+#endif

/* now we are ready to save the remaining context :) */
st -1, [sp, 8] /* orig_r8, -1 for interuppt level one */
@@ -599,6 +611,41 @@
bmsk \reg, \reg, 7
.endm

+#ifdef CONFIG_SMP
+
+/*-------------------------------------------------
+ * Retrieve the current running task on this CPU
+ * 1. Determine curr CPU id.
+ * 2. Use it to index into _current_task[ ]
+ */
+.macro GET_CURR_TASK_ON_CPU reg
+ GET_CPU_ID \reg
+ ld.as \reg, [@_current_task, \reg]
+.endm
+
+/*-------------------------------------------------
+ * Save a new task as the "current" task on this CPU
+ * 1. Determine curr CPU id.
+ * 2. Use it to index into _current_task[ ]
+ *
+ * Coded differently than GET_CURR_TASK_ON_CPU (which uses LD.AS)
+ * because ST r0, [r1, offset] can ONLY have s9 @offset
+ * while LD can take s9 (4 byte insn) or LIMM (8 byte insn)
+ */
+
+.macro SET_CURR_TASK_ON_CPU tsk, tmp
+ GET_CPU_ID \tmp
+ add2 \tmp, @_current_task, \tmp
+ st \tsk, [\tmp]
+#ifdef CONFIG_ARC_CURR_IN_REG
+ mov r25, \tsk
+#endif
+
+.endm
+
+
+#else /* Uniprocessor implementation of macros */
+
.macro GET_CURR_TASK_ON_CPU reg
ld \reg, [@_current_task]
.endm
@@ -610,6 +657,8 @@
#endif
.endm

+#endif /* SMP / UNI */
+
/* ------------------------------------------------------------------
* Get the ptr to some field of Current Task at @off in task struct
* -Uses r25 for Current task ptr if that is enabled
diff --git a/arch/arc/include/asm/mmu_context.h b/arch/arc/include/asm/mmu_context.h
index d12f3de..0d71fb1 100644
--- a/arch/arc/include/asm/mmu_context.h
+++ b/arch/arc/include/asm/mmu_context.h
@@ -147,8 +147,10 @@ init_new_context(struct task_struct *tsk, struct mm_struct *mm)
static inline void switch_mm(struct mm_struct *prev, struct mm_struct *next,
struct task_struct *tsk)
{
+#ifndef CONFIG_SMP
/* PGD cached in MMU reg to avoid 3 mem lookups: task->mm->pgd */
write_aux_reg(ARC_REG_SCRATCH_DATA0, next->pgd);
+#endif

/*
* Get a new ASID if task doesn't have a valid one. Possible when
@@ -197,7 +199,9 @@ static inline void destroy_context(struct mm_struct *mm)

static inline void activate_mm(struct mm_struct *prev, struct mm_struct *next)
{
+#ifndef CONFIG_SMP
write_aux_reg(ARC_REG_SCRATCH_DATA0, next->pgd);
+#endif

/* Unconditionally get a new ASID */
get_new_mmu_context(next);
diff --git a/arch/arc/include/asm/mutex.h b/arch/arc/include/asm/mutex.h
index 3be5e64..a2f88ff 100644
--- a/arch/arc/include/asm/mutex.h
+++ b/arch/arc/include/asm/mutex.h
@@ -6,4 +6,13 @@
* published by the Free Software Foundation.
*/

+/*
+ * xchg() based mutex fast path maintains a state of 0 or 1, as opposed to
+ * atomic dec based which can "count" any number of lock contenders.
+ * This ideally needs to be fixed in core, but for now switching to dec ver.
+ */
+#if defined(CONFIG_SMP) && (CONFIG_NR_CPUS > 2)
+#include <asm-generic/mutex-dec.h>
+#else
#include <asm-generic/mutex-xchg.h>
+#endif
diff --git a/arch/arc/include/asm/pgtable.h b/arch/arc/include/asm/pgtable.h
index dcb07015..b7e3668 100644
--- a/arch/arc/include/asm/pgtable.h
+++ b/arch/arc/include/asm/pgtable.h
@@ -354,11 +354,15 @@ static inline void set_pte_at(struct mm_struct *mm, unsigned long addr,
* Thus use this macro only when you are certain that "current" is current
* e.g. when dealing with signal frame setup code etc
*/
+#ifndef CONFIG_SMP
#define pgd_offset_fast(mm, addr) \
({ \
pgd_t *pgd_base = (pgd_t *) read_aux_reg(ARC_REG_SCRATCH_DATA0); \
pgd_base + pgd_index(addr); \
})
+#else
+#define pgd_offset_fast(mm, addr) pgd_offset(mm, addr)
+#endif

extern void paging_init(void);
extern pgd_t swapper_pg_dir[] __aligned(PAGE_SIZE);
diff --git a/arch/arc/include/asm/processor.h b/arch/arc/include/asm/processor.h
index 2fe9f22..dccef68 100644
--- a/arch/arc/include/asm/processor.h
+++ b/arch/arc/include/asm/processor.h
@@ -58,7 +58,15 @@ unsigned long thread_saved_pc(struct task_struct *t);
/* Prepare to copy thread state - unlazy all lazy status */
#define prepare_to_copy(tsk) do { } while (0)

+/*
+ * A lot of busy-wait loops in SMP are based off of non-volatile data otherwise
+ * get optimised away by gcc
+ */
+#ifdef CONFIG_SMP
+#define cpu_relax() __asm__ __volatile__ ("" : : : "memory")
+#else
#define cpu_relax() do { } while (0)
+#endif

/*
* Create a new kernel thread
diff --git a/arch/arc/include/asm/smp.h b/arch/arc/include/asm/smp.h
index 4341f3b..6ea4c52 100644
--- a/arch/arc/include/asm/smp.h
+++ b/arch/arc/include/asm/smp.h
@@ -9,6 +9,70 @@
#ifndef __ASM_ARC_SMP_H
#define __ASM_ARC_SMP_H

+#ifdef CONFIG_SMP
+
+#include <linux/types.h>
+#include <linux/init.h>
+#include <linux/threads.h>
+
+#define raw_smp_processor_id() (current_thread_info()->cpu)
+
+/* including cpumask.h leads to cyclic deps hence this Forward declaration */
+struct cpumask;
+
+/*
+ * APIs provided by arch SMP code to generic code
+ */
+extern void arch_send_call_function_single_ipi(int cpu);
+extern void arch_send_call_function_ipi_mask(const struct cpumask *mask);
+
+/*
+ * APIs provided by arch SMP code to rest of arch code
+ */
+extern void smp_ipi_init(void);
+extern void __init smp_init_cpus(void);
+extern void __init first_lines_of_secondary(void);
+
+/*
+ * API expected BY platform smp code (FROM arch smp code)
+ *
+ * smp_ipi_irq_setup:
+ * Takes @cpu and @irq to which the arch-common ISR is hooked up
+ */
+extern int smp_ipi_irq_setup(int cpu, int irq);
+
+/*
+ * APIs expected FROM platform smp code
+ *
+ * arc_platform_smp_cpuinfo:
+ * returns a string containing info for /proc/cpuinfo
+ *
+ * arc_platform_smp_init_cpu:
+ * Called from start_kernel_secondary to do any CPU local setup
+ * such as starting a timer, setting up IPI etc
+ *
+ * arc_platform_smp_wait_to_boot:
+ * Called from early bootup code for non-Master CPUs to "park" them
+ *
+ * arc_platform_smp_wakeup_cpu:
+ * Called from __cpu_up (Master CPU) to kick start another one
+ *
+ * arc_platform_ipi_send:
+ * Takes @cpumask to which IPI(s) would be sent.
+ * The actual msg-id/buffer is manager in arch-common code
+ *
+ * arc_platform_ipi_clear:
+ * Takes @cpu which got IPI at @irq to do any IPI clearing
+ */
+extern const char *arc_platform_smp_cpuinfo(void);
+extern void arc_platform_smp_init_cpu(void);
+extern void arc_platform_smp_wait_to_boot(int cpu);
+extern void arc_platform_smp_wakeup_cpu(int cpu, unsigned long pc);
+extern void arc_platform_ipi_send(const struct cpumask *callmap);
+extern void arc_platform_ipi_clear(int cpu, int irq);
+
+#endif /* CONFIG_SMP */
+
/*
* ARC700 doesn't support atomic Read-Modify-Write ops.
* Originally Interrupts had to be disabled around code to gaurantee atomicity.
@@ -18,10 +82,52 @@
*
* (1) These insn were introduced only in 4.10 release. So for older released
* support needed.
+ *
+ * (2) In a SMP setup, the LLOCK/SCOND atomiticity across CPUs needs to be
+ * gaurantted by the platform (not something which core handles).
+ * Assuming a platform won't, SMP Linux needs to use spinlocks + local IRQ
+ * disabling for atomicity.
+ *
+ * However exported spinlock API is not usable due to cyclic hdr deps
+ * (even after system.h disintegration upstream)
+ * asm/bitops.h -> linux/spinlock.h -> linux/preempt.h
+ * -> linux/thread_info.h -> linux/bitops.h -> asm/bitops.h
+ *
+ * So the workaround is to use the lowest level arch spinlock API.
+ * The exported spinlock API is smart enough to be NOP for !CONFIG_SMP,
+ * but same is not true for ARCH backend, hence the need for 2 variants
*/
#ifndef CONFIG_ARC_HAS_LLSC

#include <linux/irqflags.h>
+#ifdef CONFIG_SMP
+
+#include <asm/spinlock.h>
+
+extern arch_spinlock_t smp_atomic_ops_lock;
+extern arch_spinlock_t smp_bitops_lock;
+
+#define atomic_ops_lock(flags) do { \
+ local_irq_save(flags); \
+ arch_spin_lock(&smp_atomic_ops_lock); \
+} while (0)
+
+#define atomic_ops_unlock(flags) do { \
+ arch_spin_unlock(&smp_atomic_ops_lock); \
+ local_irq_restore(flags); \
+} while (0)
+
+#define bitops_lock(flags) do { \
+ local_irq_save(flags); \
+ arch_spin_lock(&smp_bitops_lock); \
+} while (0)
+
+#define bitops_unlock(flags) do { \
+ arch_spin_unlock(&smp_bitops_lock); \
+ local_irq_restore(flags); \
+} while (0)
+
+#else /* !CONFIG_SMP */

#define atomic_ops_lock(flags) local_irq_save(flags)
#define atomic_ops_unlock(flags) local_irq_restore(flags)
@@ -29,6 +135,8 @@
#define bitops_lock(flags) local_irq_save(flags)
#define bitops_unlock(flags) local_irq_restore(flags)

+#endif /* !CONFIG_SMP */
+
#endif /* !CONFIG_ARC_HAS_LLSC */

#endif
diff --git a/arch/arc/kernel/Makefile b/arch/arc/kernel/Makefile
index ec1f130..b2cf444 100644
--- a/arch/arc/kernel/Makefile
+++ b/arch/arc/kernel/Makefile
@@ -13,6 +13,7 @@ obj-y := arcksyms.o setup.o irq.o time.o reset.o ptrace.o entry.o process.o \

obj-$(CONFIG_MODULES) += arcksyms.o module.o
obj-$(CONFIG_ARC_DBG_EVENT_TIMELINE) += event-log.o
+obj-$(CONFIG_SMP) += smp.o
obj-$(CONFIG_ARC_FPU_SAVE_RESTORE) += fpu.o
CFLAGS_fpu.o += -mdpfp

diff --git a/arch/arc/kernel/ctx_sw.c b/arch/arc/kernel/ctx_sw.c
index fbf739c..60844da 100644
--- a/arch/arc/kernel/ctx_sw.c
+++ b/arch/arc/kernel/ctx_sw.c
@@ -58,7 +58,18 @@ __switch_to(struct task_struct *prev_task, struct task_struct *next_task)
* For SMP extra work to get to &_current_task[cpu]
* (open coded SET_CURR_TASK_ON_CPU)
*/
+#ifndef CONFIG_SMP
"st %2, [@_current_task] \n\t"
+#else
+ "lr r24, [identity] \n\t"
+ "lsr r24, r24, 8 \n\t"
+ "bmsk r24, r24, 7 \n\t"
+ "add2 r24, @_current_task, r24 \n\t"
+ "st %2, [r24] \n\t"
+#endif
+#ifdef CONFIG_ARC_CURR_IN_REG
+ "mov r25, %2 \n\t"
+#endif

/* get ksp of incoming task from tsk->thread.ksp */
"ld.as sp, [%2, %1] \n\t"
diff --git a/arch/arc/kernel/entry.S b/arch/arc/kernel/entry.S
index 5babb8a..7b24a5a 100644
--- a/arch/arc/kernel/entry.S
+++ b/arch/arc/kernel/entry.S
@@ -240,7 +240,11 @@ ARC_EXIT handle_interrupt_level2
ARC_ENTRY handle_interrupt_level1

/* free up r9 as scratchpad */
+#ifdef CONFIG_SMP
+ sr r9, [ARC_REG_SCRATCH_DATA0]
+#else
st r9, [@int1_saved_reg]
+#endif

;Which mode (user/kernel) was the system in when intr occured
lr r9, [status32_l1]
diff --git a/arch/arc/kernel/head.S b/arch/arc/kernel/head.S
index e63f6a4..006dec3 100644
--- a/arch/arc/kernel/head.S
+++ b/arch/arc/kernel/head.S
@@ -27,6 +27,15 @@ stext:
; Don't clobber r0-r4 yet. It might have bootloader provided info
;-------------------------------------------------------------------

+#ifdef CONFIG_SMP
+ ; Only Boot (Master) proceeds. Others wait in platform dependent way
+ ; IDENTITY Reg [ 3 2 1 0 ]
+ ; (cpu-id) ^^^ => Zero for UP ARC700
+ ; => #Core-ID if SMP (Master 0)
+ GET_CPU_ID r5
+ cmp r5, 0
+ jnz arc_platform_smp_wait_to_boot
+#endif
; Clear BSS before updating any globals
; XXX: use ZOL here
mov r5, __bss_start
@@ -76,3 +85,27 @@ stext:
GET_TSK_STACK_BASE r9, sp ; r9 = tsk, sp = stack base(output)

j start_kernel ; "C" entry point
+
+#ifdef CONFIG_SMP
+;----------------------------------------------------------------
+; First lines of code run by secondary before jumping to 'C'
+;----------------------------------------------------------------
+ .section .init.text, "ax",@progbits
+ .type first_lines_of_secondary, @function
+ .globl first_lines_of_secondary
+
+first_lines_of_secondary:
+
+ ; setup per-cpu idle task as "current" on this CPU
+ ld r0, [@secondary_idle_tsk]
+ SET_CURR_TASK_ON_CPU r0, r1
+
+ ; setup stack (fp, sp)
+ mov fp, 0
+
+ ; set it's stack base to tsk->thread_info bottom
+ GET_TSK_STACK_BASE r0, sp
+
+ j start_kernel_secondary
+
+#endif
diff --git a/arch/arc/kernel/irq.c b/arch/arc/kernel/irq.c
index afde5a7..32af2ee 100644
--- a/arch/arc/kernel/irq.c
+++ b/arch/arc/kernel/irq.c
@@ -73,6 +73,11 @@ void __init init_IRQ(void)
irq_modify_status(irq, IRQ_NOAUTOEN, 0);

plat_init_IRQ();
+
+#ifdef CONFIG_SMP
+ /* Master CPU to initialize the IPI framework */
+ arc_platform_smp_init_cpu();
+#endif
}

/*
diff --git a/arch/arc/kernel/setup.c b/arch/arc/kernel/setup.c
index 82ac206..258b9ee 100644
--- a/arch/arc/kernel/setup.c
+++ b/arch/arc/kernel/setup.c
@@ -79,6 +79,10 @@ void __init setup_arch(char **cmdline_p)

setup_processor();

+#ifdef CONFIG_SMP
+ smp_init_cpus();
+#endif
+
setup_arch_memory();

/* Can be issue if someone passes cmd line arg "ro"
diff --git a/arch/arc/kernel/smp.c b/arch/arc/kernel/smp.c
new file mode 100644
index 0000000..7a53bad
--- /dev/null
+++ b/arch/arc/kernel/smp.c
@@ -0,0 +1,295 @@
+/*
+ * Copyright (C) 2004, 2007-2010, 2011-2012 Synopsys, Inc. (http://www.synopsys.com)
+ *
+ * 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.
+ *
+ * RajeshwarR: Dec 11, 2007
+ * -- Added support for Inter Processor Interrupts
+ *
+ * Vineetg: Nov 1st, 2007
+ * -- Initial Write (Borrowed heavily from ARM)
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/spinlock.h>
+#include <linux/sched.h>
+#include <linux/interrupt.h>
+#include <linux/profile.h>
+#include <linux/errno.h>
+#include <linux/err.h>
+#include <linux/mm.h>
+#include <linux/cpu.h>
+#include <linux/smp.h>
+#include <linux/irq.h>
+#include <linux/delay.h>
+#include <linux/atomic.h>
+#include <linux/percpu.h>
+#include <linux/cpumask.h>
+#include <linux/spinlock_types.h>
+#include <asm/processor.h>
+#include <asm/setup.h>
+
+arch_spinlock_t smp_atomic_ops_lock = __ARCH_SPIN_LOCK_UNLOCKED;
+arch_spinlock_t smp_bitops_lock = __ARCH_SPIN_LOCK_UNLOCKED;
+
+/* XXX: per cpu ? Only needed once in early seconday boot */
+struct task_struct *secondary_idle_tsk;
+
+/* Called from start_kernel */
+void __init smp_prepare_boot_cpu(void)
+{
+}
+
+/*
+ * Initialise the CPU possible map early - this describes the CPUs
+ * which may be present or become present in the system.
+ */
+void __init smp_init_cpus(void)
+{
+ unsigned int i;
+
+ for (i = 0; i < NR_CPUS; i++)
+ set_cpu_possible(i, true);
+}
+
+/* called from init ( ) => process 1 */
+void __init smp_prepare_cpus(unsigned int max_cpus)
+{
+ int i;
+
+ /*
+ * Initialise the present map, which describes the set of CPUs
+ * actually populated at the present time.
+ */
+ for (i = 0; i < max_cpus; i++)
+ set_cpu_present(i, true);
+}
+
+void __init smp_cpus_done(unsigned int max_cpus)
+{
+
+}
+
+/*
+ * The very first "C" code executed by secondary
+ * Called from asm stub in head.S
+ * "current"/R25 already setup by low level boot code
+ */
+void __cpuinit start_kernel_secondary(void)
+{
+ struct mm_struct *mm = &init_mm;
+ unsigned int cpu = smp_processor_id();
+
+ /* MMU, Caches, Vector Table, Interrupts etc */
+ setup_processor();
+
+ atomic_inc(&mm->mm_users);
+ atomic_inc(&mm->mm_count);
+ current->active_mm = mm;
+
+ set_cpu_online(cpu, true);
+
+ pr_info("## CPU%u LIVE ##: Executing Code...\n", cpu);
+
+ arc_platform_smp_init_cpu();
+
+ /*
+ * XXX: Note that TIMER1 based cpu-local counters will not be
+ * synchronized given the time lag between time_init (boot-cpu only)
+ * vs. the time we reach here. However assuming that any SMP Linux
+ * deployments will use a recent enough ARC700 with a perfectly
+ * synchronized 64-bit CPU cycle counter backing the RTSC insn.
+ */
+ arc_clock_counter_setup();
+
+ arc_clockevent_init();
+
+ local_irq_enable();
+ preempt_disable();
+ cpu_idle();
+}
+
+/*
+ * Called from kernel_init( ) -> smp_init( ) - for each CPU
+ *
+ * At this point, Secondary Processor is "HALT"ed:
+ * -It booted, but was halted in head.S
+ * -It was configured to halt-on-reset
+ * So need to wake it up.
+ *
+ * Essential requirements being where to run from (PC) and stack (SP)
+*/
+int __cpuinit __cpu_up(unsigned int cpu, struct task_struct *idle)
+{
+ unsigned long wait_till;
+
+ secondary_idle_tsk = idle;
+
+ pr_info("Idle Task [%d] %p", cpu, idle);
+ pr_info("Trying to bring up CPU%u ...\n", cpu);
+
+ arc_platform_smp_wakeup_cpu(cpu,
+ (unsigned long)first_lines_of_secondary);
+
+ /* wait for 1 sec after kicking the secondary */
+ wait_till = jiffies + HZ;
+ while (time_before(jiffies, wait_till)) {
+ if (cpu_online(cpu))
+ break;
+ }
+
+ if (!cpu_online(cpu)) {
+ pr_info("Timeout: CPU%u FAILED to comeup !!!\n", cpu);
+ return -1;
+ }
+
+ return 0;
+}
+
+/*
+ * not supported here
+ */
+int __init setup_profiling_timer(unsigned int multiplier)
+{
+ return -EINVAL;
+}
+
+/*****************************************************************************/
+/* Inter Processor Interrupt Handling */
+/*****************************************************************************/
+
+/*
+ * structures for inter-processor calls
+ * A Collection of single bit ipi messages
+ *
+ */
+
+/*
+ * TODO_rajesh investigate tlb message types.
+ * IPI Timer not needed because each ARC has an individual Interrupting Timer
+ */
+enum ipi_msg_type {
+ IPI_NOP = 0,
+ IPI_RESCHEDULE = 1,
+ IPI_CALL_FUNC,
+ IPI_CALL_FUNC_SINGLE,
+ IPI_CPU_STOP
+};
+
+struct ipi_data {
+ unsigned long bits;
+};
+
+static DEFINE_PER_CPU(struct ipi_data, ipi_data);
+
+static void ipi_send_msg(const struct cpumask *callmap, enum ipi_msg_type msg)
+{
+ unsigned long flags;
+ unsigned int cpu;
+
+ local_irq_save(flags);
+
+ for_each_cpu(cpu, callmap) {
+ struct ipi_data *ipi = &per_cpu(ipi_data, cpu);
+ set_bit(msg, &ipi->bits);
+ }
+
+ /* Call the platform specific cross-CPU call function */
+ arc_platform_ipi_send(callmap);
+
+ local_irq_restore(flags);
+}
+
+void smp_send_reschedule(int cpu)
+{
+ ipi_send_msg(cpumask_of(cpu), IPI_RESCHEDULE);
+}
+
+void smp_send_stop(void)
+{
+ struct cpumask targets;
+ cpumask_copy(&targets, cpu_online_mask);
+ cpumask_clear_cpu(smp_processor_id(), &targets);
+ ipi_send_msg(&targets, IPI_CPU_STOP);
+}
+
+void arch_send_call_function_single_ipi(int cpu)
+{
+ ipi_send_msg(cpumask_of(cpu), IPI_CALL_FUNC_SINGLE);
+}
+
+void arch_send_call_function_ipi_mask(const struct cpumask *mask)
+{
+ ipi_send_msg(mask, IPI_CALL_FUNC);
+}
+
+/*
+ * ipi_cpu_stop - handle IPI from smp_send_stop()
+ */
+static void ipi_cpu_stop(unsigned int cpu)
+{
+ __asm__("flag 1");
+}
+
+static inline void __do_IPI(unsigned long *ops, struct ipi_data *ipi, int cpu)
+{
+ unsigned long msg = 0;
+
+ do {
+ msg = find_next_bit(ops, BITS_PER_LONG, msg+1);
+
+ switch (msg) {
+ case IPI_RESCHEDULE:
+ scheduler_ipi();
+ break;
+
+ case IPI_CALL_FUNC:
+ generic_smp_call_function_interrupt();
+ break;
+
+ case IPI_CALL_FUNC_SINGLE:
+ generic_smp_call_function_single_interrupt();
+ break;
+
+ case IPI_CPU_STOP:
+ ipi_cpu_stop(cpu);
+ break;
+ }
+ } while (msg < BITS_PER_LONG);
+
+}
+
+/*
+ * arch-common ISR to handle for inter-processor interrupts
+ * Has hooks for platform specific IPI
+ */
+irqreturn_t do_IPI(int irq, void *dev_id)
+{
+ int cpu = smp_processor_id();
+ struct ipi_data *ipi = &per_cpu(ipi_data, cpu);
+ unsigned long ops;
+
+ arc_platform_ipi_clear(cpu, irq);
+
+ /*
+ * XXX: is this loop really needed
+ * And do we need to move ipi_clean inside
+ */
+ while ((ops = xchg(&ipi->bits, 0)) != 0)
+ __do_IPI(&ops, ipi, cpu);
+
+ return IRQ_HANDLED;
+}
+
+/*
+ * API called by platform code to hookup arch-common ISR to their IPI IRQ
+ */
+static DEFINE_PER_CPU(int, ipi_dev);
+int smp_ipi_irq_setup(int cpu, int irq)
+{
+ int *dev_id = &per_cpu(ipi_dev, smp_processor_id());
+ return request_percpu_irq(irq, do_IPI, "IPI Interrupt", dev_id);
+}
diff --git a/arch/arc/mm/tlb.c b/arch/arc/mm/tlb.c
index 0e5cb9f..defa0c0 100644
--- a/arch/arc/mm/tlb.c
+++ b/arch/arc/mm/tlb.c
@@ -462,6 +462,12 @@ void __init arc_mmu_init(void)

/* Enable the MMU */
write_aux_reg(ARC_REG_PID, MMU_ENABLE);
+
+ /* In smp we use this reg for interrupt 1 scratch */
+#ifndef CONFIG_SMP
+ /* swapper_pg_dir is the pgd for the kernel, used by vmalloc */
+ write_aux_reg(ARC_REG_SCRATCH_DATA0, swapper_pg_dir);
+#endif
}

/*
diff --git a/arch/arc/mm/tlbex.S b/arch/arc/mm/tlbex.S
index a95d0f7..e933e4c 100644
--- a/arch/arc/mm/tlbex.S
+++ b/arch/arc/mm/tlbex.S
@@ -58,9 +58,15 @@
.global ex_saved_reg1
.align 1 << L1_CACHE_SHIFT ; IMP: Must be Cache Line aligned
.type ex_saved_reg1, @object
+#ifdef CONFIG_SMP
+ .size ex_saved_reg1, (CONFIG_NR_CPUS << L1_CACHE_SHIFT)
+ex_saved_reg1:
+ .zero (CONFIG_NR_CPUS << L1_CACHE_SHIFT)
+#else
.size ex_saved_reg1, 16
ex_saved_reg1:
.zero 16
+#endif

;============================================================================
; Troubleshooting Stuff
@@ -117,7 +123,13 @@ ex_saved_reg1:

lr r2, [efa]

+#ifndef CONFIG_SMP
lr r1, [ARC_REG_SCRATCH_DATA0] ; current pgd
+#else
+ GET_CURR_TASK_ON_CPU r1
+ ld r1, [r1, TASK_ACT_MM]
+ ld r1, [r1, MM_PGD]
+#endif

lsr r0, r2, PGDIR_SHIFT ; Bits for indexing into PGD
ld.as r1, [r1, r0] ; PGD entry corresp to faulting addr
@@ -193,12 +205,28 @@ ex_saved_reg1:
; ".size ex_saved_reg1, 16"
; [All of this dance is to avoid stack switching for each TLB Miss, since we
; only need to save only a handful of regs, as opposed to complete reg file]
+;
+; For ARC700 SMP, the "global" obviously can't be used for free up the FIRST
+; core reg as it will not be SMP safe.
+; Thus scratch AUX reg is used (and no longer used to cache task PGD).
+; To save the rest of 3 regs - per cpu, the global is made "per-cpu".
+; Epilogue thus has to locate the "per-cpu" storage for regs.
+; To avoid cache line bouncing the per-cpu global is aligned/sized per
+; L1_CACHE_SHIFT, despite fundamentally needing to be 12 bytes only. Hence
+; ".size ex_saved_reg1, (CONFIG_NR_CPUS << L1_CACHE_SHIFT)"

; As simple as that....

.macro TLBMISS_FREEUP_REGS
+#ifdef CONFIG_SMP
+ sr r0, [ARC_REG_SCRATCH_DATA0] ; freeup r0 to code with
+ GET_CPU_ID r0 ; get to per cpu scratch mem,
+ lsl r0, r0, L1_CACHE_SHIFT ; cache line wide per cpu
+ add r0, @ex_saved_reg1, r0
+#else
st r0, [@ex_saved_reg1]
mov_s r0, @ex_saved_reg1
+#endif
st_s r1, [r0, 4]
st_s r2, [r0, 8]
st_s r3, [r0, 12]
@@ -214,11 +242,21 @@ ex_saved_reg1:

;-----------------------------------------------------------------
.macro TLBMISS_RESTORE_REGS
+#ifdef CONFIG_SMP
+ GET_CPU_ID r0 ; get to per cpu scratch mem
+ lsl r0, r0, L1_CACHE_SHIFT ; each is cache line wide
+ add r0, @ex_saved_reg1, r0
+ ld_s r3, [r0,12]
+ ld_s r2, [r0, 8]
+ ld_s r1, [r0, 4]
+ lr r0, [ARC_REG_SCRATCH_DATA0]
+#else
mov_s r0, @ex_saved_reg1
ld_s r3, [r0,12]
ld_s r2, [r0, 8]
ld_s r1, [r0, 4]
ld_s r0, [r0]
+#endif
.endm

.section .text, "ax",@progbits ;Fast Path Code, candidate for ICCM
diff --git a/arch/arc/plat-arcfpga/Kconfig b/arch/arc/plat-arcfpga/Kconfig
index 3b399ca..1b65644 100644
--- a/arch/arc/plat-arcfpga/Kconfig
+++ b/arch/arc/plat-arcfpga/Kconfig
@@ -23,6 +23,18 @@ config ARC_BOARD_ML509

endchoice

+config ISS_SMP_EXTN
+ bool "ARC SMP Extensions (ISS Models only)"
+ default n
+ select ARC_HAS_IPI
+ help
+ SMP Extensions to ARC700, in a "simulation only" Model, supported in
+ ARC ISS (Instruction Set Simulator).
+ The SMP extensions include:
+ -IDU (Interrupt Distribution Unit)
+ -XTL (To enable CPU start/stop/set-PC for another CPU)
+ It doesn't provide coherent Caches and/or Atomic Ops (LLOCK/SCOND)
+
config ARC_SERIAL_BAUD
int "UART Baud rate"
default "115200"
diff --git a/arch/arc/plat-arcfpga/Makefile b/arch/arc/plat-arcfpga/Makefile
index 385eb9d..7b97445 100644
--- a/arch/arc/plat-arcfpga/Makefile
+++ b/arch/arc/plat-arcfpga/Makefile
@@ -7,3 +7,4 @@
#

obj-y := platform.o irq.o
+obj-$(CONFIG_ISS_SMP_EXTN) += smp.o
\ No newline at end of file
diff --git a/arch/arc/plat-arcfpga/include/plat/irq.h b/arch/arc/plat-arcfpga/include/plat/irq.h
index b34e087..255b90e 100644
--- a/arch/arc/plat-arcfpga/include/plat/irq.h
+++ b/arch/arc/plat-arcfpga/include/plat/irq.h
@@ -12,7 +12,11 @@
#ifndef __PLAT_IRQ_H
#define __PLAT_IRQ_H

-#define NR_IRQS 16
+#ifdef CONFIG_SMP
+#define NR_IRQS 32
+#else
+#define NR_IRQS 16
+#endif

#define UART0_IRQ 5
#define UART1_IRQ 10
@@ -24,4 +28,8 @@
#define PCI_IRQ 14
#define PS2_IRQ 15

+#ifdef CONFIG_SMP
+#define IDU_INTERRUPT_0 16
+#endif
+
#endif
diff --git a/arch/arc/plat-arcfpga/include/plat/smp.h b/arch/arc/plat-arcfpga/include/plat/smp.h
new file mode 100644
index 0000000..8c5e46c
--- /dev/null
+++ b/arch/arc/plat-arcfpga/include/plat/smp.h
@@ -0,0 +1,115 @@
+/*
+ * Copyright (C) 2004, 2007-2010, 2011-2012 Synopsys, Inc. (http://www.synopsys.com)
+ *
+ * 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.
+ *
+ * Rajeshwar Ranga: Interrupt Distribution Unit API's
+ */
+
+#ifndef __PLAT_ARCFPGA_SMP_H
+#define __PLAT_ARCFPGA_SMP_H
+
+#ifdef CONFIG_SMP
+
+#include <linux/types.h>
+#include <asm/arcregs.h>
+
+#define ARC_AUX_IDU_REG_CMD 0x2000
+#define ARC_AUX_IDU_REG_PARAM 0x2001
+
+#define ARC_AUX_XTL_REG_CMD 0x2002
+#define ARC_AUX_XTL_REG_PARAM 0x2003
+
+#define ARC_REG_MP_BCR 0x2021
+
+#define ARC_XTL_CMD_WRITE_PC 0x04
+#define ARC_XTL_CMD_CLEAR_HALT 0x02
+
+/*
+ * Build Configuration Register which identifies the sub-components
+ */
+struct bcr_mp {
+#ifdef CONFIG_CPU_BIG_ENDIAN
+ unsigned int mp_arch:16, pad:5, sdu:1, idu:1, scu:1, ver:8;
+#else
+ unsigned int ver:8, scu:1, idu:1, sdu:1, pad:5, mp_arch:16;
+#endif
+};
+
+/* IDU supports 256 common interrupts */
+#define NR_IDU_IRQS 256
+
+/*
+ * The Aux Regs layout is same bit-by-bit in both BE/LE modes.
+ * However when casted as a bitfield encoded "C" struct, gcc treats it as
+ * memory, generating different code for BE/LE, requiring strcture adj (see
+ * include/asm/arcregs.h)
+ *
+ * However when manually "carving" the value for a Aux, no special handling
+ * of BE is needed because of the property discribed above
+ */
+#define IDU_SET_COMMAND(irq, cmd) \
+do { \
+ uint32_t __val; \
+ __val = (((irq & 0xFF) << 8) | (cmd & 0xFF)); \
+ write_aux_reg(ARC_AUX_IDU_REG_CMD, __val); \
+} while (0)
+
+#define IDU_SET_PARAM(par) write_aux_reg(ARC_AUX_IDU_REG_PARAM, par)
+#define IDU_GET_PARAM() read_aux_reg(ARC_AUX_IDU_REG_PARAM)
+
+/* IDU Commands */
+#define IDU_DISABLE 0x00
+#define IDU_ENABLE 0x01
+#define IDU_IRQ_CLEAR 0x02
+#define IDU_IRQ_ASSERT 0x03
+#define IDU_IRQ_WMODE 0x04
+#define IDU_IRQ_STATUS 0x05
+#define IDU_IRQ_ACK 0x06
+#define IDU_IRQ_PEND 0x07
+#define IDU_IRQ_RMODE 0x08
+#define IDU_IRQ_WBITMASK 0x09
+#define IDU_IRQ_RBITMASK 0x0A
+
+#define idu_enable() IDU_SET_COMMAND(0, IDU_ENABLE)
+#define idu_disable() IDU_SET_COMMAND(0, IDU_DISABLE)
+
+#define idu_irq_assert(irq) IDU_SET_COMMAND((irq), IDU_IRQ_ASSERT)
+#define idu_irq_clear(irq) IDU_SET_COMMAND((irq), IDU_IRQ_CLEAR)
+
+/* IDU Interrupt Mode - Destination Encoding */
+#define IDU_IRQ_MOD_DISABLE 0x00
+#define IDU_IRQ_MOD_ROUND_RECP 0x01
+#define IDU_IRQ_MOD_TCPU_FIRSTRECP 0x02
+#define IDU_IRQ_MOD_TCPU_ALLRECP 0x03
+
+/* IDU Interrupt Mode - Triggering Mode */
+#define IDU_IRQ_MODE_LEVEL_TRIG 0x00
+#define IDU_IRQ_MODE_PULSE_TRIG 0x01
+
+#define IDU_IRQ_MODE_PARAM(dest_mode, trig_mode) \
+ (((trig_mode & 0x01) << 15) | (dest_mode & 0xFF))
+
+struct idu_irq_config {
+ uint8_t irq;
+ uint8_t dest_mode;
+ uint8_t trig_mode;
+};
+
+struct idu_irq_status {
+ uint8_t irq;
+ bool enabled;
+ bool status;
+ bool ack;
+ bool pend;
+ uint8_t next_rr;
+};
+
+extern void idu_irq_set_tgtcpu(uint8_t irq, uint32_t mask);
+extern void idu_irq_set_mode(uint8_t irq, uint8_t dest_mode, uint8_t trig_mode);
+
+#endif /* CONFIG_SMP */
+
+#endif
diff --git a/arch/arc/plat-arcfpga/irq.c b/arch/arc/plat-arcfpga/irq.c
index a308057..ddb7d2a 100644
--- a/arch/arc/plat-arcfpga/irq.c
+++ b/arch/arc/plat-arcfpga/irq.c
@@ -38,4 +38,14 @@ void __init plat_init_IRQ(void)

for (i = 0; i < NR_IRQS; i++)
irq_set_chip_and_handler(i, &fpga_chip, handle_level_irq);
+
+ /*
+ * SMP Hack because UART IRQ hardwired to cpu0 (boot-cpu) but if the
+ * request_irq() comes from any other CPU, the low level IRQ unamsking
+ * essential for getting Interrupts won't be enabled on cpu0, locking
+ * up the UART state machine.
+ */
+#ifdef CONFIG_SMP
+ arch_unmask_irq(UART0_IRQ);
+#endif
}
diff --git a/arch/arc/plat-arcfpga/smp.c b/arch/arc/plat-arcfpga/smp.c
new file mode 100644
index 0000000..15cf889
--- /dev/null
+++ b/arch/arc/plat-arcfpga/smp.c
@@ -0,0 +1,192 @@
+/*
+ * ARC700 Simulation-only Extensions for SMP
+ *
+ * Copyright (C) 2004, 2007-2010, 2011-2012 Synopsys, Inc. (http://www.synopsys.com)
+ *
+ * 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.
+ *
+ * Vineet Gupta - 2012 : split off arch common and plat specific SMP
+ * Rajeshwar Ranga - 2007 : Interrupt Distribution Unit API's
+ */
+
+#include <linux/smp.h>
+#include <asm/irq.h>
+#include <plat/smp.h>
+
+static char smp_cpuinfo_buf[128];
+
+/*
+ *-------------------------------------------------------------------
+ * Platform specific callbacks expected by arch SMP code
+ *-------------------------------------------------------------------
+ */
+
+const char *arc_platform_smp_cpuinfo(void)
+{
+#define IS_AVAIL1(var, str) ((var) ? str : "")
+
+ struct bcr_mp mp;
+
+ READ_BCR(ARC_REG_MP_BCR, mp);
+
+ sprintf(smp_cpuinfo_buf, "Extn [700-SMP]: v%d, arch(%d) %s %s %s\n",
+ mp.ver, mp.mp_arch, IS_AVAIL1(mp.scu, "SCU"),
+ IS_AVAIL1(mp.idu, "IDU"), IS_AVAIL1(mp.sdu, "SDU"));
+
+ return smp_cpuinfo_buf;
+}
+
+/*
+ * After power-up, a non Master CPU needs to wait for Master to kick start it
+ *
+ * W/o hardware assist, it will busy-spin on a token which is eventually set
+ * by Master, preferably from arc_platform_smp_wakeup_cpu(). Once token is
+ * available it needs to jump to @first_lines_of_secondary (using inline asm).
+ *
+ * The XTL H/w module, however allows Master to directly set Other CPU's PC as
+ * well as ability to start/stop them. This allows implementing this function
+ * as a simple dead stop using "FLAG 1" insn.
+ * As a hack for debugging (debugger will single-step over the FLAG insn), we
+ * anyways wrap it in a self loop
+ *
+ * Alert: can NOT use stack here as it has not been determined/setup for CPU.
+ * If it turns out to be elaborate, it's better to code it in assembly
+ */
+void arc_platform_smp_wait_to_boot(int cpu)
+{
+ /* Secondary Halts self. Later master will set PC and clear halt bit */
+ __asm__ __volatile__(
+ "1: \n"
+ " flag 1 \n"
+ " b 1b \n");
+}
+
+/*
+ * Master kick starting another CPU
+ */
+void arc_platform_smp_wakeup_cpu(int cpu, unsigned long pc)
+{
+ /* setup the start PC */
+ write_aux_reg(ARC_AUX_XTL_REG_PARAM, pc);
+
+ /* Trigger WRITE_PC cmd for this cpu */
+ write_aux_reg(ARC_AUX_XTL_REG_CMD,
+ (ARC_XTL_CMD_WRITE_PC | (cpu << 8)));
+
+ /* Take the cpu out of Halt */
+ write_aux_reg(ARC_AUX_XTL_REG_CMD,
+ (ARC_XTL_CMD_CLEAR_HALT | (cpu << 8)));
+
+}
+
+/*
+ * Any SMP specific init any CPU does when it comes up.
+ * Here we setup the CPU to enable Inter-Processor-Interrupts
+ * Called for each CPU
+ * -Master : init_IRQ()
+ * -Other(s) : start_kernel_secondary()
+ */
+void arc_platform_smp_init_cpu(void)
+{
+ int cpu = smp_processor_id();
+
+ /* Check if CPU is configured for more than 16 interrupts */
+ if (NR_IRQS <= 16 || get_hw_config_num_irq() <= 16)
+ panic("[arcfpga] IRQ system can't support IDU IPI\n");
+
+ idu_disable();
+
+ /****************************************************************
+ * IDU provides a set of Common IRQs, each of which can be dynamically
+ * attached to (1|many|all) CPUs.
+ * The Common IRQs [0-15] are mapped as CPU pvt [16-31]
+ *
+ * Here we use a simple 1:1 mapping:
+ * A CPU 'x' is wired to Common IRQ 'x'.
+ * So an IDU ASSERT on IRQ 'x' will trigger Interupt on CPU 'x', which
+ * makes up for our simple IPI plumbing.
+ *
+ * TBD: Have a dedicated multicast IRQ for sending IPIs to all CPUs
+ * w/o having to do one-at-a-time
+ ******************************************************************/
+
+ /*
+ * Claim an IRQ which would trigger IPI on this CPU.
+ * In IDU parlance it involves setting up a cpu bitmask for the IRQ
+ * The bitmap here contains only 1 CPU (self).
+ */
+ idu_irq_set_tgtcpu(cpu, 0x1 << cpu);
+
+ /* Set the IRQ destination to use the bitmask above */
+ idu_irq_set_mode(cpu, 7, /* XXX: IDU_IRQ_MOD_TCPU_ALLRECP: ISS bug */
+ IDU_IRQ_MODE_PULSE_TRIG);
+
+ idu_enable();
+
+ /* Attach the arch-common IPI ISR to our IDU IRQ */
+ smp_ipi_irq_setup(cpu, IDU_INTERRUPT_0 + cpu);
+}
+
+void arc_platform_ipi_send(const struct cpumask *callmap)
+{
+ unsigned int cpu;
+
+ for_each_cpu(cpu, callmap)
+ idu_irq_assert(cpu);
+}
+
+void arc_platform_ipi_clear(int cpu, int irq)
+{
+ idu_irq_clear(IDU_INTERRUPT_0 + cpu);
+}
+
+/*
+ *-------------------------------------------------------------------
+ * Low level Platform IPI Providers
+ *-------------------------------------------------------------------
+ */
+
+/* Set the Mode for the Common IRQ */
+void idu_irq_set_mode(uint8_t irq, uint8_t dest_mode, uint8_t trig_mode)
+{
+ uint32_t par = IDU_IRQ_MODE_PARAM(dest_mode, trig_mode);
+
+ IDU_SET_PARAM(par);
+ IDU_SET_COMMAND(irq, IDU_IRQ_WMODE);
+}
+
+/* Set the target cpu Bitmask for Common IRQ */
+void idu_irq_set_tgtcpu(uint8_t irq, uint32_t mask)
+{
+ IDU_SET_PARAM(mask);
+ IDU_SET_COMMAND(irq, IDU_IRQ_WBITMASK);
+}
+
+/* Get the Interrupt Acknowledged status for IRQ (as CPU Bitmask) */
+bool idu_irq_get_ack(uint8_t irq)
+{
+ uint32_t val;
+
+ IDU_SET_COMMAND(irq, IDU_IRQ_ACK);
+ val = IDU_GET_PARAM();
+
+ return val & (1 << irq);
+}
+
+/*
+ * Get the Interrupt Pending status for IRQ (as CPU Bitmask)
+ * -Pending means CPU has not yet noticed the IRQ (e.g. disabled)
+ * -After Interrupt has been taken, the IPI expcitily needs to be
+ * cleared, to be acknowledged.
+ */
+bool idu_irq_get_pend(uint8_t irq)
+{
+ uint32_t val;
+
+ IDU_SET_COMMAND(irq, IDU_IRQ_PEND);
+ val = IDU_GET_PARAM();
+
+ return val & (1 << irq);
+}
--
1.7.4.1

2012-11-12 11:51:10

by Vineet Gupta

[permalink] [raw]
Subject: [RFC Patch v1 41/55] ARC: dwarf2 stack unwinder

From: Vineet Gupta <[email protected]>

-Originally controbuted by Rajeshwar Range <rajeshwar.ranga>
-Derived off of generic unwinder in 2.6.19 and adapted to ARC

Signed-off-by: Vineet Gupta <[email protected]>
---
arch/arc/Kconfig | 15 +
arch/arc/include/asm/module.h | 7 +
arch/arc/include/asm/unwind.h | 163 +++++
arch/arc/kernel/Makefile | 6 +
arch/arc/kernel/entry.S | 9 +
arch/arc/kernel/module.c | 42 ++
arch/arc/kernel/setup.c | 3 +
arch/arc/kernel/unwind.c | 1329 +++++++++++++++++++++++++++++++++++++++++
arch/arc/kernel/vmlinux.lds.S | 23 +-
9 files changed, 1596 insertions(+), 1 deletions(-)
create mode 100644 arch/arc/include/asm/unwind.h
create mode 100644 arch/arc/kernel/unwind.c

diff --git a/arch/arc/Kconfig b/arch/arc/Kconfig
index 8d72ada..26609d6 100644
--- a/arch/arc/Kconfig
+++ b/arch/arc/Kconfig
@@ -22,6 +22,7 @@ config ARC
select GENERIC_SMP_IDLE_THREAD
select HAVE_ARCH_TRACEHOOK
select HAVE_GENERIC_HARDIRQS
+ select HAVE_MOD_ARCH_SPECIFIC if ARC_DW2_UNWIND
select HAVE_OPROFILE
select HOTPLUG if !INITRAMFS_SOURCE=""
select MODULES_USE_ELF_RELA
@@ -355,6 +356,20 @@ menuconfig ARC_DBG
bool "ARC debugging"
default y

+config ARC_DW2_UNWIND
+ bool "Enable DWARF specific kernel stack unwind"
+ depends on ARC_DBG
+ default y
+ select KALLSYMS
+ help
+ Compiles the kernel with DWARF unwind information and can be used
+ to get stack backtraces.
+
+ If you say Y here the resulting kernel image will be slightly larger
+ but not slower, and it will give very useful debugging information.
+ If you don't debug the kernel, you can say N, but we may not be able
+ to solve problems without frame unwind information
+
config ARC_DBG_TLB_PARANOIA
bool "Paranoia Checks in Low Level TLB Handlers"
depends on ARC_DBG && !SMP
diff --git a/arch/arc/include/asm/module.h b/arch/arc/include/asm/module.h
index 234b435..518222b 100644
--- a/arch/arc/include/asm/module.h
+++ b/arch/arc/include/asm/module.h
@@ -14,6 +14,13 @@

#include <asm-generic/module.h>

+#ifdef CONFIG_ARC_DW2_UNWIND
+struct mod_arch_specific {
+ void *unw_info;
+ int unw_sec_idx;
+};
+#endif
+
#define MODULE_PROC_FAMILY "ARC700"

#define MODULE_ARCH_VERMAGIC MODULE_PROC_FAMILY
diff --git a/arch/arc/include/asm/unwind.h b/arch/arc/include/asm/unwind.h
new file mode 100644
index 0000000..7ca628b
--- /dev/null
+++ b/arch/arc/include/asm/unwind.h
@@ -0,0 +1,163 @@
+/*
+ * Copyright (C) 2004, 2007-2010, 2011-2012 Synopsys, Inc. (http://www.synopsys.com)
+ *
+ * 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.
+ */
+
+#ifndef _ASM_ARC_UNWIND_H
+#define _ASM_ARC_UNWIND_H
+
+#ifdef CONFIG_ARC_DW2_UNWIND
+
+#include <linux/sched.h>
+
+struct arc700_regs {
+ unsigned long r0;
+ unsigned long r1;
+ unsigned long r2;
+ unsigned long r3;
+ unsigned long r4;
+ unsigned long r5;
+ unsigned long r6;
+ unsigned long r7;
+ unsigned long r8;
+ unsigned long r9;
+ unsigned long r10;
+ unsigned long r11;
+ unsigned long r12;
+ unsigned long r13;
+ unsigned long r14;
+ unsigned long r15;
+ unsigned long r16;
+ unsigned long r17;
+ unsigned long r18;
+ unsigned long r19;
+ unsigned long r20;
+ unsigned long r21;
+ unsigned long r22;
+ unsigned long r23;
+ unsigned long r24;
+ unsigned long r25;
+ unsigned long r26;
+ unsigned long r27; /* fp */
+ unsigned long r28; /* sp */
+ unsigned long r29;
+ unsigned long r30;
+ unsigned long r31; /* blink */
+ unsigned long r63; /* pc */
+};
+
+struct unwind_frame_info {
+ struct arc700_regs regs;
+ struct task_struct *task;
+ unsigned call_frame:1;
+};
+
+#define UNW_PC(frame) ((frame)->regs.r63)
+#define UNW_SP(frame) ((frame)->regs.r28)
+#define UNW_BLINK(frame) ((frame)->regs.r31)
+
+/* Rajesh FIXME */
+#ifdef CONFIG_FRAME_POINTER
+#define UNW_FP(frame) ((frame)->regs.r27)
+#define FRAME_RETADDR_OFFSET 4
+#define FRAME_LINK_OFFSET 0
+#define STACK_BOTTOM_UNW(tsk) STACK_LIMIT((tsk)->thread.ksp)
+#define STACK_TOP_UNW(tsk) ((tsk)->thread.ksp)
+#else
+#define UNW_FP(frame) ((void)(frame), 0)
+#endif
+
+#define STACK_LIMIT(ptr) (((ptr) - 1) & ~(THREAD_SIZE - 1))
+
+#define UNW_REGISTER_INFO \
+ PTREGS_INFO(r0), \
+ PTREGS_INFO(r1), \
+ PTREGS_INFO(r2), \
+ PTREGS_INFO(r3), \
+ PTREGS_INFO(r4), \
+ PTREGS_INFO(r5), \
+ PTREGS_INFO(r6), \
+ PTREGS_INFO(r7), \
+ PTREGS_INFO(r8), \
+ PTREGS_INFO(r9), \
+ PTREGS_INFO(r10), \
+ PTREGS_INFO(r11), \
+ PTREGS_INFO(r12), \
+ PTREGS_INFO(r13), \
+ PTREGS_INFO(r14), \
+ PTREGS_INFO(r15), \
+ PTREGS_INFO(r16), \
+ PTREGS_INFO(r17), \
+ PTREGS_INFO(r18), \
+ PTREGS_INFO(r19), \
+ PTREGS_INFO(r20), \
+ PTREGS_INFO(r21), \
+ PTREGS_INFO(r22), \
+ PTREGS_INFO(r23), \
+ PTREGS_INFO(r24), \
+ PTREGS_INFO(r25), \
+ PTREGS_INFO(r26), \
+ PTREGS_INFO(r27), \
+ PTREGS_INFO(r28), \
+ PTREGS_INFO(r29), \
+ PTREGS_INFO(r30), \
+ PTREGS_INFO(r31), \
+ PTREGS_INFO(r63)
+
+#define UNW_DEFAULT_RA(raItem, dataAlign) \
+ ((raItem).where == Memory && !((raItem).value * (dataAlign) + 4))
+
+extern int arc_unwind(struct unwind_frame_info *frame);
+extern void arc_unwind_init(void);
+extern void arc_unwind_setup(void);
+extern void *unwind_add_table(struct module *module, const void *table_start,
+ unsigned long table_size);
+extern void unwind_remove_table(void *handle, int init_only);
+
+static inline int
+arch_unwind_init_running(struct unwind_frame_info *info,
+ int (*callback) (struct unwind_frame_info *info,
+ void *arg),
+ void *arg)
+{
+ return 0;
+}
+
+static inline int arch_unw_user_mode(const struct unwind_frame_info *info)
+{
+ return 0;
+}
+
+static inline void arch_unw_init_blocked(struct unwind_frame_info *info)
+{
+ return;
+}
+
+static inline void arch_unw_init_frame_info(struct unwind_frame_info *info,
+ struct pt_regs *regs)
+{
+ return;
+}
+
+#else
+
+#define UNW_PC(frame) ((void)(frame), 0)
+#define UNW_SP(frame) ((void)(frame), 0)
+#define UNW_FP(frame) ((void)(frame), 0)
+
+static inline void arc_unwind_init(void)
+{
+}
+
+static inline void arc_unwind_setup(void)
+{
+}
+#define unwind_add_table(a, b, c)
+#define unwind_remove_table(a, b)
+
+#endif /* CONFIG_ARC_DW2_UNWIND */
+
+#endif /* _ASM_ARC_UNWIND_H */
diff --git a/arch/arc/kernel/Makefile b/arch/arc/kernel/Makefile
index b2cf444..86b159f 100644
--- a/arch/arc/kernel/Makefile
+++ b/arch/arc/kernel/Makefile
@@ -14,9 +14,15 @@ obj-y := arcksyms.o setup.o irq.o time.o reset.o ptrace.o entry.o process.o \
obj-$(CONFIG_MODULES) += arcksyms.o module.o
obj-$(CONFIG_ARC_DBG_EVENT_TIMELINE) += event-log.o
obj-$(CONFIG_SMP) += smp.o
+obj-$(CONFIG_ARC_DW2_UNWIND) += unwind.o
obj-$(CONFIG_ARC_FPU_SAVE_RESTORE) += fpu.o
CFLAGS_fpu.o += -mdpfp

+ifdef CONFIG_ARC_DW2_UNWIND
+CFLAGS_ctx_sw.o += -fno-omit-frame-pointer
+obj-y += ctx_sw.o
+else
obj-y += ctx_sw_asm.o
+endif

extra-y := vmlinux.lds head.o
diff --git a/arch/arc/kernel/entry.S b/arch/arc/kernel/entry.S
index 7b24a5a..13d6feb 100644
--- a/arch/arc/kernel/entry.S
+++ b/arch/arc/kernel/entry.S
@@ -868,3 +868,12 @@ ARC_ENTRY sys_clone_wrapper

b ret_from_system_call
ARC_EXIT sys_clone_wrapper
+
+#ifdef CONFIG_ARC_DW2_UNWIND
+; Workaround for bug 94179 (STAR ):
+; Despite -fasynchronous-unwind-tables, linker is not making dwarf2 unwinder
+; section (.debug_frame) as loadable. So we force it here.
+; This also fixes STAR 9000487933 where the prev-workaround (objcopy --setflag)
+; would not work after a clean build due to kernel build system dependencies.
+.section .debug_frame, "wa",@progbits
+#endif
diff --git a/arch/arc/kernel/module.c b/arch/arc/kernel/module.c
index cc13df4..fd38132 100644
--- a/arch/arc/kernel/module.c
+++ b/arch/arc/kernel/module.c
@@ -14,6 +14,7 @@
#include <linux/slab.h>
#include <linux/fs.h>
#include <linux/string.h>
+#include <asm/unwind.h>

static inline void arc_write_me(unsigned short *addr, unsigned long value)
{
@@ -35,14 +36,40 @@ void module_free(struct module *module, void *region)
vfree(region);
}

+/* ARC specific section quirks - before relocation loop in generic loader
+ *
+ * For dwarf unwinding out of modules, this needs to
+ * 1. Ensure the .debug_frame is allocatable (ARC Linker bug: despite
+ * -fasynchronous-unwind-tables it doesn't).
+ * 2. Since we are iterating thru sec hdr tbl anyways, make a note of
+ * the exact section index, for later use.
+ */
int module_frob_arch_sections(Elf_Ehdr *hdr, Elf_Shdr *sechdrs,
char *secstr, struct module *mod)
{
+#ifdef CONFIG_ARC_DW2_UNWIND
+ int i;
+
+ mod->arch.unw_sec_idx = 0;
+ mod->arch.unw_info = NULL;
+
+ for (i = 1; i < hdr->e_shnum; i++) {
+ if (strcmp(secstr+sechdrs[i].sh_name, ".debug_frame") == 0) {
+ sechdrs[i].sh_flags |= SHF_ALLOC;
+ mod->arch.unw_sec_idx = i;
+ break;
+ }
+ }
+#endif
return 0;
}

void module_arch_cleanup(struct module *mod)
{
+#ifdef CONFIG_ARC_DW2_UNWIND
+ if (mod->arch.unw_info)
+ unwind_remove_table(mod->arch.unw_info, 0);
+#endif
}

int apply_relocate_add(Elf32_Shdr *sechdrs,
@@ -110,8 +137,23 @@ relo_err:

}

+/* Just before lift off: After sections have been relocated, we add the
+ * dwarf section to unwinder table pool
+ * This couldn't be done in module_frob_arch_sections() because
+ * relocations had not been applied by then
+ */
int module_finalize(const Elf32_Ehdr *hdr, const Elf_Shdr *sechdrs,
struct module *mod)
{
+#ifdef CONFIG_ARC_DW2_UNWIND
+ void *unw;
+ int unwsec = mod->arch.unw_sec_idx;
+
+ if (unwsec) {
+ unw = unwind_add_table(mod, (void *)sechdrs[unwsec].sh_addr,
+ sechdrs[unwsec].sh_size);
+ mod->arch.unw_info = unw;
+ }
+#endif
return 0;
}
diff --git a/arch/arc/kernel/setup.c b/arch/arc/kernel/setup.c
index 258b9ee..34a2ff5 100644
--- a/arch/arc/kernel/setup.c
+++ b/arch/arc/kernel/setup.c
@@ -20,6 +20,7 @@
#include <asm/page.h>
#include <asm/irq.h>
#include <asm/arcregs.h>
+#include <asm/unwind.h>

#define FIX_PTR(x) __asm__ __volatile__(";" : "+r"(x))

@@ -96,6 +97,8 @@ void __init setup_arch(char **cmdline_p)
conswitchp = &dummy_con;
#endif

+ arc_unwind_init();
+ arc_unwind_setup();
}

/*
diff --git a/arch/arc/kernel/unwind.c b/arch/arc/kernel/unwind.c
new file mode 100644
index 0000000..a8d0222
--- /dev/null
+++ b/arch/arc/kernel/unwind.c
@@ -0,0 +1,1329 @@
+/*
+ * Copyright (C) 2007-2010, 2011-2012 Synopsys, Inc. (http://www.synopsys.com)
+ * Copyright (C) 2002-2006 Novell, Inc.
+ * Jan Beulich <[email protected]>
+ *
+ * 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.
+ *
+ * A simple API for unwinding kernel stacks. This is used for
+ * debugging and error reporting purposes. The kernel doesn't need
+ * full-blown stack unwinding with all the bells and whistles, so there
+ * is not much point in implementing the full Dwarf2 unwind API.
+ */
+
+#include <linux/sched.h>
+#include <linux/module.h>
+#include <linux/bootmem.h>
+#include <linux/sort.h>
+#include <linux/slab.h>
+#include <linux/stop_machine.h>
+#include <linux/uaccess.h>
+#include <linux/ptrace.h>
+#include <asm/sections.h>
+#include <asm/unaligned.h>
+#include <asm/unwind.h>
+
+extern char __start_unwind[], __end_unwind[];
+/* extern const u8 __start_unwind_hdr[], __end_unwind_hdr[];*/
+
+/* #define UNWIND_DEBUG */
+
+#ifdef UNWIND_DEBUG
+int dbg_unw;
+#define unw_debug(fmt, ...) \
+do { \
+ if (dbg_unw) \
+ pr_info(fmt, ##__VA_ARGS__); \
+} while (0);
+#else
+#define unw_debug(fmt, ...)
+#endif
+
+#define MAX_STACK_DEPTH 8
+
+#define EXTRA_INFO(f) { \
+ BUILD_BUG_ON_ZERO(offsetof(struct unwind_frame_info, f) \
+ % FIELD_SIZEOF(struct unwind_frame_info, f)) \
+ + offsetof(struct unwind_frame_info, f) \
+ / FIELD_SIZEOF(struct unwind_frame_info, f), \
+ FIELD_SIZEOF(struct unwind_frame_info, f) \
+ }
+#define PTREGS_INFO(f) EXTRA_INFO(regs.f)
+
+static const struct {
+ unsigned offs:BITS_PER_LONG / 2;
+ unsigned width:BITS_PER_LONG / 2;
+} reg_info[] = {
+UNW_REGISTER_INFO};
+
+#undef PTREGS_INFO
+#undef EXTRA_INFO
+
+#ifndef REG_INVALID
+#define REG_INVALID(r) (reg_info[r].width == 0)
+#endif
+
+#define DW_CFA_nop 0x00
+#define DW_CFA_set_loc 0x01
+#define DW_CFA_advance_loc1 0x02
+#define DW_CFA_advance_loc2 0x03
+#define DW_CFA_advance_loc4 0x04
+#define DW_CFA_offset_extended 0x05
+#define DW_CFA_restore_extended 0x06
+#define DW_CFA_undefined 0x07
+#define DW_CFA_same_value 0x08
+#define DW_CFA_register 0x09
+#define DW_CFA_remember_state 0x0a
+#define DW_CFA_restore_state 0x0b
+#define DW_CFA_def_cfa 0x0c
+#define DW_CFA_def_cfa_register 0x0d
+#define DW_CFA_def_cfa_offset 0x0e
+#define DW_CFA_def_cfa_expression 0x0f
+#define DW_CFA_expression 0x10
+#define DW_CFA_offset_extended_sf 0x11
+#define DW_CFA_def_cfa_sf 0x12
+#define DW_CFA_def_cfa_offset_sf 0x13
+#define DW_CFA_val_offset 0x14
+#define DW_CFA_val_offset_sf 0x15
+#define DW_CFA_val_expression 0x16
+#define DW_CFA_lo_user 0x1c
+#define DW_CFA_GNU_window_save 0x2d
+#define DW_CFA_GNU_args_size 0x2e
+#define DW_CFA_GNU_negative_offset_extended 0x2f
+#define DW_CFA_hi_user 0x3f
+
+#define DW_EH_PE_FORM 0x07
+#define DW_EH_PE_native 0x00
+#define DW_EH_PE_leb128 0x01
+#define DW_EH_PE_data2 0x02
+#define DW_EH_PE_data4 0x03
+#define DW_EH_PE_data8 0x04
+#define DW_EH_PE_signed 0x08
+#define DW_EH_PE_ADJUST 0x70
+#define DW_EH_PE_abs 0x00
+#define DW_EH_PE_pcrel 0x10
+#define DW_EH_PE_textrel 0x20
+#define DW_EH_PE_datarel 0x30
+#define DW_EH_PE_funcrel 0x40
+#define DW_EH_PE_aligned 0x50
+#define DW_EH_PE_indirect 0x80
+#define DW_EH_PE_omit 0xff
+
+typedef unsigned long uleb128_t;
+typedef signed long sleb128_t;
+
+static struct unwind_table {
+ struct {
+ unsigned long pc;
+ unsigned long range;
+ } core, init;
+ const void *address;
+ unsigned long size;
+ const unsigned char *header;
+ unsigned long hdrsz;
+ struct unwind_table *link;
+ const char *name;
+} root_table;
+
+struct unwind_item {
+ enum item_location {
+ Nowhere,
+ Memory,
+ Register,
+ Value
+ } where;
+ uleb128_t value;
+};
+
+struct unwind_state {
+ uleb128_t loc, org;
+ const u8 *cieStart, *cieEnd;
+ uleb128_t codeAlign;
+ sleb128_t dataAlign;
+ struct cfa {
+ uleb128_t reg, offs;
+ } cfa;
+ struct unwind_item regs[ARRAY_SIZE(reg_info)];
+ unsigned stackDepth:8;
+ unsigned version:8;
+ const u8 *label;
+ const u8 *stack[MAX_STACK_DEPTH];
+};
+
+static const struct cfa badCFA = { ARRAY_SIZE(reg_info), 1 };
+
+static struct unwind_table *find_table(unsigned long pc)
+{
+ struct unwind_table *table;
+
+ for (table = &root_table; table; table = table->link)
+ if ((pc >= table->core.pc
+ && pc < table->core.pc + table->core.range)
+ || (pc >= table->init.pc
+ && pc < table->init.pc + table->init.range))
+ break;
+
+ return table;
+}
+
+static unsigned long read_pointer(const u8 **pLoc,
+ const void *end, signed ptrType);
+
+static void init_unwind_table(struct unwind_table *table, const char *name,
+ const void *core_start, unsigned long core_size,
+ const void *init_start, unsigned long init_size,
+ const void *table_start, unsigned long table_size,
+ const u8 *header_start, unsigned long header_size)
+{
+ const u8 *ptr = header_start + 4;
+ const u8 *end = header_start + header_size;
+
+ table->core.pc = (unsigned long)core_start;
+ table->core.range = core_size;
+ table->init.pc = (unsigned long)init_start;
+ table->init.range = init_size;
+ table->address = table_start;
+ table->size = table_size;
+
+ /* See if the linker provided table looks valid. */
+ if (header_size <= 4
+ || header_start[0] != 1
+ || (void *)read_pointer(&ptr, end, header_start[1]) != table_start
+ || header_start[2] == DW_EH_PE_omit
+ || read_pointer(&ptr, end, header_start[2]) <= 0
+ || header_start[3] == DW_EH_PE_omit)
+ header_start = NULL;
+
+ table->hdrsz = header_size;
+ smp_wmb();
+ table->header = header_start;
+ table->link = NULL;
+ table->name = name;
+}
+
+void __init arc_unwind_init(void)
+{
+ init_unwind_table(&root_table, "kernel", _text, _end - _text, NULL, 0,
+ __start_unwind, __end_unwind - __start_unwind,
+ NULL, 0);
+ /*__start_unwind_hdr, __end_unwind_hdr - __start_unwind_hdr);*/
+}
+
+static const u32 bad_cie, not_fde;
+static const u32 *cie_for_fde(const u32 *fde, const struct unwind_table *);
+static signed fde_pointer_type(const u32 *cie);
+
+struct eh_frame_hdr_table_entry {
+ unsigned long start, fde;
+};
+
+static int cmp_eh_frame_hdr_table_entries(const void *p1, const void *p2)
+{
+ const struct eh_frame_hdr_table_entry *e1 = p1;
+ const struct eh_frame_hdr_table_entry *e2 = p2;
+
+ return (e1->start > e2->start) - (e1->start < e2->start);
+}
+
+static void swap_eh_frame_hdr_table_entries(void *p1, void *p2, int size)
+{
+ struct eh_frame_hdr_table_entry *e1 = p1;
+ struct eh_frame_hdr_table_entry *e2 = p2;
+ unsigned long v;
+
+ v = e1->start;
+ e1->start = e2->start;
+ e2->start = v;
+ v = e1->fde;
+ e1->fde = e2->fde;
+ e2->fde = v;
+}
+
+static void __init setup_unwind_table(struct unwind_table *table,
+ void *(*alloc) (unsigned long))
+{
+ const u8 *ptr;
+ unsigned long tableSize = table->size, hdrSize;
+ unsigned n;
+ const u32 *fde;
+ struct {
+ u8 version;
+ u8 eh_frame_ptr_enc;
+ u8 fde_count_enc;
+ u8 table_enc;
+ unsigned long eh_frame_ptr;
+ unsigned int fde_count;
+ struct eh_frame_hdr_table_entry table[];
+ } __attribute__ ((__packed__)) *header;
+
+ if (table->header)
+ return;
+
+ if (table->hdrsz)
+ pr_warn(".eh_frame_hdr for '%s' present but unusable\n",
+ table->name);
+
+ if (tableSize & (sizeof(*fde) - 1))
+ return;
+
+ for (fde = table->address, n = 0;
+ tableSize > sizeof(*fde) && tableSize - sizeof(*fde) >= *fde;
+ tableSize -= sizeof(*fde) + *fde, fde += 1 + *fde / sizeof(*fde)) {
+ const u32 *cie = cie_for_fde(fde, table);
+ signed ptrType;
+
+ if (cie == &not_fde)
+ continue;
+ if (cie == NULL || cie == &bad_cie)
+ return;
+ ptrType = fde_pointer_type(cie);
+ if (ptrType < 0)
+ return;
+
+ ptr = (const u8 *)(fde + 2);
+ if (!read_pointer(&ptr, (const u8 *)(fde + 1) + *fde,
+ ptrType)) {
+ /* FIXME_Rajesh We have 4 instances of null addresses
+ * instead of the initial loc addr
+ * return;
+ */
+ }
+ ++n;
+ }
+
+ if (tableSize || !n)
+ return;
+
+ hdrSize = 4 + sizeof(unsigned long) + sizeof(unsigned int)
+ + 2 * n * sizeof(unsigned long);
+ header = alloc(hdrSize);
+ if (!header)
+ return;
+ header->version = 1;
+ header->eh_frame_ptr_enc = DW_EH_PE_abs | DW_EH_PE_native;
+ header->fde_count_enc = DW_EH_PE_abs | DW_EH_PE_data4;
+ header->table_enc = DW_EH_PE_abs | DW_EH_PE_native;
+ put_unaligned((unsigned long)table->address, &header->eh_frame_ptr);
+ BUILD_BUG_ON(offsetof(typeof(*header), fde_count)
+ % __alignof(typeof(header->fde_count)));
+ header->fde_count = n;
+
+ BUILD_BUG_ON(offsetof(typeof(*header), table)
+ % __alignof(typeof(*header->table)));
+ for (fde = table->address, tableSize = table->size, n = 0;
+ tableSize;
+ tableSize -= sizeof(*fde) + *fde, fde += 1 + *fde / sizeof(*fde)) {
+ /* const u32 *cie = fde + 1 - fde[1] / sizeof(*fde); */
+ const u32 *cie = (const u32 *)(fde[1]);
+
+ if (fde[1] == 0xffffffff)
+ continue; /* this is a CIE */
+ ptr = (const u8 *)(fde + 2);
+ header->table[n].start = read_pointer(&ptr,
+ (const u8 *)(fde + 1) +
+ *fde,
+ fde_pointer_type(cie));
+ header->table[n].fde = (unsigned long)fde;
+ ++n;
+ }
+ WARN_ON(n != header->fde_count);
+
+ sort(header->table,
+ n,
+ sizeof(*header->table),
+ cmp_eh_frame_hdr_table_entries, swap_eh_frame_hdr_table_entries);
+
+ table->hdrsz = hdrSize;
+ smp_wmb();
+ table->header = (const void *)header;
+}
+
+static void *__init balloc(unsigned long sz)
+{
+ return __alloc_bootmem_nopanic(sz,
+ sizeof(unsigned int),
+ __pa(MAX_DMA_ADDRESS));
+}
+
+void __init arc_unwind_setup(void)
+{
+ setup_unwind_table(&root_table, balloc);
+}
+
+#ifdef CONFIG_MODULES
+
+static struct unwind_table *last_table;
+
+/* Must be called with module_mutex held. */
+void *unwind_add_table(struct module *module, const void *table_start,
+ unsigned long table_size)
+{
+ struct unwind_table *table;
+
+ if (table_size <= 0)
+ return NULL;
+
+ table = kmalloc(sizeof(*table), GFP_KERNEL);
+ if (!table)
+ return NULL;
+
+ init_unwind_table(table, module->name,
+ module->module_core, module->core_size,
+ module->module_init, module->init_size,
+ table_start, table_size,
+ NULL, 0);
+
+#ifdef UNWIND_DEBUG
+ unw_debug("Table added for [%s] %lx %lx\n",
+ module->name, table->core.pc, table->core.range);
+#endif
+ if (last_table)
+ last_table->link = table;
+ else
+ root_table.link = table;
+ last_table = table;
+
+ return table;
+}
+
+struct unlink_table_info {
+ struct unwind_table *table;
+ int init_only;
+};
+
+static int unlink_table(void *arg)
+{
+ struct unlink_table_info *info = arg;
+ struct unwind_table *table = info->table, *prev;
+
+ for (prev = &root_table; prev->link && prev->link != table;
+ prev = prev->link)
+ ;
+
+ if (prev->link) {
+ if (info->init_only) {
+ table->init.pc = 0;
+ table->init.range = 0;
+ info->table = NULL;
+ } else {
+ prev->link = table->link;
+ if (!prev->link)
+ last_table = prev;
+ }
+ } else
+ info->table = NULL;
+
+ return 0;
+}
+
+/* Must be called with module_mutex held. */
+void unwind_remove_table(void *handle, int init_only)
+{
+ struct unwind_table *table = handle;
+ struct unlink_table_info info;
+
+ if (!table || table == &root_table)
+ return;
+
+ if (init_only && table == last_table) {
+ table->init.pc = 0;
+ table->init.range = 0;
+ return;
+ }
+
+ info.table = table;
+ info.init_only = init_only;
+
+ unlink_table(&info); /* XXX: SMP */
+ kfree(table);
+}
+
+#endif /* CONFIG_MODULES */
+
+static uleb128_t get_uleb128(const u8 **pcur, const u8 *end)
+{
+ const u8 *cur = *pcur;
+ uleb128_t value;
+ unsigned shift;
+
+ for (shift = 0, value = 0; cur < end; shift += 7) {
+ if (shift + 7 > 8 * sizeof(value)
+ && (*cur & 0x7fU) >= (1U << (8 * sizeof(value) - shift))) {
+ cur = end + 1;
+ break;
+ }
+ value |= (uleb128_t) (*cur & 0x7f) << shift;
+ if (!(*cur++ & 0x80))
+ break;
+ }
+ *pcur = cur;
+
+ return value;
+}
+
+static sleb128_t get_sleb128(const u8 **pcur, const u8 *end)
+{
+ const u8 *cur = *pcur;
+ sleb128_t value;
+ unsigned shift;
+
+ for (shift = 0, value = 0; cur < end; shift += 7) {
+ if (shift + 7 > 8 * sizeof(value)
+ && (*cur & 0x7fU) >= (1U << (8 * sizeof(value) - shift))) {
+ cur = end + 1;
+ break;
+ }
+ value |= (sleb128_t) (*cur & 0x7f) << shift;
+ if (!(*cur & 0x80)) {
+ value |= -(*cur++ & 0x40) << shift;
+ break;
+ }
+ }
+ *pcur = cur;
+
+ return value;
+}
+
+static const u32 *cie_for_fde(const u32 *fde, const struct unwind_table *table)
+{
+ const u32 *cie;
+
+ if (!*fde || (*fde & (sizeof(*fde) - 1)))
+ return &bad_cie;
+
+ if (fde[1] == 0xffffffff)
+ return &not_fde; /* this is a CIE */
+
+ if ((fde[1] & (sizeof(*fde) - 1)))
+/* || fde[1] > (unsigned long)(fde + 1) - (unsigned long)table->address) */
+ return NULL; /* this is not a valid FDE */
+
+ /* cie = fde + 1 - fde[1] / sizeof(*fde); */
+ cie = (u32 *) fde[1];
+
+ if (*cie <= sizeof(*cie) + 4 || *cie >= fde[1] - sizeof(*fde)
+ || (*cie & (sizeof(*cie) - 1))
+ || (cie[1] != 0xffffffff))
+ return NULL; /* this is not a (valid) CIE */
+ return cie;
+}
+
+static unsigned long read_pointer(const u8 **pLoc, const void *end,
+ signed ptrType)
+{
+ unsigned long value = 0;
+ union {
+ const u8 *p8;
+ const u16 *p16u;
+ const s16 *p16s;
+ const u32 *p32u;
+ const s32 *p32s;
+ const unsigned long *pul;
+ } ptr;
+
+ if (ptrType < 0 || ptrType == DW_EH_PE_omit)
+ return 0;
+ ptr.p8 = *pLoc;
+ switch (ptrType & DW_EH_PE_FORM) {
+ case DW_EH_PE_data2:
+ if (end < (const void *)(ptr.p16u + 1))
+ return 0;
+ if (ptrType & DW_EH_PE_signed)
+ value = get_unaligned((u16 *) ptr.p16s++);
+ else
+ value = get_unaligned((u16 *) ptr.p16u++);
+ break;
+ case DW_EH_PE_data4:
+#ifdef CONFIG_64BIT
+ if (end < (const void *)(ptr.p32u + 1))
+ return 0;
+ if (ptrType & DW_EH_PE_signed)
+ value = get_unaligned(ptr.p32s++);
+ else
+ value = get_unaligned(ptr.p32u++);
+ break;
+ case DW_EH_PE_data8:
+ BUILD_BUG_ON(sizeof(u64) != sizeof(value));
+#else
+ BUILD_BUG_ON(sizeof(u32) != sizeof(value));
+#endif
+ case DW_EH_PE_native:
+ if (end < (const void *)(ptr.pul + 1))
+ return 0;
+ value = get_unaligned((unsigned long *)ptr.pul++);
+ break;
+ case DW_EH_PE_leb128:
+ BUILD_BUG_ON(sizeof(uleb128_t) > sizeof(value));
+ value = ptrType & DW_EH_PE_signed ? get_sleb128(&ptr.p8, end)
+ : get_uleb128(&ptr.p8, end);
+ if ((const void *)ptr.p8 > end)
+ return 0;
+ break;
+ default:
+ return 0;
+ }
+ switch (ptrType & DW_EH_PE_ADJUST) {
+ case DW_EH_PE_abs:
+ break;
+ case DW_EH_PE_pcrel:
+ value += (unsigned long)*pLoc;
+ break;
+ default:
+ return 0;
+ }
+ if ((ptrType & DW_EH_PE_indirect)
+ && __get_user(value, (unsigned long __user *)value))
+ return 0;
+ *pLoc = ptr.p8;
+
+ return value;
+}
+
+static signed fde_pointer_type(const u32 *cie)
+{
+ const u8 *ptr = (const u8 *)(cie + 2);
+ unsigned version = *ptr;
+
+ if (version != 1)
+ return -1; /* unsupported */
+
+ if (*++ptr) {
+ const char *aug;
+ const u8 *end = (const u8 *)(cie + 1) + *cie;
+ uleb128_t len;
+
+ /* check if augmentation size is first (and thus present) */
+ if (*ptr != 'z')
+ return -1;
+
+ /* check if augmentation string is nul-terminated */
+ aug = (const void *)ptr;
+ ptr = memchr(aug, 0, end - ptr);
+ if (ptr == NULL)
+ return -1;
+
+ ++ptr; /* skip terminator */
+ get_uleb128(&ptr, end); /* skip code alignment */
+ get_sleb128(&ptr, end); /* skip data alignment */
+ /* skip return address column */
+ version <= 1 ? (void) ++ptr : (void)get_uleb128(&ptr, end);
+ len = get_uleb128(&ptr, end); /* augmentation length */
+
+ if (ptr + len < ptr || ptr + len > end)
+ return -1;
+
+ end = ptr + len;
+ while (*++aug) {
+ if (ptr >= end)
+ return -1;
+ switch (*aug) {
+ case 'L':
+ ++ptr;
+ break;
+ case 'P':{
+ signed ptrType = *ptr++;
+
+ if (!read_pointer(&ptr, end, ptrType)
+ || ptr > end)
+ return -1;
+ }
+ break;
+ case 'R':
+ return *ptr;
+ default:
+ return -1;
+ }
+ }
+ }
+ return DW_EH_PE_native | DW_EH_PE_abs;
+}
+
+static int advance_loc(unsigned long delta, struct unwind_state *state)
+{
+ state->loc += delta * state->codeAlign;
+
+ /* FIXME_Rajesh: Probably we are defining for the initial range as well;
+ return delta > 0;
+ */
+ unw_debug("delta %3lu => loc 0x%lx: ", delta, state->loc);
+ return 1;
+}
+
+static void set_rule(uleb128_t reg, enum item_location where, uleb128_t value,
+ struct unwind_state *state)
+{
+ if (reg < ARRAY_SIZE(state->regs)) {
+ state->regs[reg].where = where;
+ state->regs[reg].value = value;
+
+#ifdef UNWIND_DEBUG
+ unw_debug("r%lu: ", reg);
+ switch (where) {
+ case Nowhere:
+ unw_debug("s ");
+ break;
+ case Memory:
+ unw_debug("c(%lu) ", value);
+ break;
+ case Register:
+ unw_debug("r(%lu) ", value);
+ break;
+ case Value:
+ unw_debug("v(%lu) ", value);
+ break;
+ default:
+ break;
+ }
+#endif
+ }
+}
+
+static int processCFI(const u8 *start, const u8 *end, unsigned long targetLoc,
+ signed ptrType, struct unwind_state *state)
+{
+ union {
+ const u8 *p8;
+ const u16 *p16;
+ const u32 *p32;
+ } ptr;
+ int result = 1;
+ u8 opcode;
+
+ if (start != state->cieStart) {
+ state->loc = state->org;
+ result =
+ processCFI(state->cieStart, state->cieEnd, 0, ptrType,
+ state);
+ if (targetLoc == 0 && state->label == NULL)
+ return result;
+ }
+ for (ptr.p8 = start; result && ptr.p8 < end;) {
+ switch (*ptr.p8 >> 6) {
+ uleb128_t value;
+
+ case 0:
+ opcode = *ptr.p8++;
+
+ switch (opcode) {
+ case DW_CFA_nop:
+ unw_debug("cfa nop ");
+ break;
+ case DW_CFA_set_loc:
+ state->loc = read_pointer(&ptr.p8, end,
+ ptrType);
+ if (state->loc == 0)
+ result = 0;
+ unw_debug("cfa_set_loc: 0x%lx ", state->loc);
+ break;
+ case DW_CFA_advance_loc1:
+ unw_debug("\ncfa advance loc1:");
+ result = ptr.p8 < end
+ && advance_loc(*ptr.p8++, state);
+ break;
+ case DW_CFA_advance_loc2:
+ value = *ptr.p8++;
+ value += *ptr.p8++ << 8;
+ unw_debug("\ncfa advance loc2:");
+ result = ptr.p8 <= end + 2
+ /* && advance_loc(*ptr.p16++, state); */
+ && advance_loc(value, state);
+ break;
+ case DW_CFA_advance_loc4:
+ unw_debug("\ncfa advance loc4:");
+ result = ptr.p8 <= end + 4
+ && advance_loc(*ptr.p32++, state);
+ break;
+ case DW_CFA_offset_extended:
+ value = get_uleb128(&ptr.p8, end);
+ unw_debug("cfa_offset_extended: ");
+ set_rule(value, Memory,
+ get_uleb128(&ptr.p8, end), state);
+ break;
+ case DW_CFA_val_offset:
+ value = get_uleb128(&ptr.p8, end);
+ set_rule(value, Value,
+ get_uleb128(&ptr.p8, end), state);
+ break;
+ case DW_CFA_offset_extended_sf:
+ value = get_uleb128(&ptr.p8, end);
+ set_rule(value, Memory,
+ get_sleb128(&ptr.p8, end), state);
+ break;
+ case DW_CFA_val_offset_sf:
+ value = get_uleb128(&ptr.p8, end);
+ set_rule(value, Value,
+ get_sleb128(&ptr.p8, end), state);
+ break;
+ case DW_CFA_restore_extended:
+ unw_debug("cfa_restore_extended: ");
+ case DW_CFA_undefined:
+ unw_debug("cfa_undefined: ");
+ case DW_CFA_same_value:
+ unw_debug("cfa_same_value: ");
+ set_rule(get_uleb128(&ptr.p8, end), Nowhere, 0,
+ state);
+ break;
+ case DW_CFA_register:
+ unw_debug("cfa_register: ");
+ value = get_uleb128(&ptr.p8, end);
+ set_rule(value,
+ Register,
+ get_uleb128(&ptr.p8, end), state);
+ break;
+ case DW_CFA_remember_state:
+ unw_debug("cfa_remember_state: ");
+ if (ptr.p8 == state->label) {
+ state->label = NULL;
+ return 1;
+ }
+ if (state->stackDepth >= MAX_STACK_DEPTH)
+ return 0;
+ state->stack[state->stackDepth++] = ptr.p8;
+ break;
+ case DW_CFA_restore_state:
+ unw_debug("cfa_restore_state: ");
+ if (state->stackDepth) {
+ const uleb128_t loc = state->loc;
+ const u8 *label = state->label;
+
+ state->label =
+ state->stack[state->stackDepth - 1];
+ memcpy(&state->cfa, &badCFA,
+ sizeof(state->cfa));
+ memset(state->regs, 0,
+ sizeof(state->regs));
+ state->stackDepth = 0;
+ result =
+ processCFI(start, end, 0, ptrType,
+ state);
+ state->loc = loc;
+ state->label = label;
+ } else
+ return 0;
+ break;
+ case DW_CFA_def_cfa:
+ state->cfa.reg = get_uleb128(&ptr.p8, end);
+ unw_debug("cfa_def_cfa: r%lu ", state->cfa.reg);
+ /*nobreak*/
+ case DW_CFA_def_cfa_offset:
+ state->cfa.offs = get_uleb128(&ptr.p8, end);
+ unw_debug("cfa_def_cfa_offset: 0x%lx ",
+ state->cfa.offs);
+ break;
+ case DW_CFA_def_cfa_sf:
+ state->cfa.reg = get_uleb128(&ptr.p8, end);
+ /*nobreak */
+ case DW_CFA_def_cfa_offset_sf:
+ state->cfa.offs = get_sleb128(&ptr.p8, end)
+ * state->dataAlign;
+ break;
+ case DW_CFA_def_cfa_register:
+ unw_debug("cfa_def_cfa_regsiter: ");
+ state->cfa.reg = get_uleb128(&ptr.p8, end);
+ break;
+ /*todo case DW_CFA_def_cfa_expression: */
+ /*todo case DW_CFA_expression: */
+ /*todo case DW_CFA_val_expression: */
+ case DW_CFA_GNU_args_size:
+ get_uleb128(&ptr.p8, end);
+ break;
+ case DW_CFA_GNU_negative_offset_extended:
+ value = get_uleb128(&ptr.p8, end);
+ set_rule(value,
+ Memory,
+ (uleb128_t) 0 - get_uleb128(&ptr.p8,
+ end),
+ state);
+ break;
+ case DW_CFA_GNU_window_save:
+ default:
+ unw_debug("UNKNOW OPCODE 0x%x\n", opcode);
+ result = 0;
+ break;
+ }
+ break;
+ case 1:
+ unw_debug("\ncfa_adv_loc: ");
+ result = advance_loc(*ptr.p8++ & 0x3f, state);
+ break;
+ case 2:
+ unw_debug("cfa_offset: ");
+ value = *ptr.p8++ & 0x3f;
+ set_rule(value, Memory, get_uleb128(&ptr.p8, end),
+ state);
+ break;
+ case 3:
+ unw_debug("cfa_restore: ");
+ set_rule(*ptr.p8++ & 0x3f, Nowhere, 0, state);
+ break;
+ }
+
+ if (ptr.p8 > end)
+ result = 0;
+ if (result && targetLoc != 0 && targetLoc < state->loc)
+ return 1;
+ }
+
+ return result && ptr.p8 == end && (targetLoc == 0 || (
+ /*todo While in theory this should apply, gcc in practice omits
+ everything past the function prolog, and hence the location
+ never reaches the end of the function.
+ targetLoc < state->loc && */ state->label == NULL));
+}
+
+/* Unwind to previous to frame. Returns 0 if successful, negative
+ * number in case of an error. */
+int arc_unwind(struct unwind_frame_info *frame)
+{
+#define FRAME_REG(r, t) (((t *)frame)[reg_info[r].offs])
+ const u32 *fde = NULL, *cie = NULL;
+ const u8 *ptr = NULL, *end = NULL;
+ unsigned long pc = UNW_PC(frame) - frame->call_frame;
+ unsigned long startLoc = 0, endLoc = 0, cfa;
+ unsigned i;
+ signed ptrType = -1;
+ uleb128_t retAddrReg = 0;
+ const struct unwind_table *table;
+ struct unwind_state state;
+ unsigned long *fptr;
+ unsigned long addr;
+
+ unw_debug("\n\nUNWIND FRAME:\n");
+ unw_debug("PC: 0x%lx BLINK: 0x%lx, SP: 0x%lx, FP: 0x%x\n",
+ UNW_PC(frame), UNW_BLINK(frame), UNW_SP(frame),
+ UNW_FP(frame));
+
+ if (UNW_PC(frame) == 0)
+ return -EINVAL;
+
+#ifdef UNWIND_DEBUG
+ {
+ unsigned long *sptr = (unsigned long *)UNW_SP(frame);
+ unw_debug("\nStack Dump:\n");
+ for (i = 0; i < 20; i++, sptr++)
+ unw_debug("0x%p: 0x%lx\n", sptr, *sptr);
+ unw_debug("\n");
+ }
+#endif
+
+ table = find_table(pc);
+ if (table != NULL
+ && !(table->size & (sizeof(*fde) - 1))) {
+ const u8 *hdr = table->header;
+ unsigned long tableSize;
+
+ smp_rmb();
+ if (hdr && hdr[0] == 1) {
+ switch (hdr[3] & DW_EH_PE_FORM) {
+ case DW_EH_PE_native:
+ tableSize = sizeof(unsigned long);
+ break;
+ case DW_EH_PE_data2:
+ tableSize = 2;
+ break;
+ case DW_EH_PE_data4:
+ tableSize = 4;
+ break;
+ case DW_EH_PE_data8:
+ tableSize = 8;
+ break;
+ default:
+ tableSize = 0;
+ break;
+ }
+ ptr = hdr + 4;
+ end = hdr + table->hdrsz;
+ if (tableSize && read_pointer(&ptr, end, hdr[1])
+ == (unsigned long)table->address
+ && (i = read_pointer(&ptr, end, hdr[2])) > 0
+ && i == (end - ptr) / (2 * tableSize)
+ && !((end - ptr) % (2 * tableSize))) {
+ do {
+ const u8 *cur =
+ ptr + (i / 2) * (2 * tableSize);
+
+ startLoc = read_pointer(&cur,
+ cur + tableSize,
+ hdr[3]);
+ if (pc < startLoc)
+ i /= 2;
+ else {
+ ptr = cur - tableSize;
+ i = (i + 1) / 2;
+ }
+ } while (startLoc && i > 1);
+ if (i == 1
+ && (startLoc = read_pointer(&ptr,
+ ptr + tableSize,
+ hdr[3])) != 0
+ && pc >= startLoc)
+ fde = (void *)read_pointer(&ptr,
+ ptr +
+ tableSize,
+ hdr[3]);
+ }
+ }
+
+ if (fde != NULL) {
+ cie = cie_for_fde(fde, table);
+ ptr = (const u8 *)(fde + 2);
+ if (cie != NULL
+ && cie != &bad_cie
+ && cie != &not_fde
+ && (ptrType = fde_pointer_type(cie)) >= 0
+ && read_pointer(&ptr,
+ (const u8 *)(fde + 1) + *fde,
+ ptrType) == startLoc) {
+ if (!(ptrType & DW_EH_PE_indirect))
+ ptrType &=
+ DW_EH_PE_FORM | DW_EH_PE_signed;
+ endLoc =
+ startLoc + read_pointer(&ptr,
+ (const u8 *)(fde +
+ 1) +
+ *fde, ptrType);
+ if (pc >= endLoc)
+ fde = NULL;
+ } else
+ fde = NULL;
+ }
+ if (fde == NULL) {
+ for (fde = table->address, tableSize = table->size;
+ cie = NULL, tableSize > sizeof(*fde)
+ && tableSize - sizeof(*fde) >= *fde;
+ tableSize -= sizeof(*fde) + *fde,
+ fde += 1 + *fde / sizeof(*fde)) {
+ cie = cie_for_fde(fde, table);
+ if (cie == &bad_cie) {
+ cie = NULL;
+ break;
+ }
+ if (cie == NULL
+ || cie == &not_fde
+ || (ptrType = fde_pointer_type(cie)) < 0)
+ continue;
+ ptr = (const u8 *)(fde + 2);
+ startLoc = read_pointer(&ptr,
+ (const u8 *)(fde + 1) +
+ *fde, ptrType);
+ if (!startLoc)
+ continue;
+ if (!(ptrType & DW_EH_PE_indirect))
+ ptrType &=
+ DW_EH_PE_FORM | DW_EH_PE_signed;
+ endLoc =
+ startLoc + read_pointer(&ptr,
+ (const u8 *)(fde +
+ 1) +
+ *fde, ptrType);
+ if (pc >= startLoc && pc < endLoc)
+ break;
+ }
+ }
+ }
+ if (cie != NULL) {
+ memset(&state, 0, sizeof(state));
+ state.cieEnd = ptr; /* keep here temporarily */
+ ptr = (const u8 *)(cie + 2);
+ end = (const u8 *)(cie + 1) + *cie;
+ frame->call_frame = 1;
+ if ((state.version = *ptr) != 1)
+ cie = NULL; /* unsupported version */
+ else if (*++ptr) {
+ /* check if augmentation size is first (thus present) */
+ if (*ptr == 'z') {
+ while (++ptr < end && *ptr) {
+ switch (*ptr) {
+ /* chk for ignorable or already handled
+ * nul-terminated augmentation string */
+ case 'L':
+ case 'P':
+ case 'R':
+ continue;
+ case 'S':
+ frame->call_frame = 0;
+ continue;
+ default:
+ break;
+ }
+ break;
+ }
+ }
+ if (ptr >= end || *ptr)
+ cie = NULL;
+ }
+ ++ptr;
+ }
+ if (cie != NULL) {
+ /* get code aligment factor */
+ state.codeAlign = get_uleb128(&ptr, end);
+ /* get data aligment factor */
+ state.dataAlign = get_sleb128(&ptr, end);
+ if (state.codeAlign == 0 || state.dataAlign == 0 || ptr >= end)
+ cie = NULL;
+ else {
+ retAddrReg =
+ state.version <= 1 ? *ptr++ : get_uleb128(&ptr,
+ end);
+ unw_debug("CIE Frame Info:\n");
+ unw_debug("return Address register 0x%lx\n",
+ retAddrReg);
+ unw_debug("data Align: %ld\n", state.dataAlign);
+ unw_debug("code Align: %lu\n", state.codeAlign);
+ /* skip augmentation */
+ if (((const char *)(cie + 2))[1] == 'z') {
+ uleb128_t augSize = get_uleb128(&ptr, end);
+
+ ptr += augSize;
+ }
+ if (ptr > end || retAddrReg >= ARRAY_SIZE(reg_info)
+ || REG_INVALID(retAddrReg)
+ || reg_info[retAddrReg].width !=
+ sizeof(unsigned long))
+ cie = NULL;
+ }
+ }
+ if (cie != NULL) {
+ state.cieStart = ptr;
+ ptr = state.cieEnd;
+ state.cieEnd = end;
+ end = (const u8 *)(fde + 1) + *fde;
+ /* skip augmentation */
+ if (((const char *)(cie + 2))[1] == 'z') {
+ uleb128_t augSize = get_uleb128(&ptr, end);
+
+ if ((ptr += augSize) > end)
+ fde = NULL;
+ }
+ }
+ if (cie == NULL || fde == NULL) {
+#ifdef CONFIG_FRAME_POINTER
+ unsigned long top, bottom;
+
+ top = STACK_TOP_UNW(frame->task);
+ bottom = STACK_BOTTOM_UNW(frame->task);
+#if FRAME_RETADDR_OFFSET < 0
+ if (UNW_SP(frame) < top && UNW_FP(frame) <= UNW_SP(frame)
+ && bottom < UNW_FP(frame)
+#else
+ if (UNW_SP(frame) > top && UNW_FP(frame) >= UNW_SP(frame)
+ && bottom > UNW_FP(frame)
+#endif
+ && !((UNW_SP(frame) | UNW_FP(frame))
+ & (sizeof(unsigned long) - 1))) {
+ unsigned long link;
+
+ if (!__get_user(link, (unsigned long *)
+ (UNW_FP(frame) + FRAME_LINK_OFFSET))
+#if FRAME_RETADDR_OFFSET < 0
+ && link > bottom && link < UNW_FP(frame)
+#else
+ && link > UNW_FP(frame) && link < bottom
+#endif
+ && !(link & (sizeof(link) - 1))
+ && !__get_user(UNW_PC(frame),
+ (unsigned long *)(UNW_FP(frame)
+ + FRAME_RETADDR_OFFSET)))
+ {
+ UNW_SP(frame) =
+ UNW_FP(frame) + FRAME_RETADDR_OFFSET
+#if FRAME_RETADDR_OFFSET < 0
+ -
+#else
+ +
+#endif
+ sizeof(UNW_PC(frame));
+ UNW_FP(frame) = link;
+ return 0;
+ }
+ }
+#endif
+ return -ENXIO;
+ }
+ state.org = startLoc;
+ memcpy(&state.cfa, &badCFA, sizeof(state.cfa));
+
+ unw_debug("\nProcess instructions\n");
+
+ /* process instructions
+ * For ARC, we optimize by having blink(retAddrReg) with
+ * the sameValue in the leaf function, so we should not check
+ * state.regs[retAddrReg].where == Nowhere
+ */
+ if (!processCFI(ptr, end, pc, ptrType, &state)
+ || state.loc > endLoc
+/* || state.regs[retAddrReg].where == Nowhere */
+ || state.cfa.reg >= ARRAY_SIZE(reg_info)
+ || reg_info[state.cfa.reg].width != sizeof(unsigned long)
+ || state.cfa.offs % sizeof(unsigned long))
+ return -EIO;
+
+#ifdef UNWIND_DEBUG
+ unw_debug("\n");
+
+ unw_debug("\nRegister State Based on the rules parsed from FDE:\n");
+ for (i = 0; i < ARRAY_SIZE(state.regs); ++i) {
+
+ if (REG_INVALID(i))
+ continue;
+
+ switch (state.regs[i].where) {
+ case Nowhere:
+ break;
+ case Memory:
+ unw_debug(" r%d: c(%lu),", i, state.regs[i].value);
+ break;
+ case Register:
+ unw_debug(" r%d: r(%lu),", i, state.regs[i].value);
+ break;
+ case Value:
+ unw_debug(" r%d: v(%lu),", i, state.regs[i].value);
+ break;
+ }
+ }
+
+ unw_debug("\n");
+#endif
+
+ /* update frame */
+#ifndef CONFIG_AS_CFI_SIGNAL_FRAME
+ if (frame->call_frame
+ && !UNW_DEFAULT_RA(state.regs[retAddrReg], state.dataAlign))
+ frame->call_frame = 0;
+#endif
+ cfa = FRAME_REG(state.cfa.reg, unsigned long) + state.cfa.offs;
+ startLoc = min_t(unsigned long, UNW_SP(frame), cfa);
+ endLoc = max_t(unsigned long, UNW_SP(frame), cfa);
+ if (STACK_LIMIT(startLoc) != STACK_LIMIT(endLoc)) {
+ startLoc = min(STACK_LIMIT(cfa), cfa);
+ endLoc = max(STACK_LIMIT(cfa), cfa);
+ }
+
+ unw_debug("\nCFA reg: 0x%lx, offset: 0x%lx => 0x%lx\n",
+ state.cfa.reg, state.cfa.offs, cfa);
+
+ for (i = 0; i < ARRAY_SIZE(state.regs); ++i) {
+ if (REG_INVALID(i)) {
+ if (state.regs[i].where == Nowhere)
+ continue;
+ return -EIO;
+ }
+ switch (state.regs[i].where) {
+ default:
+ break;
+ case Register:
+ if (state.regs[i].value >= ARRAY_SIZE(reg_info)
+ || REG_INVALID(state.regs[i].value)
+ || reg_info[i].width >
+ reg_info[state.regs[i].value].width)
+ return -EIO;
+ switch (reg_info[state.regs[i].value].width) {
+ case sizeof(u8):
+ state.regs[i].value =
+ FRAME_REG(state.regs[i].value, const u8);
+ break;
+ case sizeof(u16):
+ state.regs[i].value =
+ FRAME_REG(state.regs[i].value, const u16);
+ break;
+ case sizeof(u32):
+ state.regs[i].value =
+ FRAME_REG(state.regs[i].value, const u32);
+ break;
+#ifdef CONFIG_64BIT
+ case sizeof(u64):
+ state.regs[i].value =
+ FRAME_REG(state.regs[i].value, const u64);
+ break;
+#endif
+ default:
+ return -EIO;
+ }
+ break;
+ }
+ }
+
+ unw_debug("\nRegister state after evaluation with realtime Stack:\n");
+ fptr = (unsigned long *)(&frame->regs);
+ for (i = 0; i < ARRAY_SIZE(state.regs); ++i, fptr++) {
+
+ if (REG_INVALID(i))
+ continue;
+ switch (state.regs[i].where) {
+ case Nowhere:
+ if (reg_info[i].width != sizeof(UNW_SP(frame))
+ || &FRAME_REG(i, __typeof__(UNW_SP(frame)))
+ != &UNW_SP(frame))
+ continue;
+ UNW_SP(frame) = cfa;
+ break;
+ case Register:
+ switch (reg_info[i].width) {
+ case sizeof(u8):
+ FRAME_REG(i, u8) = state.regs[i].value;
+ break;
+ case sizeof(u16):
+ FRAME_REG(i, u16) = state.regs[i].value;
+ break;
+ case sizeof(u32):
+ FRAME_REG(i, u32) = state.regs[i].value;
+ break;
+#ifdef CONFIG_64BIT
+ case sizeof(u64):
+ FRAME_REG(i, u64) = state.regs[i].value;
+ break;
+#endif
+ default:
+ return -EIO;
+ }
+ break;
+ case Value:
+ if (reg_info[i].width != sizeof(unsigned long))
+ return -EIO;
+ FRAME_REG(i, unsigned long) = cfa + state.regs[i].value
+ * state.dataAlign;
+ break;
+ case Memory:
+ addr = cfa + state.regs[i].value * state.dataAlign;
+
+ if ((state.regs[i].value * state.dataAlign)
+ % sizeof(unsigned long)
+ || addr < startLoc
+ || addr + sizeof(unsigned long) < addr
+ || addr + sizeof(unsigned long) > endLoc)
+ return -EIO;
+
+ switch (reg_info[i].width) {
+ case sizeof(u8):
+ __get_user(FRAME_REG(i, u8),
+ (u8 __user *)addr);
+ break;
+ case sizeof(u16):
+ __get_user(FRAME_REG(i, u16),
+ (u16 __user *)addr);
+ break;
+ case sizeof(u32):
+ __get_user(FRAME_REG(i, u32),
+ (u32 __user *)addr);
+ break;
+#ifdef CONFIG_64BIT
+ case sizeof(u64):
+ __get_user(FRAME_REG(i, u64),
+ (u64 __user *)addr);
+ break;
+#endif
+ default:
+ return -EIO;
+ }
+
+ break;
+ }
+ unw_debug("r%d: 0x%lx ", i, *fptr);
+ }
+
+ return 0;
+#undef FRAME_REG
+}
+EXPORT_SYMBOL(arc_unwind);
diff --git a/arch/arc/kernel/vmlinux.lds.S b/arch/arc/kernel/vmlinux.lds.S
index 67c35bd..56c074e 100644
--- a/arch/arc/kernel/vmlinux.lds.S
+++ b/arch/arc/kernel/vmlinux.lds.S
@@ -101,17 +101,38 @@ SECTIONS

BSS_SECTION(0, 0, 0)

+#ifdef CONFIG_ARC_DW2_UNWIND
+ . = ALIGN(PAGE_SIZE);
+ .debug_frame : {
+ __start_unwind = .;
+ *(.debug_frame)
+ __end_unwind = .;
+ }
+#else
+ /DISCARD/ : { *(.debug_frame) }
+#endif
+
NOTES

. = ALIGN(PAGE_SIZE);
_end = . ;

STABS_DEBUG
- DWARF_DEBUG
DISCARDS

.arcextmap 0 : {
*(.gnu.linkonce.arcextmap.*)
*(.arcextmap.*)
}
+
+ /* open-coded because we need .debug_frame seperately for unwinding */
+ .debug_aranges 0 : { *(.debug_aranges) }
+ .debug_pubnames 0 : { *(.debug_pubnames) }
+ .debug_info 0 : { *(.debug_info) }
+ .debug_abbrev 0 : { *(.debug_abbrev) }
+ .debug_line 0 : { *(.debug_line) }
+ .debug_str 0 : { *(.debug_str) }
+ .debug_loc 0 : { *(.debug_loc) }
+ .debug_macinfo 0 : { *(.debug_macinfo) }
+
}
--
1.7.4.1

2012-11-12 11:51:17

by Vineet Gupta

[permalink] [raw]
Subject: [RFC Patch v1 42/55] ARC: stacktracing APIs based on dw2 unwinder

From: Vineet Gupta <[email protected]>

Signed-off-by: Vineet Gupta <[email protected]>
---
arch/arc/kernel/stacktrace.c | 215 +++++++++++++++++++++++++++++++++++++++++-
1 files changed, 213 insertions(+), 2 deletions(-)

diff --git a/arch/arc/kernel/stacktrace.c b/arch/arc/kernel/stacktrace.c
index b9d1646..a63ff84 100644
--- a/arch/arc/kernel/stacktrace.c
+++ b/arch/arc/kernel/stacktrace.c
@@ -1,13 +1,206 @@
/*
+ * stacktrace.c : stacktracing APIs needed by rest of kernel
+ * (wrappers over ARC dwarf based unwinder)
+ *
* Copyright (C) 2004, 2007-2010, 2011-2012 Synopsys, Inc. (http://www.synopsys.com)
*
* 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.
+ *
+ * vineetg: aug 2009
+ * -Implemented CONFIG_STACKTRACE APIs, primarily save_stack_trace_tsk( )
+ * for displaying task's kernel mode call stack in /proc/<pid>/stack
+ * -Iterator based approach to have single copy of unwinding core and APIs
+ * needing unwinding, implement the logic in iterator regarding:
+ * = which frame onwards to start capture
+ * = which frame to stop capturing (wchan)
+ * = specifics of data structs where trace is saved(CONFIG_STACKTRACE etc)
+ *
+ * vineetg: March 2009
+ * -Implemented correct versions of thread_saved_pc() and get_wchan()
+ *
+ * rajeshwarr: 2008
+ * -Initial implementation
*/

#include <linux/ptrace.h>
#include <linux/export.h>
+#include <linux/stacktrace.h>
+#include <linux/kallsyms.h>
+#include <asm/arcregs.h>
+#include <asm/unwind.h>
+#include <asm/switch_to.h>
+
+/*-------------------------------------------------------------------------
+ * Unwinder Iterator
+ *-------------------------------------------------------------------------
+ */
+
+#ifdef CONFIG_ARC_DW2_UNWIND
+
+static void seed_unwind_frame_info(struct task_struct *tsk,
+ struct pt_regs *regs,
+ struct unwind_frame_info *frame_info)
+{
+ if (tsk == NULL && regs == NULL) {
+ unsigned long fp, sp, blink, ret;
+ frame_info->task = current;
+
+ __asm__ __volatile__(
+ "mov %0,r27\n\t"
+ "mov %1,r28\n\t"
+ "mov %2,r31\n\t"
+ "mov %3,r63\n\t"
+ : "=r"(fp), "=r"(sp), "=r"(blink), "=r"(ret)
+ );
+
+ frame_info->regs.r27 = fp;
+ frame_info->regs.r28 = sp;
+ frame_info->regs.r31 = blink;
+ frame_info->regs.r63 = ret;
+ frame_info->call_frame = 0;
+ } else if (regs == NULL) {
+
+ frame_info->task = tsk;
+
+ frame_info->regs.r27 = KSTK_FP(tsk);
+ frame_info->regs.r28 = KSTK_ESP(tsk);
+ frame_info->regs.r31 = KSTK_BLINK(tsk);
+ frame_info->regs.r63 = (unsigned int)__switch_to;
+
+ /* In the prologue of __switch_to, first FP is saved on stack
+ * and then SP is copied to FP. Dwarf assumes cfa as FP based
+ * but we didn't save FP. The value retrieved above is FP's
+ * state in previous frame.
+ * As a work around for this, we unwind from __switch_to start
+ * and adjust SP accordingly. The other limitation is that
+ * __switch_to macro is dwarf rules are not generated for inline
+ * assembly code
+ */
+ frame_info->regs.r27 = 0;
+ frame_info->regs.r28 += 64;
+ frame_info->call_frame = 0;
+
+ } else {
+ frame_info->task = tsk;
+
+ frame_info->regs.r27 = regs->fp;
+ frame_info->regs.r28 = regs->sp;
+ frame_info->regs.r31 = regs->blink;
+ frame_info->regs.r63 = regs->ret;
+ frame_info->call_frame = 0;
+ }
+}
+
+#endif
+
+static noinline unsigned int
+arc_unwind_core(struct task_struct *tsk, struct pt_regs *regs,
+ int (*consumer_fn) (unsigned int, void *), void *arg)
+{
+#ifdef CONFIG_ARC_DW2_UNWIND
+ int ret = 0;
+ unsigned int address;
+ struct unwind_frame_info frame_info;
+
+ seed_unwind_frame_info(tsk, regs, &frame_info);
+
+ while (1) {
+ address = UNW_PC(&frame_info);
+
+ if (address && __kernel_text_address(address)) {
+ if (consumer_fn(address, arg) == -1)
+ break;
+ }
+
+ ret = arc_unwind(&frame_info);
+
+ if (ret == 0) {
+ frame_info.regs.r63 = frame_info.regs.r31;
+ continue;
+ } else {
+ break;
+ }
+ }
+
+ return address; /* return the last address it saw */
+#else
+ /* On ARC, only Dward based unwinder works. fp based backtracing is
+ * not possible (-fno-omit-frame-pointer) because of the way function
+ * prelogue is setup (callee regs saved and then fp set and not other
+ * way around
+ */
+ pr_warn("CONFIG_ARC_DW2_UNWIND needs to be enabled\n");
+ return 0;
+
+#endif
+}
+
+/*-------------------------------------------------------------------------
+ * callbacks called by unwinder iterator to implement kernel APIs
+ *
+ * The callback can return -1 to force the iterator to stop, which by default
+ * keeps going till the bottom-most frame.
+ *-------------------------------------------------------------------------
+ */
+
+/* Call-back which plugs into unwinding core to dump the stack in
+ * case of panic/OOPs/BUG etc
+ */
+static int __print_sym(unsigned int address, void *unused)
+{
+ __print_symbol(" %s\n", address);
+ return 0;
+}
+
+#ifdef CONFIG_STACKTRACE
+
+/* Call-back which plugs into unwinding core to capture the
+ * traces needed by kernel on /proc/<pid>/stack
+ */
+static int __collect_all(unsigned int address, void *arg)
+{
+ struct stack_trace *trace = arg;
+
+ if (trace->skip > 0)
+ trace->skip--;
+ else
+ trace->entries[trace->nr_entries++] = address;
+
+ if (trace->nr_entries >= trace->max_entries)
+ return -1;
+
+ return 0;
+}
+
+static int __collect_all_but_sched(unsigned int address, void *arg)
+{
+ struct stack_trace *trace = arg;
+
+ if (in_sched_functions(address))
+ return 0;
+
+ if (trace->skip > 0)
+ trace->skip--;
+ else
+ trace->entries[trace->nr_entries++] = address;
+
+ if (trace->nr_entries >= trace->max_entries)
+ return -1;
+
+ return 0;
+}
+
+#endif
+
+static int __get_first_nonsched(unsigned int address, void *unused)
+{
+ if (in_sched_functions(address))
+ return 0;
+
+ return -1;
+}

/*-------------------------------------------------------------------------
* APIs expected by various kernel sub-systems
@@ -16,7 +209,8 @@

noinline void show_stacktrace(struct task_struct *tsk, struct pt_regs *regs)
{
- pr_info("\nStack Trace: NOT Available\n");
+ pr_info("\nStack Trace:\n");
+ arc_unwind_core(tsk, regs, __print_sym, NULL);
}
EXPORT_SYMBOL(show_stacktrace);

@@ -39,5 +233,22 @@ EXPORT_SYMBOL(dump_stack);
*/
unsigned int get_wchan(struct task_struct *tsk)
{
- return 0;
+ return arc_unwind_core(tsk, NULL, __get_first_nonsched, NULL);
+}
+
+#ifdef CONFIG_STACKTRACE
+
+/*
+ * API required by CONFIG_STACKTRACE, CONFIG_LATENCYTOP.
+ * A typical use is when /proc/<pid>/stack is queried by userland
+ */
+void save_stack_trace_tsk(struct task_struct *tsk, struct stack_trace *trace)
+{
+ arc_unwind_core(tsk, NULL, __collect_all_but_sched, trace);
+}
+
+void save_stack_trace(struct stack_trace *trace)
+{
+ arc_unwind_core(current, NULL, __collect_all, trace);
}
+#endif
--
1.7.4.1

2012-11-12 11:51:24

by Vineet Gupta

[permalink] [raw]
Subject: [RFC Patch v1 43/55] ARC: disassembly (needed by kprobes/kgdb/unaligned-access-emul)

From: Vineet Gupta <[email protected]>

Orig written by Rajeshwar Ranga <[email protected]>

Consolidation/cleanups by Mischa Jonker <[email protected]>

Signed-off-by: Vineet Gupta <[email protected]>
---
arch/arc/include/asm/disasm.h | 116 +++++++++
arch/arc/kernel/Makefile | 2 +-
arch/arc/kernel/disasm.c | 539 +++++++++++++++++++++++++++++++++++++++++
3 files changed, 656 insertions(+), 1 deletions(-)
create mode 100644 arch/arc/include/asm/disasm.h
create mode 100644 arch/arc/kernel/disasm.c

diff --git a/arch/arc/include/asm/disasm.h b/arch/arc/include/asm/disasm.h
new file mode 100644
index 0000000..f1cce3d
--- /dev/null
+++ b/arch/arc/include/asm/disasm.h
@@ -0,0 +1,116 @@
+/*
+ * several functions that help interpret ARC instructions
+ * used for unaligned accesses, kprobes and kgdb
+ *
+ * Copyright (C) 2004, 2007-2010, 2011-2012 Synopsys, Inc. (http://www.synopsys.com)
+ *
+ * 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.
+ */
+
+#ifndef __ARC_DISASM_H__
+#define __ARC_DISASM_H__
+
+enum {
+ op_Bcc = 0, op_BLcc = 1, op_LD = 2, op_ST = 3, op_MAJOR_4 = 4,
+ op_MAJOR_5 = 5, op_LD_ADD = 12, op_ADD_SUB_SHIFT = 13,
+ op_ADD_MOV_CMP = 14, op_S = 15, op_LD_S = 16, op_LDB_S = 17,
+ op_LDW_S = 18, op_LDWX_S = 19, op_ST_S = 20, op_STB_S = 21,
+ op_STW_S = 22, op_Su5 = 23, op_SP = 24, op_GP = 25,
+ op_Pcl = 26, op_MOV_S = 27, op_ADD_CMP = 28, op_BR_S = 29,
+ op_B_S = 30, op_BL_S = 31
+};
+
+enum flow {
+ noflow,
+ direct_jump,
+ direct_call,
+ indirect_jump,
+ indirect_call,
+ invalid_instr
+};
+
+#define IS_BIT(word, n) ((word) & (1<<n))
+#define BITS(word, s, e) (((word) >> (s)) & (~((-2) << ((e) - (s)))))
+
+#define MAJOR_OPCODE(word) (BITS((word), 27, 31))
+#define MINOR_OPCODE(word) (BITS((word), 16, 21))
+#define FIELD_A(word) (BITS((word), 0, 5))
+#define FIELD_B(word) ((BITS((word), 12, 14)<<3) | \
+ (BITS((word), 24, 26)))
+#define FIELD_C(word) (BITS((word), 6, 11))
+#define FIELD_u6(word) FIELDC(word)
+#define FIELD_s12(word) sign_extend(((BITS((word), 0, 5) << 6) | \
+ BITS((word), 6, 11)), 12)
+
+/* note that for BL/BRcc these two macro's need another AND statement to mask
+ * out bit 1 (make the result a multiple of 4) */
+#define FIELD_s9(word) sign_extend(((BITS(word, 15, 15) << 8) | \
+ BITS(word, 16, 23)), 9)
+#define FIELD_s21(word) sign_extend(((BITS(word, 6, 15) << 11) | \
+ (BITS(word, 17, 26) << 1)), 12)
+#define FIELD_s25(word) sign_extend(((BITS(word, 0, 3) << 21) | \
+ (BITS(word, 6, 15) << 11) | \
+ (BITS(word, 17, 26) << 1)), 12)
+
+/* note: these operate on 16 bits! */
+#define FIELD_S_A(word) ((BITS((word), 2, 2)<<3) | BITS((word), 0, 2))
+#define FIELD_S_B(word) ((BITS((word), 10, 10)<<3) | \
+ BITS((word), 8, 10))
+#define FIELD_S_C(word) ((BITS((word), 7, 7)<<3) | BITS((word), 5, 7))
+#define FIELD_S_H(word) ((BITS((word), 0, 2)<<3) | BITS((word), 5, 8))
+#define FIELD_S_u5(word) (BITS((word), 0, 4))
+#define FIELD_S_u6(word) (BITS((word), 0, 4) << 1)
+#define FIELD_S_u7(word) (BITS((word), 0, 4) << 2)
+#define FIELD_S_u10(word) (BITS((word), 0, 7) << 2)
+#define FIELD_S_s7(word) sign_extend(BITS((word), 0, 5) << 1, 9)
+#define FIELD_S_s8(word) sign_extend(BITS((word), 0, 7) << 1, 9)
+#define FIELD_S_s9(word) sign_extend(BITS((word), 0, 8), 9)
+#define FIELD_S_s10(word) sign_extend(BITS((word), 0, 8) << 1, 10)
+#define FIELD_S_s11(word) sign_extend(BITS((word), 0, 8) << 2, 11)
+#define FIELD_S_s13(word) sign_extend(BITS((word), 0, 10) << 2, 13)
+
+#define STATUS32_L 0x00000100
+#define REG_LIMM 62
+
+struct disasm_state {
+ /* generic info */
+ unsigned long words[2];
+ int instr_len;
+ int major_opcode;
+ /* info for branch/jump */
+ int is_branch;
+ int target;
+ int delay_slot;
+ enum flow flow;
+ /* info for load/store */
+ int src1, src2, src3, dest, wb_reg;
+ int zz, aa, x, pref, di;
+ int fault, write;
+};
+
+static inline int sign_extend(int value, int bits)
+{
+ if (IS_BIT(value, (bits - 1)))
+ value |= (0xffffffff << bits);
+
+ return value;
+}
+
+static inline int is_short_instr(unsigned long addr)
+{
+ uint16_t word = *((uint16_t *)addr);
+ int opcode = (word >> 11) & 0x1F;
+ return (opcode >= 0x0B);
+}
+
+void disasm_instr(unsigned long addr, struct disasm_state *state,
+ int userspace, struct pt_regs *regs, struct callee_regs *cregs);
+int disasm_next_pc(unsigned long pc, struct pt_regs *regs, struct callee_regs
+ *cregs, unsigned long *fall_thru, unsigned long *target);
+long get_reg(int reg, struct pt_regs *regs, struct callee_regs *cregs);
+void set_reg(int reg, long val, struct pt_regs *regs,
+ struct callee_regs *cregs);
+
+#endif /* __ARC_DISASM_H__ */
diff --git a/arch/arc/kernel/Makefile b/arch/arc/kernel/Makefile
index 86b159f..1fc0461 100644
--- a/arch/arc/kernel/Makefile
+++ b/arch/arc/kernel/Makefile
@@ -9,7 +9,7 @@
CFLAGS_ptrace.o += -DUTS_MACHINE='"$(UTS_MACHINE)"'

obj-y := arcksyms.o setup.o irq.o time.o reset.o ptrace.o entry.o process.o \
- signal.o traps.o sys.o troubleshoot.o stacktrace.o
+ signal.o traps.o sys.o troubleshoot.o stacktrace.o disasm.c

obj-$(CONFIG_MODULES) += arcksyms.o module.o
obj-$(CONFIG_ARC_DBG_EVENT_TIMELINE) += event-log.o
diff --git a/arch/arc/kernel/disasm.c b/arch/arc/kernel/disasm.c
new file mode 100644
index 0000000..254d11a
--- /dev/null
+++ b/arch/arc/kernel/disasm.c
@@ -0,0 +1,539 @@
+/*
+ * several functions that help interpret ARC instructions
+ * used for unaligned accesses, kprobes and kgdb
+ *
+ * Copyright (C) 2004, 2007-2010, 2011-2012 Synopsys, Inc. (http://www.synopsys.com)
+ *
+ * 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.
+ */
+
+#include <linux/types.h>
+#include <linux/kprobes.h>
+#include <linux/slab.h>
+#include <asm/disasm.h>
+#include <asm/uaccess.h>
+
+#if defined(CONFIG_KGDB) || defined(CONFIG_MISALIGN_ACCESS) || \
+ defined(CONFIG_KPROBES)
+
+/* disasm_instr: Analyses instruction at addr, stores
+ * findings in *state
+ */
+void __kprobes disasm_instr(unsigned long addr, struct disasm_state *state,
+ int userspace, struct pt_regs *regs, struct callee_regs *cregs)
+{
+ int fieldA = 0;
+ int fieldC = 0, fieldCisReg = 0;
+ uint16_t word1 = 0, word0 = 0;
+ int subopcode, is_linked, op_format;
+ uint16_t *ins_ptr;
+ uint16_t ins_buf[4];
+ int bytes_not_copied = 0;
+
+ memset(state, 0, sizeof(struct disasm_state));
+
+ /* This fetches the upper part of the 32 bit instruction
+ * in both the cases of Little Endian or Big Endian configurations. */
+ if (userspace) {
+ bytes_not_copied = copy_from_user(ins_buf,
+ (const void __user *) addr, 8);
+ if (bytes_not_copied > 6)
+ goto fault;
+ ins_ptr = ins_buf;
+ } else {
+ ins_ptr = (uint16_t *) addr;
+ }
+
+ word1 = *((uint16_t *)addr);
+
+ state->major_opcode = (word1 >> 11) & 0x1F;
+
+ /* Check if the instruction is 32 bit or 16 bit instruction */
+ if (state->major_opcode < 0x0B) {
+ if (bytes_not_copied > 4)
+ goto fault;
+ state->instr_len = 4;
+ word0 = *((uint16_t *)(addr+2));
+ state->words[0] = (word1 << 16) | word0;
+ } else {
+ state->instr_len = 2;
+ state->words[0] = word1;
+ }
+
+ /* Read the second word in case of limm */
+ word1 = *((uint16_t *)(addr + state->instr_len));
+ word0 = *((uint16_t *)(addr + state->instr_len + 2));
+ state->words[1] = (word1 << 16) | word0;
+
+ switch (state->major_opcode) {
+ case op_Bcc:
+ state->is_branch = 1;
+
+ /* unconditional branch s25, conditional branch s21 */
+ fieldA = (IS_BIT(state->words[0], 16)) ?
+ FIELD_s25(state->words[0]) :
+ FIELD_s21(state->words[0]);
+
+ state->delay_slot = IS_BIT(state->words[0], 5);
+ state->target = fieldA + (addr & ~0x3);
+ state->flow = direct_jump;
+ break;
+
+ case op_BLcc:
+ if (IS_BIT(state->words[0], 16)) {
+ /* Branch and Link*/
+ /* unconditional branch s25, conditional branch s21 */
+ fieldA = (IS_BIT(state->words[0], 17)) ?
+ (FIELD_s25(state->words[0]) & ~0x3) :
+ FIELD_s21(state->words[0]);
+
+ state->flow = direct_call;
+ } else {
+ /*Branch On Compare */
+ fieldA = FIELD_s9(state->words[0]) & ~0x3;
+ state->flow = direct_jump;
+ }
+
+ state->delay_slot = IS_BIT(state->words[0], 5);
+ state->target = fieldA + (addr & ~0x3);
+ state->is_branch = 1;
+ break;
+
+ case op_LD: /* LD<zz> a,[b,s9] */
+ state->write = 0;
+ state->di = BITS(state->words[0], 11, 11);
+ if (state->di)
+ break;
+ state->x = BITS(state->words[0], 6, 6);
+ state->zz = BITS(state->words[0], 7, 8);
+ state->aa = BITS(state->words[0], 9, 10);
+ state->wb_reg = FIELD_B(state->words[0]);
+ if (state->wb_reg == REG_LIMM) {
+ state->instr_len += 4;
+ state->aa = 0;
+ state->src1 = state->words[1];
+ } else {
+ state->src1 = get_reg(state->wb_reg, regs, cregs);
+ }
+ state->src2 = FIELD_s9(state->words[0]);
+ state->dest = FIELD_A(state->words[0]);
+ state->pref = (state->dest == REG_LIMM);
+ break;
+
+ case op_ST:
+ state->write = 1;
+ state->di = BITS(state->words[0], 5, 5);
+ if (state->di)
+ break;
+ state->aa = BITS(state->words[0], 3, 4);
+ state->zz = BITS(state->words[0], 1, 2);
+ state->src1 = FIELD_C(state->words[0]);
+ if (state->src1 == REG_LIMM) {
+ state->instr_len += 4;
+ state->src1 = state->words[1];
+ } else {
+ state->src1 = get_reg(state->src1, regs, cregs);
+ }
+ state->wb_reg = FIELD_B(state->words[0]);
+ if (state->wb_reg == REG_LIMM) {
+ state->aa = 0;
+ state->instr_len += 4;
+ state->src2 = state->words[1];
+ } else {
+ state->src2 = get_reg(state->wb_reg, regs, cregs);
+ }
+ state->src3 = FIELD_s9(state->words[0]);
+ break;
+
+ case op_MAJOR_4:
+ subopcode = MINOR_OPCODE(state->words[0]);
+ switch (subopcode) {
+ case 32: /* Jcc */
+ case 33: /* Jcc.D */
+ case 34: /* JLcc */
+ case 35: /* JLcc.D */
+ is_linked = 0;
+
+ if (subopcode == 33 || subopcode == 35)
+ state->delay_slot = 1;
+
+ if (subopcode == 34 || subopcode == 35)
+ is_linked = 1;
+
+ fieldCisReg = 0;
+ op_format = BITS(state->words[0], 22, 23);
+ if (op_format == 0 || ((op_format == 3) &&
+ (!IS_BIT(state->words[0], 5)))) {
+ fieldC = FIELD_C(state->words[0]);
+
+ if (fieldC == REG_LIMM) {
+ fieldC = state->words[1];
+ state->instr_len += 4;
+ } else {
+ fieldCisReg = 1;
+ }
+ } else if (op_format == 1 || ((op_format == 3)
+ && (IS_BIT(state->words[0], 5)))) {
+ fieldC = FIELD_C(state->words[0]);
+ } else {
+ /* op_format == 2 */
+ fieldC = FIELD_s12(state->words[0]);
+ }
+
+ if (!fieldCisReg) {
+ state->target = fieldC;
+ state->flow = is_linked ?
+ direct_call : direct_jump;
+ } else {
+ state->target = get_reg(fieldC, regs, cregs);
+ state->flow = is_linked ?
+ indirect_call : indirect_jump;
+ }
+ state->is_branch = 1;
+ break;
+
+ case 40: /* LPcc */
+ if (BITS(state->words[0], 22, 23) == 3) {
+ /* Conditional LPcc u7 */
+ fieldC = FIELD_C(state->words[0]);
+
+ fieldC = fieldC << 1;
+ fieldC += (addr & ~0x03);
+ state->is_branch = 1;
+ state->flow = direct_jump;
+ state->target = fieldC;
+ }
+ /* For Unconditional lp, next pc is the fall through
+ * which is updated */
+ break;
+
+ case 48 ... 55: /* LD a,[b,c] */
+ state->di = BITS(state->words[0], 15, 15);
+ if (state->di)
+ break;
+ state->x = BITS(state->words[0], 16, 16);
+ state->zz = BITS(state->words[0], 17, 18);
+ state->aa = BITS(state->words[0], 22, 23);
+ state->wb_reg = FIELD_B(state->words[0]);
+ if (state->wb_reg == REG_LIMM) {
+ state->instr_len += 4;
+ state->src1 = state->words[1];
+ } else {
+ state->src1 = get_reg(state->wb_reg, regs,
+ cregs);
+ }
+ state->src2 = FIELD_C(state->words[0]);
+ if (state->src2 == REG_LIMM) {
+ state->instr_len += 4;
+ state->src2 = state->words[1];
+ } else {
+ state->src2 = get_reg(state->src2, regs,
+ cregs);
+ }
+ state->dest = FIELD_A(state->words[0]);
+ if (state->dest == REG_LIMM)
+ state->pref = 1;
+ break;
+
+ case 10: /* MOV */
+ /* still need to check for limm to extract instr len */
+ /* MOV is special case because it only takes 2 args */
+ switch (BITS(state->words[0], 22, 23)) {
+ case 0: /* OP a,b,c */
+ if (FIELD_C(state->words[0]) == REG_LIMM)
+ state->instr_len += 4;
+ break;
+ case 1: /* OP a,b,u6 */
+ break;
+ case 2: /* OP b,b,s12 */
+ break;
+ case 3: /* OP.cc b,b,c/u6 */
+ if ((!IS_BIT(state->words[0], 5)) &&
+ (FIELD_C(state->words[0]) == REG_LIMM))
+ state->instr_len += 4;
+ break;
+ }
+ break;
+
+
+ default:
+ /* Not a Load, Jump or Loop instruction */
+ /* still need to check for limm to extract instr len */
+ switch (BITS(state->words[0], 22, 23)) {
+ case 0: /* OP a,b,c */
+ if ((FIELD_B(state->words[0]) == REG_LIMM) ||
+ (FIELD_C(state->words[0]) == REG_LIMM))
+ state->instr_len += 4;
+ break;
+ case 1: /* OP a,b,u6 */
+ break;
+ case 2: /* OP b,b,s12 */
+ break;
+ case 3: /* OP.cc b,b,c/u6 */
+ if ((!IS_BIT(state->words[0], 5)) &&
+ ((FIELD_B(state->words[0]) == REG_LIMM) ||
+ (FIELD_C(state->words[0]) == REG_LIMM)))
+ state->instr_len += 4;
+ break;
+ }
+ break;
+ }
+ break;
+
+ /* 16 Bit Instructions */
+ case op_LD_ADD: /* LD_S|LDB_S|LDW_S a,[b,c] */
+ state->zz = BITS(state->words[0], 3, 4);
+ state->src1 = get_reg(FIELD_S_B(state->words[0]), regs, cregs);
+ state->src2 = get_reg(FIELD_S_C(state->words[0]), regs, cregs);
+ state->dest = FIELD_S_A(state->words[0]);
+ break;
+
+ case op_ADD_MOV_CMP:
+ /* check for limm, ignore mov_s h,b (== mov_s 0,b) */
+ if ((BITS(state->words[0], 3, 4) < 3) &&
+ (FIELD_S_H(state->words[0]) == REG_LIMM))
+ state->instr_len += 4;
+ break;
+
+ case op_S:
+ subopcode = BITS(state->words[0], 5, 7);
+ switch (subopcode) {
+ case 0: /* j_s */
+ case 1: /* j_s.d */
+ case 2: /* jl_s */
+ case 3: /* jl_s.d */
+ state->target = get_reg(FIELD_S_B(state->words[0]),
+ regs, cregs);
+ state->delay_slot = subopcode & 1;
+ state->flow = (subopcode >= 2) ?
+ direct_call : indirect_jump;
+ break;
+ case 7:
+ switch (BITS(state->words[0], 8, 10)) {
+ case 4: /* jeq_s [blink] */
+ case 5: /* jne_s [blink] */
+ case 6: /* j_s [blink] */
+ case 7: /* j_s.d [blink] */
+ state->delay_slot = (subopcode == 7);
+ state->flow = indirect_jump;
+ state->target = get_reg(31, regs, cregs);
+ default:
+ break;
+ }
+ default:
+ break;
+ }
+ break;
+
+ case op_LD_S: /* LD_S c, [b, u7] */
+ state->src1 = get_reg(FIELD_S_B(state->words[0]), regs, cregs);
+ state->src2 = FIELD_S_u7(state->words[0]);
+ state->dest = FIELD_S_C(state->words[0]);
+ break;
+
+ case op_LDB_S:
+ case op_STB_S:
+ /* no further handling required as byte accesses should not
+ * cause an unaligned access exception */
+ state->zz = 1;
+ break;
+
+ case op_LDWX_S: /* LDWX_S c, [b, u6] */
+ state->x = 1;
+ /* intentional fall-through */
+
+ case op_LDW_S: /* LDW_S c, [b, u6] */
+ state->zz = 2;
+ state->src1 = get_reg(FIELD_S_B(state->words[0]), regs, cregs);
+ state->src2 = FIELD_S_u6(state->words[0]);
+ state->dest = FIELD_S_C(state->words[0]);
+ break;
+
+ case op_ST_S: /* ST_S c, [b, u7] */
+ state->write = 1;
+ state->src1 = get_reg(FIELD_S_C(state->words[0]), regs, cregs);
+ state->src2 = get_reg(FIELD_S_B(state->words[0]), regs, cregs);
+ state->src3 = FIELD_S_u7(state->words[0]);
+ break;
+
+ case op_STW_S: /* STW_S c,[b,u6] */
+ state->write = 1;
+ state->zz = 2;
+ state->src1 = get_reg(FIELD_S_C(state->words[0]), regs, cregs);
+ state->src2 = get_reg(FIELD_S_B(state->words[0]), regs, cregs);
+ state->src3 = FIELD_S_u6(state->words[0]);
+ break;
+
+ case op_SP: /* LD_S|LDB_S b,[sp,u7], ST_S|STB_S b,[sp,u7] */
+ /* note: we are ignoring possibility of:
+ * ADD_S, SUB_S, PUSH_S, POP_S as these should not
+ * cause unaliged exception anyway */
+ state->write = BITS(state->words[0], 6, 6);
+ state->zz = BITS(state->words[0], 5, 5);
+ if (state->zz)
+ break; /* byte accesses should not come here */
+ if (!state->write) {
+ state->src1 = get_reg(28, regs, cregs);
+ state->src2 = FIELD_S_u7(state->words[0]);
+ state->dest = FIELD_S_B(state->words[0]);
+ } else {
+ state->src1 = get_reg(FIELD_S_B(state->words[0]), regs,
+ cregs);
+ state->src2 = get_reg(28, regs, cregs);
+ state->src3 = FIELD_S_u7(state->words[0]);
+ }
+ break;
+
+ case op_GP: /* LD_S|LDB_S|LDW_S r0,[gp,s11/s9/s10] */
+ /* note: ADD_S r0, gp, s11 is ignored */
+ state->zz = BITS(state->words[0], 9, 10);
+ state->src1 = get_reg(26, regs, cregs);
+ state->src2 = state->zz ? FIELD_S_s10(state->words[0]) :
+ FIELD_S_s11(state->words[0]);
+ state->dest = 0;
+ break;
+
+ case op_Pcl: /* LD_S b,[pcl,u10] */
+ state->src1 = regs->ret & ~3;
+ state->src2 = FIELD_S_u10(state->words[0]);
+ state->dest = FIELD_S_B(state->words[0]);
+ break;
+
+ case op_BR_S:
+ state->target = FIELD_S_s8(state->words[0]) + (addr & ~0x03);
+ state->flow = direct_jump;
+ state->is_branch = 1;
+ break;
+
+ case op_B_S:
+ fieldA = (BITS(state->words[0], 9, 10) == 3) ?
+ FIELD_S_s7(state->words[0]) :
+ FIELD_S_s10(state->words[0]);
+ state->target = fieldA + (addr & ~0x03);
+ state->flow = direct_jump;
+ state->is_branch = 1;
+ break;
+
+ case op_BL_S:
+ state->target = FIELD_S_s13(state->words[0]) + (addr & ~0x03);
+ state->flow = direct_call;
+ state->is_branch = 1;
+ break;
+
+ default:
+ break;
+ }
+
+ if (bytes_not_copied <= (8 - state->instr_len))
+ return;
+
+fault: state->fault = 1;
+}
+
+long __kprobes get_reg(int reg, struct pt_regs *regs,
+ struct callee_regs *cregs)
+{
+ long *p;
+
+ if (reg <= 12) {
+ p = &regs->r0;
+ return p[-reg];
+ }
+
+ if (cregs && (reg <= 25)) {
+ p = &cregs->r13;
+ return p[13-reg];
+ }
+
+ if (reg == 26)
+ return regs->r26;
+ if (reg == 27)
+ return regs->fp;
+ if (reg == 28)
+ return regs->sp;
+ if (reg == 31)
+ return regs->blink;
+
+ return 0;
+}
+
+void __kprobes set_reg(int reg, long val, struct pt_regs *regs,
+ struct callee_regs *cregs)
+{
+ long *p;
+
+ switch (reg) {
+ case 0 ... 12:
+ p = &regs->r0;
+ p[-reg] = val;
+ break;
+ case 13 ... 25:
+ if (cregs) {
+ p = &cregs->r13;
+ p[13-reg] = val;
+ }
+ break;
+ case 26:
+ regs->r26 = val;
+ break;
+ case 27:
+ regs->fp = val;
+ break;
+ case 28:
+ regs->sp = val;
+ break;
+ case 31:
+ regs->blink = val;
+ break;
+ default:
+ break;
+ }
+}
+
+/*
+ * Disassembles the insn at @pc and sets @next_pc to next PC (which could be
+ * @pc +2/4/6 (ARCompact ISA allows free intermixing of 16/32 bit insns).
+ *
+ * If @pc is a branch
+ * -@tgt_if_br is set to branch target.
+ * -If branch has delay slot, @next_pc updated with actual next PC.
+ *
+ */
+int __kprobes disasm_next_pc(unsigned long pc, struct pt_regs *regs,
+ struct callee_regs *cregs,
+ unsigned long *next_pc, unsigned long *tgt_if_br)
+{
+ struct disasm_state instr;
+
+ memset(&instr, 0, sizeof(struct disasm_state));
+ disasm_instr(pc, &instr, 0, regs, cregs);
+
+ *next_pc = pc + instr.instr_len;
+
+ /* Instruction with possible two targets branch, jump and loop */
+ if (instr.is_branch)
+ *tgt_if_br = instr.target;
+
+ /* For the instructions with delay slots, the fall through is the
+ * instruction following the instruction in delay slot.
+ */
+ if (instr.delay_slot) {
+ struct disasm_state instr_d;
+
+ disasm_instr(*next_pc, &instr_d, 0, regs, cregs);
+
+ *next_pc += instr_d.instr_len;
+ }
+
+ /* Zero Overhead Loop - end of the loop */
+ if (!(regs->status32 & STATUS32_L) && (*next_pc == regs->lp_end)
+ && (regs->lp_count > 1)) {
+ *next_pc = regs->lp_start;
+ }
+
+ return instr.is_branch;
+}
+
+#endif /* CONFIG_KGDB || CONFIG_MISALIGN_ACCESS || CONFIG_KPROBES */
--
1.7.4.1

2012-11-12 11:51:32

by Vineet Gupta

[permalink] [raw]
Subject: [RFC Patch v1 44/55] ARC: kprobes support

From: Vineet Gupta <[email protected]>

Origin port done by Rajeshwar Ranga <[email protected]>

Signed-off-by: Vineet Gupta <[email protected]>
---
arch/arc/Kconfig | 2 +
arch/arc/include/asm/kprobes.h | 62 +++++
arch/arc/kernel/Makefile | 1 +
arch/arc/kernel/disasm.c | 5 +-
arch/arc/kernel/kprobes.c | 525 ++++++++++++++++++++++++++++++++++++++++
arch/arc/kernel/traps.c | 13 +
6 files changed, 605 insertions(+), 3 deletions(-)
create mode 100644 arch/arc/include/asm/kprobes.h
create mode 100644 arch/arc/kernel/kprobes.c

diff --git a/arch/arc/Kconfig b/arch/arc/Kconfig
index 26609d6..3ef21be 100644
--- a/arch/arc/Kconfig
+++ b/arch/arc/Kconfig
@@ -22,6 +22,8 @@ config ARC
select GENERIC_SMP_IDLE_THREAD
select HAVE_ARCH_TRACEHOOK
select HAVE_GENERIC_HARDIRQS
+ select HAVE_KPROBES
+ select HAVE_KRETPROBES
select HAVE_MOD_ARCH_SPECIFIC if ARC_DW2_UNWIND
select HAVE_OPROFILE
select HOTPLUG if !INITRAMFS_SOURCE=""
diff --git a/arch/arc/include/asm/kprobes.h b/arch/arc/include/asm/kprobes.h
new file mode 100644
index 0000000..4d9c211
--- /dev/null
+++ b/arch/arc/include/asm/kprobes.h
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2004, 2007-2010, 2011-2012 Synopsys, Inc. (http://www.synopsys.com)
+ *
+ * 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.
+ */
+
+#ifndef _ARC_KPROBES_H
+#define _ARC_KPROBES_H
+
+#ifdef CONFIG_KPROBES
+
+typedef u16 kprobe_opcode_t;
+
+#define UNIMP_S_INSTRUCTION 0x79e0
+#define TRAP_S_2_INSTRUCTION 0x785e
+
+#define MAX_INSN_SIZE 8
+#define MAX_STACK_SIZE 64
+
+struct arch_specific_insn {
+ int is_short;
+ kprobe_opcode_t *t1_addr, *t2_addr;
+ kprobe_opcode_t t1_opcode, t2_opcode;
+};
+
+#define flush_insn_slot(p) do { } while (0)
+
+#define kretprobe_blacklist_size 0
+
+struct kprobe;
+
+void arch_remove_kprobe(struct kprobe *p);
+
+int kprobe_exceptions_notify(struct notifier_block *self,
+ unsigned long val, void *data);
+
+struct prev_kprobe {
+ struct kprobe *kp;
+ unsigned long status;
+};
+
+struct kprobe_ctlblk {
+ unsigned int kprobe_status;
+ struct pt_regs jprobe_saved_regs;
+ char jprobes_stack[MAX_STACK_SIZE];
+ struct prev_kprobe prev_kprobe;
+};
+
+int kprobe_fault_handler(struct pt_regs *regs, unsigned long cause);
+void kretprobe_trampoline(void);
+void trap_is_kprobe(unsigned long cause, unsigned long address,
+ struct pt_regs *regs);
+#else
+static void trap_is_kprobe(unsigned long cause, unsigned long address,
+ struct pt_regs *regs)
+{
+}
+#endif
+
+#endif
diff --git a/arch/arc/kernel/Makefile b/arch/arc/kernel/Makefile
index 1fc0461..442d024 100644
--- a/arch/arc/kernel/Makefile
+++ b/arch/arc/kernel/Makefile
@@ -15,6 +15,7 @@ obj-$(CONFIG_MODULES) += arcksyms.o module.o
obj-$(CONFIG_ARC_DBG_EVENT_TIMELINE) += event-log.o
obj-$(CONFIG_SMP) += smp.o
obj-$(CONFIG_ARC_DW2_UNWIND) += unwind.o
+obj-$(CONFIG_KPROBES) += kprobes.o
obj-$(CONFIG_ARC_FPU_SAVE_RESTORE) += fpu.o
CFLAGS_fpu.o += -mdpfp

diff --git a/arch/arc/kernel/disasm.c b/arch/arc/kernel/disasm.c
index 254d11a..51bad8f 100644
--- a/arch/arc/kernel/disasm.c
+++ b/arch/arc/kernel/disasm.c
@@ -497,9 +497,8 @@ void __kprobes set_reg(int reg, long val, struct pt_regs *regs,
* @pc +2/4/6 (ARCompact ISA allows free intermixing of 16/32 bit insns).
*
* If @pc is a branch
- * -@tgt_if_br is set to branch target.
- * -If branch has delay slot, @next_pc updated with actual next PC.
- *
+ * -@tgt_if_br is set to branch target.
+ * -If branch has delay slot, @next_pc updated with actual next PC.
*/
int __kprobes disasm_next_pc(unsigned long pc, struct pt_regs *regs,
struct callee_regs *cregs,
diff --git a/arch/arc/kernel/kprobes.c b/arch/arc/kernel/kprobes.c
new file mode 100644
index 0000000..47e42f2
--- /dev/null
+++ b/arch/arc/kernel/kprobes.c
@@ -0,0 +1,525 @@
+/*
+ * Copyright (C) 2004, 2007-2010, 2011-2012 Synopsys, Inc. (http://www.synopsys.com)
+ *
+ * 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.
+ */
+
+#include <linux/types.h>
+#include <linux/kprobes.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/kprobes.h>
+#include <linux/kdebug.h>
+#include <linux/sched.h>
+#include <linux/uaccess.h>
+#include <asm/cacheflush.h>
+#include <asm/current.h>
+#include <asm/disasm.h>
+
+#define MIN_STACK_SIZE(addr) min((unsigned long)MAX_STACK_SIZE, \
+ (unsigned long)current_thread_info() + THREAD_SIZE - (addr))
+
+DEFINE_PER_CPU(struct kprobe *, current_kprobe) = NULL;
+DEFINE_PER_CPU(struct kprobe_ctlblk, kprobe_ctlblk);
+
+int __kprobes arch_prepare_kprobe(struct kprobe *p)
+{
+ /* Attempt to probe at unaligned address */
+ if ((unsigned long)p->addr & 0x01)
+ return -EINVAL;
+
+ /* Address should not be in exception handling code */
+
+ p->ainsn.is_short = is_short_instr((unsigned long)p->addr);
+ p->opcode = *p->addr;
+
+ return 0;
+}
+
+void __kprobes arch_arm_kprobe(struct kprobe *p)
+{
+ *p->addr = UNIMP_S_INSTRUCTION;
+
+ flush_icache_range((unsigned long)p->addr,
+ (unsigned long)p->addr + sizeof(kprobe_opcode_t));
+}
+
+void __kprobes arch_disarm_kprobe(struct kprobe *p)
+{
+ *p->addr = p->opcode;
+
+ flush_icache_range((unsigned long)p->addr,
+ (unsigned long)p->addr + sizeof(kprobe_opcode_t));
+}
+
+void __kprobes arch_remove_kprobe(struct kprobe *p)
+{
+ arch_disarm_kprobe(p);
+
+ /* Can we remove the kprobe in the middle of kprobe handling? */
+ if (p->ainsn.t1_addr) {
+ *(p->ainsn.t1_addr) = p->ainsn.t1_opcode;
+
+ flush_icache_range((unsigned long)p->ainsn.t1_addr,
+ (unsigned long)p->ainsn.t1_addr +
+ sizeof(kprobe_opcode_t));
+
+ p->ainsn.t1_addr = NULL;
+ }
+
+ if (p->ainsn.t2_addr) {
+ *(p->ainsn.t2_addr) = p->ainsn.t2_opcode;
+
+ flush_icache_range((unsigned long)p->ainsn.t2_addr,
+ (unsigned long)p->ainsn.t2_addr +
+ sizeof(kprobe_opcode_t));
+
+ p->ainsn.t2_addr = NULL;
+ }
+}
+
+static void __kprobes save_previous_kprobe(struct kprobe_ctlblk *kcb)
+{
+ kcb->prev_kprobe.kp = kprobe_running();
+ kcb->prev_kprobe.status = kcb->kprobe_status;
+}
+
+static void __kprobes restore_previous_kprobe(struct kprobe_ctlblk *kcb)
+{
+ __get_cpu_var(current_kprobe) = kcb->prev_kprobe.kp;
+ kcb->kprobe_status = kcb->prev_kprobe.status;
+}
+
+static inline void __kprobes set_current_kprobe(struct kprobe *p)
+{
+ __get_cpu_var(current_kprobe) = p;
+}
+
+static void __kprobes resume_execution(struct kprobe *p, unsigned long addr,
+ struct pt_regs *regs)
+{
+ /* Remove the trap instructions inserted for single step and
+ * restore the original instructions
+ */
+ if (p->ainsn.t1_addr) {
+ *(p->ainsn.t1_addr) = p->ainsn.t1_opcode;
+
+ flush_icache_range((unsigned long)p->ainsn.t1_addr,
+ (unsigned long)p->ainsn.t1_addr +
+ sizeof(kprobe_opcode_t));
+
+ p->ainsn.t1_addr = NULL;
+ }
+
+ if (p->ainsn.t2_addr) {
+ *(p->ainsn.t2_addr) = p->ainsn.t2_opcode;
+
+ flush_icache_range((unsigned long)p->ainsn.t2_addr,
+ (unsigned long)p->ainsn.t2_addr +
+ sizeof(kprobe_opcode_t));
+
+ p->ainsn.t2_addr = NULL;
+ }
+
+ return;
+}
+
+static void __kprobes setup_singlestep(struct kprobe *p, struct pt_regs *regs)
+{
+ unsigned long next_pc;
+ unsigned long tgt_if_br = 0;
+ int is_branch;
+ unsigned long bta;
+
+ /* Copy the opcode back to the kprobe location and execute the
+ * instruction. Because of this we will not be able to get into the
+ * same kprobe until this kprobe is done
+ */
+ *(p->addr) = p->opcode;
+
+ flush_icache_range((unsigned long)p->addr,
+ (unsigned long)p->addr + sizeof(kprobe_opcode_t));
+
+ /* Now we insert the trap at the next location after this instruction to
+ * single step. If it is a branch we insert the trap at possible branch
+ * targets
+ */
+
+ bta = regs->bta;
+
+ if (regs->status32 & 0x40) {
+ /* We are in a delay slot with the branch taken */
+
+ next_pc = bta & ~0x01;
+
+ if (!p->ainsn.is_short) {
+ if (bta & 0x01)
+ regs->blink += 2;
+ else {
+ /* Branch not taken */
+ next_pc += 2;
+
+ /* next pc is taken from bta after executing the
+ * delay slot instruction
+ */
+ regs->bta += 2;
+ }
+ }
+
+ is_branch = 0;
+ } else
+ is_branch =
+ disasm_next_pc((unsigned long)p->addr, regs,
+ (struct callee_regs *) current->thread.callee_reg,
+ &next_pc, &tgt_if_br);
+
+ p->ainsn.t1_addr = (kprobe_opcode_t *) next_pc;
+ p->ainsn.t1_opcode = *(p->ainsn.t1_addr);
+ *(p->ainsn.t1_addr) = TRAP_S_2_INSTRUCTION;
+
+ flush_icache_range((unsigned long)p->ainsn.t1_addr,
+ (unsigned long)p->ainsn.t1_addr +
+ sizeof(kprobe_opcode_t));
+
+ if (is_branch) {
+ p->ainsn.t2_addr = (kprobe_opcode_t *) tgt_if_br;
+ p->ainsn.t2_opcode = *(p->ainsn.t2_addr);
+ *(p->ainsn.t2_addr) = TRAP_S_2_INSTRUCTION;
+
+ flush_icache_range((unsigned long)p->ainsn.t2_addr,
+ (unsigned long)p->ainsn.t2_addr +
+ sizeof(kprobe_opcode_t));
+ }
+}
+
+int __kprobes arc_kprobe_handler(unsigned long addr, struct pt_regs *regs)
+{
+ struct kprobe *p;
+ struct kprobe_ctlblk *kcb;
+
+ preempt_disable();
+
+ kcb = get_kprobe_ctlblk();
+ p = get_kprobe((unsigned long *)addr);
+
+ if (p) {
+ /*
+ * We have reentered the kprobe_handler, since another kprobe
+ * was hit while within the handler, we save the original
+ * kprobes and single step on the instruction of the new probe
+ * without calling any user handlers to avoid recursive
+ * kprobes.
+ */
+ if (kprobe_running()) {
+ save_previous_kprobe(kcb);
+ set_current_kprobe(p);
+ kprobes_inc_nmissed_count(p);
+ setup_singlestep(p, regs);
+ kcb->kprobe_status = KPROBE_REENTER;
+ return 1;
+ }
+
+ set_current_kprobe(p);
+ kcb->kprobe_status = KPROBE_HIT_ACTIVE;
+
+ /* If we have no pre-handler or it returned 0, we continue with
+ * normal processing. If we have a pre-handler and it returned
+ * non-zero - which is expected from setjmp_pre_handler for
+ * jprobe, we return without single stepping and leave that to
+ * the break-handler which is invoked by a kprobe from
+ * jprobe_return
+ */
+ if (!p->pre_handler || !p->pre_handler(p, regs)) {
+ setup_singlestep(p, regs);
+ kcb->kprobe_status = KPROBE_HIT_SS;
+ }
+
+ return 1;
+ } else if (kprobe_running()) {
+ p = __get_cpu_var(current_kprobe);
+ if (p->break_handler && p->break_handler(p, regs)) {
+ setup_singlestep(p, regs);
+ kcb->kprobe_status = KPROBE_HIT_SS;
+ return 1;
+ }
+ }
+
+ /* no_kprobe: */
+ preempt_enable_no_resched();
+ return 0;
+}
+
+static int __kprobes arc_post_kprobe_handler(unsigned long addr,
+ struct pt_regs *regs)
+{
+ struct kprobe *cur = kprobe_running();
+ struct kprobe_ctlblk *kcb = get_kprobe_ctlblk();
+
+ if (!cur)
+ return 0;
+
+ resume_execution(cur, addr, regs);
+
+ /* Rearm the kprobe */
+ arch_arm_kprobe(cur);
+
+ /*
+ * When we return from trap instruction we go to the next instruction
+ * We restored the actual instruction in resume_exectuiont and we to
+ * return to the same address and execute it
+ */
+ regs->ret = addr;
+
+ if ((kcb->kprobe_status != KPROBE_REENTER) && cur->post_handler) {
+ kcb->kprobe_status = KPROBE_HIT_SSDONE;
+ cur->post_handler(cur, regs, 0);
+ }
+
+ if (kcb->kprobe_status == KPROBE_REENTER) {
+ restore_previous_kprobe(kcb);
+ goto out;
+ }
+
+ reset_current_kprobe();
+
+out:
+ preempt_enable_no_resched();
+ return 1;
+}
+
+/*
+ * Fault can be for the instruction being single stepped or for the
+ * pre/post handlers in the module.
+ * This is applicable for applications like user probes, where we have the
+ * probe in user space and the handlers in the kernel
+ */
+
+int __kprobes kprobe_fault_handler(struct pt_regs *regs, unsigned long trapnr)
+{
+ struct kprobe *cur = kprobe_running();
+ struct kprobe_ctlblk *kcb = get_kprobe_ctlblk();
+
+ switch (kcb->kprobe_status) {
+ case KPROBE_HIT_SS:
+ case KPROBE_REENTER:
+ /*
+ * We are here because the instruction being single stepped
+ * caused the fault. We reset the current kprobe and allow the
+ * exception handler as if it is regular exception. In our
+ * case it doesn't matter because the system will be halted
+ */
+ resume_execution(cur, (unsigned long)cur->addr, regs);
+
+ if (kcb->kprobe_status == KPROBE_REENTER)
+ restore_previous_kprobe(kcb);
+ else
+ reset_current_kprobe();
+
+ preempt_enable_no_resched();
+ break;
+
+ case KPROBE_HIT_ACTIVE:
+ case KPROBE_HIT_SSDONE:
+ /*
+ * We are here because the instructions in the pre/post handler
+ * caused the fault.
+ */
+
+ /* We increment the nmissed count for accounting,
+ * we can also use npre/npostfault count for accouting
+ * these specific fault cases.
+ */
+ kprobes_inc_nmissed_count(cur);
+
+ /*
+ * We come here because instructions in the pre/post
+ * handler caused the page_fault, this could happen
+ * if handler tries to access user space by
+ * copy_from_user(), get_user() etc. Let the
+ * user-specified handler try to fix it first.
+ */
+ if (cur->fault_handler && cur->fault_handler(cur, regs, trapnr))
+ return 1;
+
+ /*
+ * In case the user-specified fault handler returned zero,
+ * try to fix up.
+ */
+ if (fixup_exception(regs))
+ return 1;
+
+ /*
+ * fixup_exception() could not handle it,
+ * Let do_page_fault() fix it.
+ */
+ break;
+
+ default:
+ break;
+ }
+ return 0;
+}
+
+int __kprobes kprobe_exceptions_notify(struct notifier_block *self,
+ unsigned long val, void *data)
+{
+ struct die_args *args = data;
+ unsigned long addr = args->err;
+ int ret = NOTIFY_DONE;
+
+ switch (val) {
+ case DIE_IERR:
+ if (arc_kprobe_handler(addr, args->regs))
+ return NOTIFY_STOP;
+ break;
+
+ case DIE_TRAP:
+ if (arc_post_kprobe_handler(addr, args->regs))
+ return NOTIFY_STOP;
+ break;
+
+ default:
+ break;
+ }
+
+ return ret;
+}
+
+int __kprobes setjmp_pre_handler(struct kprobe *p, struct pt_regs *regs)
+{
+ struct jprobe *jp = container_of(p, struct jprobe, kp);
+ struct kprobe_ctlblk *kcb = get_kprobe_ctlblk();
+ unsigned long sp_addr = regs->sp;
+
+ kcb->jprobe_saved_regs = *regs;
+ memcpy(kcb->jprobes_stack, (void *)sp_addr, MIN_STACK_SIZE(sp_addr));
+ regs->ret = (unsigned long)(jp->entry);
+
+ return 1;
+}
+
+void __kprobes jprobe_return(void)
+{
+ __asm__ __volatile__("unimp_s");
+ return;
+}
+
+int __kprobes longjmp_break_handler(struct kprobe *p, struct pt_regs *regs)
+{
+ struct kprobe_ctlblk *kcb = get_kprobe_ctlblk();
+ unsigned long sp_addr;
+
+ *regs = kcb->jprobe_saved_regs;
+ sp_addr = regs->sp;
+ memcpy((void *)sp_addr, kcb->jprobes_stack, MIN_STACK_SIZE(sp_addr));
+ preempt_enable_no_resched();
+
+ return 1;
+}
+
+static void __used kretprobe_trampoline_holder(void)
+{
+ __asm__ __volatile__(".global kretprobe_trampoline\n"
+ "kretprobe_trampoline:\n" "nop\n");
+}
+
+void __kprobes arch_prepare_kretprobe(struct kretprobe_instance *ri,
+ struct pt_regs *regs)
+{
+
+ ri->ret_addr = (kprobe_opcode_t *) regs->blink;
+
+ /* Replace the return addr with trampoline addr */
+ regs->blink = (unsigned long)&kretprobe_trampoline;
+}
+
+static int __kprobes trampoline_probe_handler(struct kprobe *p,
+ struct pt_regs *regs)
+{
+ struct kretprobe_instance *ri = NULL;
+ struct hlist_head *head, empty_rp;
+ struct hlist_node *node, *tmp;
+ unsigned long flags, orig_ret_address = 0;
+ unsigned long trampoline_address = (unsigned long)&kretprobe_trampoline;
+
+ INIT_HLIST_HEAD(&empty_rp);
+ kretprobe_hash_lock(current, &head, &flags);
+
+ /*
+ * It is possible to have multiple instances associated with a given
+ * task either because an multiple functions in the call path
+ * have a return probe installed on them, and/or more than one return
+ * return probe was registered for a target function.
+ *
+ * We can handle this because:
+ * - instances are always inserted at the head of the list
+ * - when multiple return probes are registered for the same
+ * function, the first instance's ret_addr will point to the
+ * real return address, and all the rest will point to
+ * kretprobe_trampoline
+ */
+ hlist_for_each_entry_safe(ri, node, tmp, head, hlist) {
+ if (ri->task != current)
+ /* another task is sharing our hash bucket */
+ continue;
+
+ if (ri->rp && ri->rp->handler)
+ ri->rp->handler(ri, regs);
+
+ orig_ret_address = (unsigned long)ri->ret_addr;
+ recycle_rp_inst(ri, &empty_rp);
+
+ if (orig_ret_address != trampoline_address) {
+ /*
+ * This is the real return address. Any other
+ * instances associated with this task are for
+ * other calls deeper on the call stack
+ */
+ break;
+ }
+ }
+
+ kretprobe_assert(ri, orig_ret_address, trampoline_address);
+ regs->ret = orig_ret_address;
+
+ reset_current_kprobe();
+ kretprobe_hash_unlock(current, &flags);
+ preempt_enable_no_resched();
+
+ hlist_for_each_entry_safe(ri, node, tmp, &empty_rp, hlist) {
+ hlist_del(&ri->hlist);
+ kfree(ri);
+ }
+
+ /* By returning a non zero value, we are telling the kprobe handler
+ * that we don't want the post_handler to run
+ */
+ return 1;
+}
+
+static struct kprobe trampoline_p = {
+ .addr = (kprobe_opcode_t *) &kretprobe_trampoline,
+ .pre_handler = trampoline_probe_handler
+};
+
+int __init arch_init_kprobes(void)
+{
+ /* Registering the trampoline code for the kret probe */
+ return register_kprobe(&trampoline_p);
+}
+
+int __kprobes arch_trampoline_kprobe(struct kprobe *p)
+{
+ if (p->addr == (kprobe_opcode_t *) &kretprobe_trampoline)
+ return 1;
+
+ return 0;
+}
+
+void trap_is_kprobe(unsigned long cause, unsigned long address,
+ struct pt_regs *regs)
+{
+ notify_die(DIE_TRAP, "kprobe_trap", regs, address, cause, SIGTRAP);
+}
diff --git a/arch/arc/kernel/traps.c b/arch/arc/kernel/traps.c
index fd2457c..c6396b4 100644
--- a/arch/arc/kernel/traps.c
+++ b/arch/arc/kernel/traps.c
@@ -15,6 +15,7 @@
#include <linux/uaccess.h>
#include <asm/ptrace.h>
#include <asm/setup.h>
+#include <asm/kprobes.h>

void __init trap_init(void)
{
@@ -90,6 +91,7 @@ void do_machine_check_fault(unsigned long cause, unsigned long address,
die("Machine Check Exception", regs, address, cause);
}

+
/*
* Entry point for traps induced by ARCompact TRAP_S <n> insn
* This is same family as TRAP0/SWI insn (use the same vector).
@@ -109,6 +111,10 @@ void do_non_swi_trap(unsigned long cause, unsigned long address,
trap_is_brkpt(cause, address, regs);
break;

+ case 2:
+ trap_is_kprobe(param, address, regs);
+ break;
+
default:
break;
}
@@ -116,10 +122,17 @@ void do_non_swi_trap(unsigned long cause, unsigned long address,

/*
* Entry point for Instruction Error Exception
+ * -For a corner case, ARC kprobes implementation resorts to using
+ * this exception, hence the check
*/
void do_insterror_or_kprobe(unsigned long cause,
unsigned long address,
struct pt_regs *regs)
{
+ /* Check if this exception is caused by kprobes */
+ if (notify_die(DIE_IERR, "kprobe_ierr", regs, address,
+ cause, SIGILL) == NOTIFY_STOP)
+ return;
+
insterror_is_error(cause, address, regs);
}
--
1.7.4.1

2012-11-12 11:51:41

by Vineet Gupta

[permalink] [raw]
Subject: [RFC Patch v1 45/55] ARC: unaligned access emulation

From: Vineet Gupta <[email protected]>

Signed-off-by: Vineet Gupta <[email protected]>
---
arch/arc/Kconfig | 9 ++
arch/arc/include/asm/Kbuild | 1 -
arch/arc/include/asm/ptrace.h | 3 +
arch/arc/include/asm/unaligned.h | 29 +++++
arch/arc/kernel/Makefile | 3 +-
arch/arc/kernel/disasm.c | 2 +-
arch/arc/kernel/entry.S | 13 ++
arch/arc/kernel/traps.c | 26 +++++
arch/arc/kernel/unaligned.c | 225 ++++++++++++++++++++++++++++++++++++++
9 files changed, 308 insertions(+), 3 deletions(-)
create mode 100644 arch/arc/include/asm/unaligned.h
create mode 100644 arch/arc/kernel/unaligned.c

diff --git a/arch/arc/Kconfig b/arch/arc/Kconfig
index 3ef21be..81d78fc 100644
--- a/arch/arc/Kconfig
+++ b/arch/arc/Kconfig
@@ -343,6 +343,15 @@ config ARC_CURR_IN_REG
This reserved Register R25 to point to Current Task in
kernel mode. This saves memory access for each such access

+
+config ARC_MISALIGN_ACCESS
+ bool "Emulate unaligned memory access (userspace only)"
+ default N
+ help
+ This enables misaligned 16 & 32 bit memory access from user space.
+ Use ONLY-IF-ABS-NECESSARY as it will be very slow and also can hide
+ potential bugs in code
+
config ARC_STACK_NONEXEC
bool "Make stack non-executable"
default n
diff --git a/arch/arc/include/asm/Kbuild b/arch/arc/include/asm/Kbuild
index a246390..06ca702 100644
--- a/arch/arc/include/asm/Kbuild
+++ b/arch/arc/include/asm/Kbuild
@@ -51,7 +51,6 @@ generic-y += termios.h
generic-y += topology.h
generic-y += types.h
generic-y += ucontext.h
-generic-y += unaligned.h
generic-y += user.h
generic-y += vga.h
generic-y += xor.h
diff --git a/arch/arc/include/asm/ptrace.h b/arch/arc/include/asm/ptrace.h
index bb621e8..2843dd4 100644
--- a/arch/arc/include/asm/ptrace.h
+++ b/arch/arc/include/asm/ptrace.h
@@ -117,6 +117,9 @@ struct user_regs_struct {
/* return 1 if user mode or 0 if kernel mode */
#define user_mode(regs) (regs->status32 & STATUS_U_MASK)

+/* return 1 if PC in delay slot */
+#define delay_mode(regs) ((regs->status32 & STATUS_DE_MASK) == STATUS_DE_MASK)
+
/* return 1 if in syscall, 0 if Intr or Exception */
#define in_syscall(regs) (((regs->orig_r8) >= 0 && \
(regs->orig_r8 <= NR_syscalls)) ? 1 : 0)
diff --git a/arch/arc/include/asm/unaligned.h b/arch/arc/include/asm/unaligned.h
new file mode 100644
index 0000000..5dbe63f
--- /dev/null
+++ b/arch/arc/include/asm/unaligned.h
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2004, 2007-2010, 2011-2012 Synopsys, Inc. (http://www.synopsys.com)
+ *
+ * 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.
+ */
+
+#ifndef _ASM_ARC_UNALIGNED_H
+#define _ASM_ARC_UNALIGNED_H
+
+/* ARC700 can't handle unaligned Data accesses. */
+
+#include <asm-generic/unaligned.h>
+#include <asm/ptrace.h>
+
+#ifdef CONFIG_ARC_MISALIGN_ACCESS
+int misaligned_fixup(unsigned long address, struct pt_regs *regs,
+ unsigned long cause, struct callee_regs *cregs);
+#else
+static inline int
+misaligned_fixup(unsigned long address, struct pt_regs *regs,
+ unsigned long cause, struct callee_regs *cregs)
+{
+ return 0;
+}
+#endif
+
+#endif /* _ASM_ARC_UNALIGNED_H */
diff --git a/arch/arc/kernel/Makefile b/arch/arc/kernel/Makefile
index 442d024..52ef6ce 100644
--- a/arch/arc/kernel/Makefile
+++ b/arch/arc/kernel/Makefile
@@ -9,13 +9,14 @@
CFLAGS_ptrace.o += -DUTS_MACHINE='"$(UTS_MACHINE)"'

obj-y := arcksyms.o setup.o irq.o time.o reset.o ptrace.o entry.o process.o \
- signal.o traps.o sys.o troubleshoot.o stacktrace.o disasm.c
+ signal.o traps.o sys.o troubleshoot.o stacktrace.o disasm.o

obj-$(CONFIG_MODULES) += arcksyms.o module.o
obj-$(CONFIG_ARC_DBG_EVENT_TIMELINE) += event-log.o
obj-$(CONFIG_SMP) += smp.o
obj-$(CONFIG_ARC_DW2_UNWIND) += unwind.o
obj-$(CONFIG_KPROBES) += kprobes.o
+obj-$(CONFIG_ARC_MISALIGN_ACCESS) += unaligned.o
obj-$(CONFIG_ARC_FPU_SAVE_RESTORE) += fpu.o
CFLAGS_fpu.o += -mdpfp

diff --git a/arch/arc/kernel/disasm.c b/arch/arc/kernel/disasm.c
index 51bad8f..2f39028 100644
--- a/arch/arc/kernel/disasm.c
+++ b/arch/arc/kernel/disasm.c
@@ -15,7 +15,7 @@
#include <asm/disasm.h>
#include <asm/uaccess.h>

-#if defined(CONFIG_KGDB) || defined(CONFIG_MISALIGN_ACCESS) || \
+#if defined(CONFIG_KGDB) || defined(CONFIG_ARC_MISALIGN_ACCESS) || \
defined(CONFIG_KPROBES)

/* disasm_instr: Analyses instruction at addr, stores
diff --git a/arch/arc/kernel/entry.S b/arch/arc/kernel/entry.S
index 13d6feb..2ff15e0 100644
--- a/arch/arc/kernel/entry.S
+++ b/arch/arc/kernel/entry.S
@@ -12,6 +12,9 @@
* needed before task_pt_regs was invented. This saves a branch per call
* and more importantly allows clean invocation of post-syscall ptrace hook
*
+ * vineetg: May 2011
+ * -Userspace unaligned access emulation
+ *
* vineetg: Feb 2011 (ptrace low level code fixes)
* -traced syscall return code (r0) was not saved into pt_regs for restoring
* into user reg-file when traded task rets to user space.
@@ -404,7 +407,17 @@ ARC_ENTRY EV_TLBProtV
mov r1, r4 ; faulting address
mov r2, sp ; pt_regs

+#ifdef CONFIG_ARC_MISALIGN_ACCESS
+ SAVE_CALLEE_SAVED_USER
+ mov r3, sp ; callee_regs
+#endif
+
bl do_misaligned_access
+
+#ifdef CONFIG_ARC_MISALIGN_ACCESS
+ DISCARD_CALLEE_SAVED_USER
+#endif
+
b ret_from_exception

ARC_EXIT EV_TLBProtV
diff --git a/arch/arc/kernel/traps.c b/arch/arc/kernel/traps.c
index c6396b4..d2bc5bd 100644
--- a/arch/arc/kernel/traps.c
+++ b/arch/arc/kernel/traps.c
@@ -7,6 +7,9 @@
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
+ * vineetg: May 2011
+ * -user-space unaligned access emulation
+ *
* Rahul Trivedi: Codito Technologies 2004
*/

@@ -16,6 +19,7 @@
#include <asm/ptrace.h>
#include <asm/setup.h>
#include <asm/kprobes.h>
+#include <asm/unaligned.h>

void __init trap_init(void)
{
@@ -79,7 +83,29 @@ DO_ERROR_INFO(SIGILL, "Illegal Insn (or Seq)", insterror_is_error, ILL_ILLOPC)
DO_ERROR_INFO(SIGBUS, "Invalid Mem Access", do_memory_error, BUS_ADRERR)
DO_ERROR_INFO(SIGTRAP, "Breakpoint Set", trap_is_brkpt, TRAP_BRKPT)

+#ifdef CONFIG_ARC_MISALIGN_ACCESS
+/*
+ * Entry Point for Misaligned Data access Exception, for emulating in software
+ */
+int do_misaligned_access(unsigned long cause, unsigned long address,
+ struct pt_regs *regs, struct callee_regs *cregs)
+{
+ if (misaligned_fixup(address, regs, cause, cregs) != 0) {
+ siginfo_t info;
+
+ info.si_signo = SIGSEGV;
+ info.si_errno = 0;
+ info.si_code = SEGV_ACCERR;
+ info.si_addr = (void __user *)address;
+ return handle_exception(cause, "Misaligned Access", regs,
+ &info);
+ }
+ return 0;
+}
+
+#else
DO_ERROR_INFO(SIGSEGV, "Misaligned Access", do_misaligned_access, SEGV_ACCERR)
+#endif

/*
* Entry point for miscll errors such as Nested Exceptions
diff --git a/arch/arc/kernel/unaligned.c b/arch/arc/kernel/unaligned.c
new file mode 100644
index 0000000..b41e218
--- /dev/null
+++ b/arch/arc/kernel/unaligned.c
@@ -0,0 +1,225 @@
+/*
+ * Copyright (C) 2011-2012 Synopsys (http://www.synopsys.com)
+ *
+ * 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.
+ *
+ * vineetg : May 2011
+ * -Adapted (from .26 to .35)
+ * -original contribution by [email protected]
+ *
+ */
+
+#include <linux/types.h>
+#include <linux/ptrace.h>
+#include <linux/uaccess.h>
+#include <asm/disasm.h>
+
+#define __get8_unaligned_check(val, addr, err) \
+ __asm__( \
+ "1: ldb.ab %1, [%2, 1]\n" \
+ "2:\n" \
+ " .section .fixup,\"ax\"\n" \
+ " .align 4\n" \
+ "3: mov %0, 1\n" \
+ " b 2b\n" \
+ " .previous\n" \
+ " .section __ex_table,\"a\"\n" \
+ " .align 4\n" \
+ " .long 1b, 3b\n" \
+ " .previous\n" \
+ : "=r" (err), "=&r" (val), "=r" (addr) \
+ : "0" (err), "2" (addr))
+
+#define get16_unaligned_check(val, addr) \
+ do { \
+ unsigned int err = 0, v, a = addr; \
+ __get8_unaligned_check(v, a, err); \
+ val = v ; \
+ __get8_unaligned_check(v, a, err); \
+ val |= v << 8; \
+ if (err) \
+ goto fault; \
+ } while (0)
+
+#define get32_unaligned_check(val, addr) \
+ do { \
+ unsigned int err = 0, v, a = addr; \
+ __get8_unaligned_check(v, a, err); \
+ val = v << 0; \
+ __get8_unaligned_check(v, a, err); \
+ val |= v << 8; \
+ __get8_unaligned_check(v, a, err); \
+ val |= v << 16; \
+ __get8_unaligned_check(v, a, err); \
+ val |= v << 24; \
+ if (err) \
+ goto fault; \
+ } while (0)
+
+#define put16_unaligned_check(val, addr) \
+ do { \
+ unsigned int err = 0, v = val, a = addr;\
+ \
+ __asm__( \
+ "1: stb.ab %1, [%2, 1]\n" \
+ " lsr %1, %1, 8\n" \
+ "2: stb %1, [%2]\n" \
+ "3:\n" \
+ " .section .fixup,\"ax\"\n" \
+ " .align 4\n" \
+ "4: mov %0, 1\n" \
+ " b 3b\n" \
+ " .previous\n" \
+ " .section __ex_table,\"a\"\n" \
+ " .align 4\n" \
+ " .long 1b, 4b\n" \
+ " .long 2b, 4b\n" \
+ " .previous\n" \
+ : "=r" (err), "=&r" (v), "=&r" (a) \
+ : "0" (err), "1" (v), "2" (a)); \
+ \
+ if (err) \
+ goto fault; \
+ } while (0)
+
+#define put32_unaligned_check(val, addr) \
+ do { \
+ unsigned int err = 0, v = val, a = addr;\
+ __asm__( \
+ \
+ "1: stb.ab %1, [%2, 1]\n" \
+ " lsr %1, %1, 8\n" \
+ "2: stb.ab %1, [%2, 1]\n" \
+ " lsr %1, %1, 8\n" \
+ "3: stb.ab %1, [%2, 1]\n" \
+ " lsr %1, %1, 8\n" \
+ "4: stb %1, [%2]\n" \
+ "5:\n" \
+ " .section .fixup,\"ax\"\n" \
+ " .align 4\n" \
+ "6: mov %0, 1\n" \
+ " b 5b\n" \
+ " .previous\n" \
+ " .section __ex_table,\"a\"\n" \
+ " .align 4\n" \
+ " .long 1b, 6b\n" \
+ " .long 2b, 6b\n" \
+ " .long 3b, 6b\n" \
+ " .long 4b, 6b\n" \
+ " .previous\n" \
+ : "=r" (err), "=&r" (v), "=&r" (a) \
+ : "0" (err), "1" (v), "2" (a)); \
+ \
+ if (err) \
+ goto fault; \
+ } while (0)
+
+static void fixup_load(struct disasm_state *state, struct pt_regs *regs,
+ struct callee_regs *cregs)
+{
+ int val;
+
+ /* register write back */
+ if ((state->aa == 1) || (state->aa == 2)) {
+ set_reg(state->wb_reg, state->src1 + state->src2, regs,
+ cregs);
+
+ if (state->aa == 2)
+ state->src2 = 0;
+ }
+
+ if (state->zz == 0) {
+ get32_unaligned_check(val, state->src1 + state->src2);
+ } else {
+ get16_unaligned_check(val, state->src1 + state->src2);
+
+ if (state->x)
+ val = (val << 16) >> 16;
+ }
+
+ if (state->pref == 0)
+ set_reg(state->dest, val, regs, cregs);
+
+ return;
+
+fault: state->fault = 1;
+}
+
+static void fixup_store(struct disasm_state *state, struct pt_regs *regs,
+ struct callee_regs *cregs)
+{
+ /* register write back */
+ if ((state->aa == 1) || (state->aa == 2)) {
+ set_reg(state->wb_reg, state->src2 + state->src3, regs, cregs);
+
+ if (state->aa == 3)
+ state->src3 = 0;
+ } else if (state->aa == 3) {
+ if (state->zz == 2) {
+ set_reg(state->wb_reg, state->src2 +
+ (state->src3 << 1), regs, cregs);
+ } else if (!state->zz) {
+ set_reg(state->wb_reg, state->src2 +
+ (state->src3 << 2), regs, cregs);
+ } else {
+ goto fault;
+ }
+ }
+
+ /* write fix-up */
+ if (!state->zz) {
+ put32_unaligned_check(state->src1, state->src2 +
+ state->src3);
+ } else {
+ put16_unaligned_check(state->src1, state->src2 +
+ state->src3);
+ }
+
+ return;
+
+fault: state->fault = 1;
+}
+
+int misaligned_fixup(unsigned long address, struct pt_regs *regs,
+ unsigned long cause, struct callee_regs *cregs)
+{
+ struct disasm_state state;
+
+ /* handle user mode only */
+ if (!user_mode(regs))
+ return 1;
+
+ disasm_instr(regs->ret, &state, 1, regs, cregs);
+
+ if (state.fault)
+ goto fault;
+
+ /* ldb/stb should not have unaligned exception */
+ if ((state.zz == 1) || (state.di))
+ goto fault;
+
+ if (!state.write)
+ fixup_load(&state, regs, cregs);
+ else
+ fixup_store(&state, regs, cregs);
+
+ if (state.fault)
+ goto fault;
+
+ if (delay_mode(regs)) {
+ regs->ret = regs->bta;
+ regs->status32 &= ~STATUS_DE_MASK;
+ } else {
+ regs->ret += state.instr_len;
+ }
+
+ return 0;
+
+fault:
+ pr_err("Alignment trap: fault in fix-up %08lx at [<%08lx>]\n",
+ state.words[0], address);
+
+ return 1;
+}
--
1.7.4.1

2012-11-12 11:51:57

by Vineet Gupta

[permalink] [raw]
Subject: [RFC Patch v1 47/55] ARC: startup #2: Verbose Boot reporting / feature verification

From: Vineet Gupta <[email protected]>

Signed-off-by: Vineet Gupta <[email protected]>
---
arch/arc/Makefile | 2 +
arch/arc/include/asm/arcregs.h | 122 ++++++++++++++++++++--
arch/arc/include/asm/defines.h | 56 ++++++++++
arch/arc/include/asm/setup.h | 14 +++
arch/arc/kernel/setup.c | 223 +++++++++++++++++++++++++++++++++++++++-
arch/arc/mm/cache_arc700.c | 46 ++++++++
arch/arc/mm/tlb.c | 38 +++++++
7 files changed, 490 insertions(+), 11 deletions(-)
create mode 100644 arch/arc/include/asm/defines.h

diff --git a/arch/arc/Makefile b/arch/arc/Makefile
index a86e284..3de315e 100644
--- a/arch/arc/Makefile
+++ b/arch/arc/Makefile
@@ -20,6 +20,8 @@ export PLATFORM
cflags-y += -Iarch/arc/plat-$(PLATFORM)/include
cflags-y += -mA7 -fno-common -pipe -fno-builtin -D__linux__

+LINUXINCLUDE += -include ${src}/arch/arc/include/asm/defines.h
+
ifdef CONFIG_ARC_CURR_IN_REG
# For a global register defintion, make sure it gets passed to every file
# We had a customer reported bug where some code built in kernel was NOT using
diff --git a/arch/arc/include/asm/arcregs.h b/arch/arc/include/asm/arcregs.h
index 725b94c..bd126c9 100644
--- a/arch/arc/include/asm/arcregs.h
+++ b/arch/arc/include/asm/arcregs.h
@@ -12,8 +12,26 @@
#ifdef __KERNEL__

/* Build Configuration Registers */
+#define ARC_REG_DCCMBASE_BCR 0x61 /* DCCM Base Addr */
+#define ARC_REG_CRC_BCR 0x62
+#define ARC_REG_DVFB_BCR 0x64
+#define ARC_REG_EXTARITH_BCR 0x65
#define ARC_REG_VECBASE_BCR 0x68
+#define ARC_REG_PERIBASE_BCR 0x69
+#define ARC_REG_FP_BCR 0x6B /* Single-Precision FPU */
+#define ARC_REG_DPFP_BCR 0x6C /* Dbl Precision FPU */
#define ARC_REG_MMU_BCR 0x6f
+#define ARC_REG_DCCM_BCR 0x74 /* DCCM Present + SZ */
+#define ARC_REG_TIMERS_BCR 0x75
+#define ARC_REG_ICCM_BCR 0x78
+#define ARC_REG_XY_MEM_BCR 0x79
+#define ARC_REG_MAC_BCR 0x7a
+#define ARC_REG_MUL_BCR 0x7b
+#define ARC_REG_SWAP_BCR 0x7c
+#define ARC_REG_NORM_BCR 0x7d
+#define ARC_REG_MIXMAX_BCR 0x7e
+#define ARC_REG_BARREL_BCR 0x7f
+#define ARC_REG_D_UNCACH_BCR 0x6A

/* status32 Bits Positions */
#define STATUS_H_BIT 0 /* CPU Halted */
@@ -87,16 +105,6 @@
#define TIMER_CTRL_IE (1 << 0) /* Interupt when Count reachs limit */
#define TIMER_CTRL_NH (1 << 1) /* Count only when CPU NOT halted */

-#if defined(CONFIG_ARC_MMU_V1)
-#define CONFIG_ARC_MMU_VER 1
-#elif defined(CONFIG_ARC_MMU_V2)
-#define CONFIG_ARC_MMU_VER 2
-#elif defined(CONFIG_ARC_MMU_V3)
-#define CONFIG_ARC_MMU_VER 3
-#else
-#error "Error: MMU ver"
-#endif
-
/* MMU Management regs */
#define ARC_REG_TLBPD0 0x405
#define ARC_REG_TLBPD1 0x406
@@ -276,6 +284,13 @@ struct arc_fpu {
***************************************************************
* Build Configuration Registers, with encoded hardware config
*/
+struct bcr_identity {
+#ifdef CONFIG_CPU_BIG_ENDIAN
+ unsigned int chip_id:16, cpu_id:8, family:8;
+#else
+ unsigned int family:8, cpu_id:8, chip_id:16;
+#endif
+};

struct bcr_mmu_1_2 {
#ifdef CONFIG_CPU_BIG_ENDIAN
@@ -295,6 +310,38 @@ struct bcr_mmu_3 {
#endif
};

+#define EXTN_SWAP_VALID 0x1
+#define EXTN_NORM_VALID 0x2
+#define EXTN_MINMAX_VALID 0x2
+#define EXTN_BARREL_VALID 0x2
+
+struct bcr_extn {
+#ifdef CONFIG_CPU_BIG_ENDIAN
+ unsigned int pad:20, crc:1, ext_arith:2, mul:2, barrel:2, minmax:2,
+ norm:2, swap:1;
+#else
+ unsigned int swap:1, norm:2, minmax:2, barrel:2, mul:2, ext_arith:2,
+ crc:1, pad:20;
+#endif
+};
+
+/* DSP Options Ref Manual */
+struct bcr_extn_mac_mul {
+#ifdef CONFIG_CPU_BIG_ENDIAN
+ unsigned int pad:16, type:8, ver:8;
+#else
+ unsigned int ver:8, type:8, pad:16;
+#endif
+};
+
+struct bcr_extn_xymem {
+#ifdef CONFIG_CPU_BIG_ENDIAN
+ unsigned int ram_org:2, num_banks:4, bank_sz:4, ver:8;
+#else
+ unsigned int ver:8, bank_sz:4, num_banks:4, ram_org:2;
+#endif
+};
+
struct bcr_cache {
#ifdef CONFIG_CPU_BIG_ENDIAN
unsigned int pad:12, line_len:4, sz:4, config:4, ver:8;
@@ -303,6 +350,48 @@ struct bcr_cache {
#endif
};

+struct bcr_perip {
+#ifdef CONFIG_CPU_BIG_ENDIAN
+ unsigned int start:8, pad2:8, sz:8, pad:8;
+#else
+ unsigned int pad:8, sz:8, pad2:8, start:8;
+#endif
+};
+struct bcr_iccm {
+#ifdef CONFIG_CPU_BIG_ENDIAN
+ unsigned int base:16, pad:5, sz:3, ver:8;
+#else
+ unsigned int ver:8, sz:3, pad:5, base:16;
+#endif
+};
+
+/* DCCM Base Address Register: ARC_REG_DCCMBASE_BCR */
+struct bcr_dccm_base {
+#ifdef CONFIG_CPU_BIG_ENDIAN
+ unsigned int addr:24, ver:8;
+#else
+ unsigned int ver:8, addr:24;
+#endif
+};
+
+/* DCCM RAM Configuration Register: ARC_REG_DCCM_BCR */
+struct bcr_dccm {
+#ifdef CONFIG_CPU_BIG_ENDIAN
+ unsigned int res:21, sz:3, ver:8;
+#else
+ unsigned int ver:8, sz:3, res:21;
+#endif
+};
+
+/* Both SP and DP FPU BCRs have same format */
+struct bcr_fp {
+#ifdef CONFIG_CPU_BIG_ENDIAN
+ unsigned int fast:1, ver:8;
+#else
+ unsigned int ver:8, fast:1;
+#endif
+};
+
/*
*******************************************************************
* Generic structures to hold build configuration used at runtime
@@ -316,9 +405,22 @@ struct cpuinfo_arc_cache {
unsigned int has_aliasing, sz, line_len, assoc, ver;
};

+struct cpuinfo_arc_ccm {
+ unsigned int base_addr, sz;
+};
+
struct cpuinfo_arc {
struct cpuinfo_arc_cache icache, dcache;
struct cpuinfo_arc_mmu mmu;
+ struct bcr_identity core;
+ unsigned int timers;
+ unsigned int vec_base;
+ unsigned int uncached_base;
+ struct cpuinfo_arc_ccm iccm, dccm;
+ struct bcr_extn extn;
+ struct bcr_extn_xymem extn_xymem;
+ struct bcr_extn_mac_mul extn_mac_mul;
+ struct bcr_fp fp, dpfp;
};

extern struct cpuinfo_arc cpuinfo_arc700[];
diff --git a/arch/arc/include/asm/defines.h b/arch/arc/include/asm/defines.h
new file mode 100644
index 0000000..6097bb4
--- /dev/null
+++ b/arch/arc/include/asm/defines.h
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2004, 2007-2010, 2011-2012 Synopsys, Inc. (http://www.synopsys.com)
+ *
+ * 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.
+ */
+
+#ifndef __ARC_ASM_DEFINES_H__
+#define __ARC_ASM_DEFINES_H__
+
+#if defined(CONFIG_ARC_MMU_V1)
+#define CONFIG_ARC_MMU_VER 1
+#elif defined(CONFIG_ARC_MMU_V2)
+#define CONFIG_ARC_MMU_VER 2
+#elif defined(CONFIG_ARC_MMU_V3)
+#define CONFIG_ARC_MMU_VER 3
+#endif
+
+#ifdef CONFIG_ARC_HAS_LLSC
+#define __CONFIG_ARC_HAS_LLSC_VAL 1
+#else
+#define __CONFIG_ARC_HAS_LLSC_VAL 0
+#endif
+
+#ifdef CONFIG_ARC_HAS_SWAPE
+#define __CONFIG_ARC_HAS_SWAPE_VAL 1
+#else
+#define __CONFIG_ARC_HAS_SWAPE_VAL 0
+#endif
+
+#ifdef CONFIG_ARC_HAS_RTSC
+#define __CONFIG_ARC_HAS_RTSC_VAL 1
+#else
+#define __CONFIG_ARC_HAS_RTSC_VAL 0
+#endif
+
+#ifdef CONFIG_ARC_MMU_SASID
+#define __CONFIG_ARC_MMU_SASID_VAL 1
+#else
+#define __CONFIG_ARC_MMU_SASID_VAL 0
+#endif
+
+#ifdef CONFIG_ARC_HAS_ICACHE
+#define __CONFIG_ARC_HAS_ICACHE 1
+#else
+#define __CONFIG_ARC_HAS_ICACHE 0
+#endif
+
+#ifdef CONFIG_ARC_HAS_DCACHE
+#define __CONFIG_ARC_HAS_DCACHE 1
+#else
+#define __CONFIG_ARC_HAS_DCACHE 0
+#endif
+
+#endif /* __ARC_ASM_DEFINES_H__ */
diff --git a/arch/arc/include/asm/setup.h b/arch/arc/include/asm/setup.h
index ab427e6..fc97411 100644
--- a/arch/arc/include/asm/setup.h
+++ b/arch/arc/include/asm/setup.h
@@ -13,6 +13,20 @@

#define COMMAND_LINE_SIZE 256

+/*
+ * Data structure to map a ID to string
+ * Used a lot for bootup reporting of hardware diversity
+ */
+struct id_to_str {
+ int id;
+ const char *str;
+};
+
+struct cpuinfo_data {
+ struct id_to_str info;
+ int up_range;
+};
+
extern int root_mountflags, end_mem;
extern int running_on_hw;

diff --git a/arch/arc/kernel/setup.c b/arch/arc/kernel/setup.c
index 34a2ff5..d470aa1 100644
--- a/arch/arc/kernel/setup.c
+++ b/arch/arc/kernel/setup.c
@@ -13,6 +13,7 @@
#include <linux/console.h>
#include <linux/module.h>
#include <linux/cpu.h>
+#include <asm/sections.h>
#include <asm/arcregs.h>
#include <asm/tlb.h>
#include <asm/cache.h>
@@ -32,10 +33,205 @@ struct task_struct *_current_task[NR_CPUS]; /* For stack switching */

struct cpuinfo_arc cpuinfo_arc700[NR_CPUS];

+
void __init read_arc_build_cfg_regs(void)
{
+ struct bcr_perip uncached_space;
+ struct cpuinfo_arc *cpu = &cpuinfo_arc700[smp_processor_id()];
+ FIX_PTR(cpu);
+
+ READ_BCR(AUX_IDENTITY, cpu->core);
+
+ cpu->timers = read_aux_reg(ARC_REG_TIMERS_BCR);
+
+ cpu->vec_base = read_aux_reg(AUX_INTR_VEC_BASE);
+ if (cpu->vec_base == 0)
+ cpu->vec_base = (unsigned int)_int_vec_base_lds;
+
+ READ_BCR(ARC_REG_D_UNCACH_BCR, uncached_space);
+ cpu->uncached_base = uncached_space.start << 24;
+
+ cpu->extn.mul = read_aux_reg(ARC_REG_MUL_BCR);
+ cpu->extn.swap = read_aux_reg(ARC_REG_SWAP_BCR);
+ cpu->extn.norm = read_aux_reg(ARC_REG_NORM_BCR);
+ cpu->extn.minmax = read_aux_reg(ARC_REG_MIXMAX_BCR);
+ cpu->extn.barrel = read_aux_reg(ARC_REG_BARREL_BCR);
+ READ_BCR(ARC_REG_MAC_BCR, cpu->extn_mac_mul);
+
+ cpu->extn.ext_arith = read_aux_reg(ARC_REG_EXTARITH_BCR);
+ cpu->extn.crc = read_aux_reg(ARC_REG_CRC_BCR);
+
+ READ_BCR(ARC_REG_XY_MEM_BCR, cpu->extn_xymem);
+
read_decode_mmu_bcr();
read_decode_cache_bcr();
+
+ READ_BCR(ARC_REG_FP_BCR, cpu->fp);
+ READ_BCR(ARC_REG_DPFP_BCR, cpu->dpfp);
+}
+
+static const struct cpuinfo_data arc_cpu_tbl[] = {
+ { {0x10, "ARCTangent A5"}, 0x1F},
+ { {0x20, "ARC 600" }, 0x2F},
+ { {0x30, "ARC 700" }, 0x33},
+ { {0x34, "ARC 700 R4.10"}, 0x34},
+ { {0x00, NULL } }
+};
+
+char *arc_cpu_mumbojumbo(int cpu_id, char *buf, int len)
+{
+ int n = 0;
+ struct cpuinfo_arc *cpu = &cpuinfo_arc700[cpu_id];
+ struct bcr_identity *core = &cpu->core;
+ const struct cpuinfo_data *tbl;
+ int be = 0;
+#ifdef CONFIG_CPU_BIG_ENDIAN
+ be = 1;
+#endif
+ FIX_PTR(cpu);
+
+ n += scnprintf(buf + n, len - n,
+ "\nARC IDENTITY\t: Family [%#02x]"
+ " Cpu-id [%#02x] Chip-id [%#4x]\n",
+ core->family, core->cpu_id,
+ core->chip_id);
+
+ for (tbl = &arc_cpu_tbl[0]; tbl->info.id != 0; tbl++) {
+ if ((core->family >= tbl->info.id) &&
+ (core->family <= tbl->up_range)) {
+ n += scnprintf(buf + n, len - n,
+ "processor\t: %s %s\n",
+ tbl->info.str,
+ be ? "[Big Endian]" : "");
+ break;
+ }
+ }
+
+ if (tbl->info.id == 0)
+ n += scnprintf(buf + n, len - n, "UNKNOWN ARC Processor\n");
+
+ n += scnprintf(buf + n, len - n, "CPU speed\t: %u.%02u Mhz\n",
+ (unsigned int)(CONFIG_ARC_PLAT_CLK / 1000000),
+ (unsigned int)(CONFIG_ARC_PLAT_CLK / 10000) % 100);
+
+ n += scnprintf(buf + n, len - n, "Timers\t\t: %s %s\n",
+ (cpu->timers & 0x200) ? "TIMER1" : "",
+ (cpu->timers & 0x100) ? "TIMER0" : "");
+
+ n += scnprintf(buf + n, len - n, "Vect Tbl Base\t: %#x\n",
+ cpu->vec_base);
+
+ n += scnprintf(buf + n, len - n, "UNCACHED Base\t: %#x\n",
+ cpu->uncached_base);
+
+ return buf;
+}
+
+static const struct id_to_str mul_type_nm[] = {
+ { 0x0, "N/A"},
+ { 0x1, "32x32 (spl Result Reg)" },
+ { 0x2, "32x32 (ANY Result Reg)" }
+};
+
+static const struct id_to_str mac_mul_nm[] = {
+ {0x0, "N/A"},
+ {0x1, "N/A"},
+ {0x2, "Dual 16 x 16"},
+ {0x3, "N/A"},
+ {0x4, "32x16"},
+ {0x5, "N/A"},
+ {0x6, "Dual 16x16 and 32x16"}
+};
+
+char *arc_extn_mumbojumbo(int cpu_id, char *buf, int len)
+{
+ int n = 0;
+ struct cpuinfo_arc *cpu = &cpuinfo_arc700[cpu_id];
+
+ FIX_PTR(cpu);
+#define IS_AVAIL1(var, str) ((var) ? str : "")
+#define IS_AVAIL2(var, str) ((var == 0x2) ? str : "")
+#define IS_USED(var) ((var) ? "(in-use)" : "(not used)")
+
+ n += scnprintf(buf + n, len - n,
+ "Extn [700-Base]\t: %s %s %s %s %s %s\n",
+ IS_AVAIL2(cpu->extn.norm, "norm,"),
+ IS_AVAIL2(cpu->extn.barrel, "barrel-shift,"),
+ IS_AVAIL1(cpu->extn.swap, "swap,"),
+ IS_AVAIL2(cpu->extn.minmax, "minmax,"),
+ IS_AVAIL1(cpu->extn.crc, "crc,"),
+ IS_AVAIL2(cpu->extn.ext_arith, "ext-arith"));
+
+ n += scnprintf(buf + n, len - n, "Extn [700-MPY]\t: %s",
+ mul_type_nm[cpu->extn.mul].str);
+
+ n += scnprintf(buf + n, len - n, " MAC MPY: %s\n",
+ mac_mul_nm[cpu->extn_mac_mul.type].str);
+
+ if (cpu->core.family == 0x34) {
+ n += scnprintf(buf + n, len - n,
+ "Extn [700-4.10]\t: LLOCK/SCOND %s, SWAPE %s, RTSC %s\n",
+ IS_USED(__CONFIG_ARC_HAS_LLSC_VAL),
+ IS_USED(__CONFIG_ARC_HAS_SWAPE_VAL),
+ IS_USED(__CONFIG_ARC_HAS_RTSC_VAL));
+ }
+
+ n += scnprintf(buf + n, len - n, "Extn [CCM]\t: %s",
+ !(cpu->dccm.sz || cpu->iccm.sz) ? "N/A" : "");
+
+ if (cpu->dccm.sz)
+ n += scnprintf(buf + n, len - n, "DCCM: @ %x, %d KB ",
+ cpu->dccm.base_addr, TO_KB(cpu->dccm.sz));
+
+ if (cpu->iccm.sz)
+ n += scnprintf(buf + n, len - n, "ICCM: @ %x, %d KB",
+ cpu->iccm.base_addr, TO_KB(cpu->iccm.sz));
+
+ n += scnprintf(buf + n, len - n, "\nExtn [FPU]\t: %s",
+ !(cpu->fp.ver || cpu->dpfp.ver) ? "N/A" : "");
+
+ if (cpu->fp.ver)
+ n += scnprintf(buf + n, len - n, "SP [v%d] %s",
+ cpu->fp.ver, cpu->fp.fast ? "(fast)" : "");
+
+ if (cpu->dpfp.ver)
+ n += scnprintf(buf + n, len - n, "DP [v%d] %s",
+ cpu->dpfp.ver, cpu->dpfp.fast ? "(fast)" : "");
+
+ n += scnprintf(buf + n, len - n, "\n");
+
+#ifdef _ASM_GENERIC_UNISTD_H
+ n += scnprintf(buf + n, len - n,
+ "OS ABI [v2]\t: asm-generic/{unistd,stat,fcntl}\n");
+#endif
+
+ return buf;
+}
+
+/*
+ * Ensure that FP hardware and kernel config match
+ * -If hardware contains DPFP, kernel needs to save/restore FPU state
+ * across context switches
+ * -If hardware lacks DPFP, but kernel configured to save FPU state then
+ * kernel trying to access non-existant DPFP regs will crash
+ *
+ * We only check for Dbl precision Floating Point, because only DPFP
+ * hardware has dedicated regs which need to be saved/restored on ctx-sw
+ * (Single Precision uses core regs), thus kernel is kind of oblivious to it
+ */
+void __init arc_chk_fpu(void)
+{
+ struct cpuinfo_arc *cpu = &cpuinfo_arc700[smp_processor_id()];
+
+ if (cpu->dpfp.ver) {
+#ifndef CONFIG_ARC_FPU_SAVE_RESTORE
+ pr_warn("DPFP support broken in this kernel...\n");
+#endif
+ } else {
+#ifdef CONFIG_ARC_FPU_SAVE_RESTORE
+ panic("H/w lacks DPFP support, apps won't work\n");
+#endif
+ }
}

/*
@@ -46,10 +242,25 @@ void __init read_arc_build_cfg_regs(void)

void __init setup_processor(void)
{
+ char str[512];
+ int cpu_id = smp_processor_id();
+
read_arc_build_cfg_regs();
arc_init_IRQ();
+
+ printk(arc_cpu_mumbojumbo(cpu_id, str, sizeof(str)));
+
arc_mmu_init();
arc_cache_init();
+
+
+ printk(arc_extn_mumbojumbo(cpu_id, str, sizeof(str)));
+
+#ifdef CONFIG_SMP
+ printk(arc_platform_smp_cpuinfo());
+#endif
+
+ arc_chk_fpu();
}

void __init __attribute__((weak)) arc_platform_early_init(void)
@@ -117,12 +328,22 @@ static int show_cpuinfo(struct seq_file *m, void *v)
if (!str)
goto done;

- seq_printf(m, "ARC700 #%d\n", cpu_id);
+ seq_printf(m, arc_cpu_mumbojumbo(cpu_id, str, PAGE_SIZE));

seq_printf(m, "Bogo MIPS : \t%lu.%02lu\n",
loops_per_jiffy / (500000 / HZ),
(loops_per_jiffy / (5000 / HZ)) % 100);

+ seq_printf(m, arc_mmu_mumbojumbo(cpu_id, str, PAGE_SIZE));
+
+ seq_printf(m, arc_cache_mumbojumbo(cpu_id, str, PAGE_SIZE));
+
+ seq_printf(m, arc_extn_mumbojumbo(cpu_id, str, PAGE_SIZE));
+
+#ifdef CONFIG_SMP
+ seq_printf(m, arc_platform_smp_cpuinfo());
+#endif
+
free_page((unsigned long)str);
done:
seq_printf(m, "\n\n");
diff --git a/arch/arc/mm/cache_arc700.c b/arch/arc/mm/cache_arc700.c
index 9f8f567..a50f947 100644
--- a/arch/arc/mm/cache_arc700.c
+++ b/arch/arc/mm/cache_arc700.c
@@ -82,6 +82,28 @@ static void __ic_line_inv_4_alias(unsigned long, int);
static void (*___flush_icache_rtn) (unsigned long, int);
#endif

+char *arc_cache_mumbojumbo(int cpu_id, char *buf, int len)
+{
+ int n = 0;
+ unsigned int c = smp_processor_id();
+
+#define PR_CACHE(p, enb, str) \
+{ \
+ if (!(p)->ver) \
+ n += scnprintf(buf + n, len - n, str"\t\t: N/A\n"); \
+ else \
+ n += scnprintf(buf + n, len - n, \
+ str"\t\t: (%uK) VIPT, %dway set-asc, %ub Line %s\n", \
+ TO_KB((p)->sz), (p)->assoc, (p)->line_len, \
+ enb ? "" : "DISABLED (kernel-build)"); \
+}
+
+ PR_CACHE(&cpuinfo_arc700[c].icache, __CONFIG_ARC_HAS_ICACHE, "I-Cache");
+ PR_CACHE(&cpuinfo_arc700[c].dcache, __CONFIG_ARC_HAS_DCACHE, "D-Cache");
+
+ return buf;
+}
+
/*
* Read the Cache Build Confuration Registers, Decode them and save into
* the cpuinfo structure for later use.
@@ -132,10 +154,29 @@ void __init arc_cache_init(void)
struct cpuinfo_arc_cache *dc;
#endif
int way_pg_ratio = way_pg_ratio;
+ char str[256];
+
+ printk(arc_cache_mumbojumbo(0, str, sizeof(str)));

#ifdef CONFIG_ARC_HAS_ICACHE
ic = &cpuinfo_arc700[cpu].icache;

+ /* 1. Confirm some of I-cache params which Linux assumes */
+ if ((ic->assoc != ARC_ICACHE_WAYS) ||
+ (ic->line_len != ARC_ICACHE_LINE_LEN)) {
+ panic("Cache H/W doesn't match kernel Config");
+ }
+#if (CONFIG_ARC_MMU_VER > 2)
+ if (ic->ver != 3) {
+ if (running_on_hw)
+ panic("Cache ver doesn't match MMU ver\n");
+
+ /* For ISS - suggest the toggles to use */
+ pr_err("Use -prop=icache_version=3,-prop=dcache_version=3\n");
+
+ }
+#endif
+
/*
* if Cache way size is <= page size then no aliasing exhibited
* otherwise ratio determines num of aliases.
@@ -175,6 +216,11 @@ void __init arc_cache_init(void)
#ifdef CONFIG_ARC_HAS_DCACHE
dc = &cpuinfo_arc700[cpu].dcache;

+ if ((dc->assoc != ARC_DCACHE_WAYS) ||
+ (dc->line_len != ARC_DCACHE_LINE_LEN)) {
+ panic("Cache H/W doesn't match kernel Config");
+ }
+
/* check for D-Cache aliasing */
if ((dc->sz / ARC_DCACHE_WAYS) > PAGE_SIZE)
panic("D$ aliasing not handled right now\n");
diff --git a/arch/arc/mm/tlb.c b/arch/arc/mm/tlb.c
index defa0c0..36bae47 100644
--- a/arch/arc/mm/tlb.c
+++ b/arch/arc/mm/tlb.c
@@ -451,8 +451,46 @@ void __init read_decode_mmu_bcr(void)
mmu->num_tlb = mmu->sets * mmu->ways;
}

+char *arc_mmu_mumbojumbo(int cpu_id, char *buf, int len)
+{
+ int n = 0;
+ struct cpuinfo_arc_mmu *p_mmu = &cpuinfo_arc700[smp_processor_id()].mmu;
+
+ n += scnprintf(buf + n, len - n, "ARC700 MMU [v%x]\t: %dk PAGE, ",
+ p_mmu->ver, TO_KB(p_mmu->pg_sz));
+
+ n += scnprintf(buf + n, len - n,
+ "J-TLB %d (%dx%d), uDTLB %d, uITLB %d, %s\n",
+ p_mmu->num_tlb, p_mmu->sets, p_mmu->ways,
+ p_mmu->u_dtlb, p_mmu->u_itlb,
+ __CONFIG_ARC_MMU_SASID_VAL ? "SASID" : "");
+
+ return buf;
+}
+
void __init arc_mmu_init(void)
{
+ char str[256];
+ struct cpuinfo_arc_mmu *mmu = &cpuinfo_arc700[smp_processor_id()].mmu;
+
+ printk(arc_mmu_mumbojumbo(0, str, sizeof(str)));
+
+ /* For efficiency sake, kernel is compile time built for a MMU ver
+ * This must match the hardware it is running on.
+ * Linux built for MMU V2, if run on MMU V1 will break down because V1
+ * hardware doesn't understand cmds such as WriteNI, or IVUTLB
+ * On the other hand, Linux built for V1 if run on MMU V2 will do
+ * un-needed workarounds to prevent memcpy thrashing.
+ * Similarly MMU V3 has new features which won't work on older MMU
+ */
+ if (mmu->ver != CONFIG_ARC_MMU_VER) {
+ panic("MMU ver %d doesn't match kernel built for %d...\n",
+ mmu->ver, CONFIG_ARC_MMU_VER);
+ }
+
+ if (mmu->pg_sz != PAGE_SIZE)
+ panic("MMU pg size != PAGE_SIZE (%luk)\n", TO_KB(PAGE_SIZE));
+
/*
* ASID mgmt data structures are compile time init
* asid_cache = FIRST_ASID and asid_mm_map[] all zeroes
--
1.7.4.1

2012-11-12 11:52:06

by Vineet Gupta

[permalink] [raw]
Subject: [RFC Patch v1 48/55] ARC: [plat-arfpga] BVCI Latency Unit setup

From: Vineet Gupta <[email protected]>

Signed-off-by: Vineet Gupta <[email protected]>
---
arch/arc/plat-arcfpga/Kconfig | 32 +++++++++++++++++++++
arch/arc/plat-arcfpga/platform.c | 56 ++++++++++++++++++++++++++++++++++++++
2 files changed, 88 insertions(+), 0 deletions(-)

diff --git a/arch/arc/plat-arcfpga/Kconfig b/arch/arc/plat-arcfpga/Kconfig
index 1b65644..6bc1149 100644
--- a/arch/arc/plat-arcfpga/Kconfig
+++ b/arch/arc/plat-arcfpga/Kconfig
@@ -55,4 +55,36 @@ config ARC_SERIAL_LV2
depends on ARC_COMPACT_IRQ_LEVELS
select ARC_IRQ5_LV2

+menuconfig ARC_HAS_BVCI_LAT_UNIT
+ bool "BVCI Bus Latency Unit"
+ depends on ARC_BOARD_ML509 || ARC_BOARD_ANGEL4
+ help
+ IP to add artifical latency to BVCI Bus Based FPGA builds.
+ The default latency (even worst case) for FPGA is non-realistic
+ (~10 SDRAM, ~5 SSRAM).
+
+config BVCI_LAT_UNITS
+ hex "Latency Unit(s) Bitmap"
+ default "0x0"
+ depends on ARC_HAS_BVCI_LAT_UNIT
+ help
+ There are multiple Latency Units corresponding to the many
+ interfaces of the system bus arbiter (both CPU side as well as
+ the peripheral side).
+ To add latency to ALL memory transaction, choose Unit 0, otherwise
+ for finer grainer - interface wise latency, specify a bitmap (1 bit
+ per unit) of all units. e.g. 1,2,12 will be 0x1003
+
+ Unit 0 - System Arb and Mem Controller
+ Unit 1 - I$ and System Bus
+ Unit 2 - D$ and System Bus
+ ..
+ Unit 12 - IDE Disk controller and System Bus
+
+config BVCI_LAT_CYCLES
+ int "Latency Value in cycles"
+ range 0 63
+ default "30"
+ depends on ARC_HAS_BVCI_LAT_UNIT
+
endif
diff --git a/arch/arc/plat-arcfpga/platform.c b/arch/arc/plat-arcfpga/platform.c
index 3243091..d5d4a10 100644
--- a/arch/arc/plat-arcfpga/platform.c
+++ b/arch/arc/plat-arcfpga/platform.c
@@ -17,6 +17,60 @@
#include <asm/irq.h>
#include <plat/memmap.h>

+/*-----------------------BVCI Latency Unit -----------------------------*/
+
+#ifdef CONFIG_ARC_HAS_BVCI_LAT_UNIT
+
+int lat_cycles = CONFIG_BVCI_LAT_CYCLES;
+
+/* BVCI Bus Profiler: Latency Unit */
+static void __init setup_bvci_lat_unit(void)
+{
+#define MAX_BVCI_UNITS 12
+
+ /* TBD: rewrite this using I/O macros */
+ volatile unsigned int *base = (unsigned int *)BVCI_LAT_UNIT_BASE;
+ volatile unsigned int *lat_unit = (unsigned int *)base + 21;
+ volatile unsigned int *lat_val = (unsigned int *)base + 22;
+ unsigned int unit;
+ const unsigned long units_req = CONFIG_BVCI_LAT_UNITS;
+
+ /*
+ * There are multiple Latency Units corresponding to the many
+ * interfaces of the system bus arbiter (both CPU side as well as
+ * the peripheral side).
+ *
+ * Unit 0 - System Arb and Mem Controller - adds latency to all
+ * memory trasactions
+ * Unit 1 - I$ and System Bus
+ * Unit 2 - D$ and System Bus
+ * ..
+ * Unit 12 - IDE Disk controller and System Bus
+ *
+ * The programmers model requires writing to lat_unit reg first
+ * and then the latency value (cycles) to lat_value reg
+ */
+
+ if (CONFIG_BVCI_LAT_UNITS == 0) {
+ *lat_unit = 0;
+ *lat_val = lat_cycles;
+ pr_info("BVCI Latency for all Memory Transactions %d cycles\n",
+ lat_cycles);
+ } else {
+ for_each_set_bit(unit, &units_req, MAX_BVCI_UNITS) {
+ *lat_unit = unit + 1; /* above returns 0 based */
+ *lat_val = lat_cycles;
+ pr_info("BVCI Latency for Unit[%d] = %d cycles\n",
+ (unit + 1), lat_cycles);
+ }
+ }
+}
+#else
+static void __init setup_bvci_lat_unit(void)
+{
+}
+#endif
+
/*----------------------- Platform Devices -----------------------------*/

#if defined(CONFIG_SERIAL_ARC) || defined(CONFIG_SERIAL_ARC_MODULE)
@@ -111,6 +165,8 @@ void __init arc_platform_early_init(void)
{
pr_info("[plat-arcfpga]: registering early dev resources\n");

+ setup_bvci_lat_unit();
+
arc_fpga_serial_init();
}

--
1.7.4.1

2012-11-12 11:52:13

by Vineet Gupta

[permalink] [raw]
Subject: [RFC Patch v1 49/55] perf, ARC: Enable building perf tools for ARC

From: Vineet Gupta <[email protected]>

Although with uClibc there's more we need to do

Signed-off-by: Vineet Gupta <[email protected]>
---
tools/perf/perf.h | 6 ++++++
1 files changed, 6 insertions(+), 0 deletions(-)

diff --git a/tools/perf/perf.h b/tools/perf/perf.h
index c50985e..1006b17 100644
--- a/tools/perf/perf.h
+++ b/tools/perf/perf.h
@@ -107,6 +107,12 @@ void get_term_dimensions(struct winsize *ws);
#define CPUINFO_PROC "cpu model"
#endif

+#ifdef __arc__
+#define rmb() asm volatile("" ::: "memory")
+#define cpu_relax() rmb()
+#define CPUINFO_PROC "Processor"
+#endif
+
#include <time.h>
#include <unistd.h>
#include <sys/types.h>
--
1.7.4.1

2012-11-12 11:52:27

by Vineet Gupta

[permalink] [raw]
Subject: [RFC Patch v1 51/55] modpost: Ignore ARC specific non-alloc section

From: Vineet Gupta <[email protected]>

ARC relocatable object files contain one/more .gnu.linkonce.arcextmap.*
sections (collated into .arcextmap in final link). These sections are
used by debuggers to display the extension instructions and need-not be
loaded by target (hence !SHF_ALLOC)

This patch adds the section to modpost's ignore list to avoid the
following

======================>8========================
WARNING: vmlinux.o (.gnu.linkonce.arcextmap.inst.3.5.8): unexpected
non-allocatable section.
Did you forget to use "ax"/"aw" in a .S file?
Note that for example <linux/init.h> contains
section definitions for use in .S files.
======================>8========================

Signed-off-by: Vineet Gupta <[email protected]>
---
scripts/mod/modpost.c | 1 +
1 files changed, 1 insertions(+), 0 deletions(-)

diff --git a/scripts/mod/modpost.c b/scripts/mod/modpost.c
index 0d93856..fbbdb8e 100644
--- a/scripts/mod/modpost.c
+++ b/scripts/mod/modpost.c
@@ -830,6 +830,7 @@ static const char *section_white_list[] =
".toc*",
".xt.prop", /* xtensa */
".xt.lit", /* xtensa */
+ ".arcextmap*", /* arc */
NULL
};

--
1.7.4.1

2012-11-12 11:52:29

by Vineet Gupta

[permalink] [raw]
Subject: [RFC Patch v1 50/55] ARC: perf support (software counters only)

From: Vineet Gupta <[email protected]>

Signed-off-by: Vineet Gupta <[email protected]>
---
arch/arc/Kconfig | 3 +++
arch/arc/include/asm/perf_event.h | 19 +++++++++++++++++++
2 files changed, 22 insertions(+), 0 deletions(-)
create mode 100644 arch/arc/include/asm/perf_event.h

diff --git a/arch/arc/Kconfig b/arch/arc/Kconfig
index b878d3e..9882ce0c 100644
--- a/arch/arc/Kconfig
+++ b/arch/arc/Kconfig
@@ -23,12 +23,15 @@ config ARC
select HAVE_ARCH_KGDB
select HAVE_ARCH_TRACEHOOK
select HAVE_GENERIC_HARDIRQS
+ select HAVE_IRQ_WORK
select HAVE_KPROBES
select HAVE_KRETPROBES
select HAVE_MOD_ARCH_SPECIFIC if ARC_DW2_UNWIND
select HAVE_OPROFILE
+ select HAVE_PERF_EVENTS
select HOTPLUG if !INITRAMFS_SOURCE=""
select MODULES_USE_ELF_RELA
+ select PERF_USE_VMALLOC

config SCHED_OMIT_FRAME_POINTER
def_bool y
diff --git a/arch/arc/include/asm/perf_event.h b/arch/arc/include/asm/perf_event.h
new file mode 100644
index 0000000..a10eb78
--- /dev/null
+++ b/arch/arc/include/asm/perf_event.h
@@ -0,0 +1,19 @@
+/*
+ * Copyright (C) 2011-2012 Synopsys, Inc. (http://www.synopsys.com)
+ *
+ * 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.
+ *
+ */
+
+#ifndef __ASM_PERF_EVENT_H
+#define __ASM_PERF_EVENT_H
+
+#define PERF_EVENT_INDEX_OFFSET 0
+
+static inline void set_perf_event_pending(void)
+{
+}
+
+#endif /* __ASM_PERF_EVENT_H */
--
1.7.4.1

2012-11-12 11:52:37

by Vineet Gupta

[permalink] [raw]
Subject: [RFC Patch v1 52/55] ARC: Support for single cycle Close Coupled Mem (CCM)

From: Vineet Gupta <[email protected]>

* Includes mapping of CCMs in address space
* Annotations to move arbitrary code/data into CCM
* Moving some of the critical code/data into CCM
* Runtime detection/reporting

Signed-off-by: Vineet Gupta <[email protected]>
---
arch/arc/Kconfig | 27 ++++++++++++++++++++
arch/arc/include/asm/linkage.h | 33 +++++++++++++++++++++++++
arch/arc/kernel/entry.S | 4 +-
arch/arc/kernel/setup.c | 53 +++++++++++++++++++++++++++++++++++++++-
arch/arc/kernel/vmlinux.lds.S | 21 ++++++++++++++++
arch/arc/mm/tlbex.S | 5 +--
6 files changed, 137 insertions(+), 6 deletions(-)

diff --git a/arch/arc/Kconfig b/arch/arc/Kconfig
index 9882ce0c..79e8f2f 100644
--- a/arch/arc/Kconfig
+++ b/arch/arc/Kconfig
@@ -198,6 +198,33 @@ config ARC_CACHE_PAGES

endif #ARC_CACHE

+config ARC_HAS_ICCM
+ bool "Use ICCM"
+ help
+ Single Cycle RAMS to store Fast Path Code
+ default n
+
+config ARC_ICCM_SZ
+ int "ICCM Size in KB"
+ default "64"
+ depends on ARC_HAS_ICCM
+
+config ARC_HAS_DCCM
+ bool "Use DCCM"
+ help
+ Single Cycle RAMS to store Fast Path Data
+ default n
+
+config ARC_DCCM_SZ
+ int "DCCM Size in KB"
+ default "64"
+ depends on ARC_HAS_DCCM
+
+config ARC_DCCM_BASE
+ hex "DCCM map address"
+ default "0xA0000000"
+ depends on ARC_HAS_DCCM
+
config ARC_HAS_HW_MPY
bool "Use Hardware Multiplier (Normal or Faster XMAC)"
default y
diff --git a/arch/arc/include/asm/linkage.h b/arch/arc/include/asm/linkage.h
index a45d1bb..0283e9e 100644
--- a/arch/arc/include/asm/linkage.h
+++ b/arch/arc/include/asm/linkage.h
@@ -25,6 +25,39 @@
.size \ name, ASM_PREV_SYM_ADDR(\name)
.endm

+/* annotation for data we want in DCCM - if enabled in .config */
+.macro ARCFP_DATA nm
+#ifdef CONFIG_ARC_HAS_DCCM
+ .section .data.arcfp
+#else
+ .section .data
+#endif
+ .global \nm
+.endm
+
+/* annotation for data we want in DCCM - if enabled in .config */
+.macro ARCFP_CODE
+#ifdef CONFIG_ARC_HAS_ICCM
+ .section .text.arcfp, "ax",@progbits
+#else
+ .section .text, "ax",@progbits
+#endif
+.endm
+
+#else /* !__ASSEMBLY__ */
+
+#ifdef CONFIG_ARC_HAS_ICCM
+#define __arcfp_code __attribute__((__section__(".text.arcfp")))
+#else
+#define __arcfp_code __attribute__((__section__(".text")))
+#endif
+
+#ifdef CONFIG_ARC_HAS_DCCM
+#define __arcfp_data __attribute__((__section__(".data.arcfp")))
+#else
+#define __arcfp_data __attribute__((__section__(".data")))
+#endif
+
#endif /* __ASSEMBLY__ */

#endif
diff --git a/arch/arc/kernel/entry.S b/arch/arc/kernel/entry.S
index 2ff15e0..873588e 100644
--- a/arch/arc/kernel/entry.S
+++ b/arch/arc/kernel/entry.S
@@ -155,7 +155,7 @@ VECTOR reserved ; Reserved Exceptions

;##################### Scratch Mem for IRQ stack switching #############

- .section .data ; NOT .global
+ARCFP_DATA int1_saved_reg
.align 32
.type int1_saved_reg, @object
.size int1_saved_reg, 4
@@ -165,7 +165,7 @@ int1_saved_reg:
/* Each Interrupt level needs it's own scratch */
#ifdef CONFIG_ARC_COMPACT_IRQ_LEVELS

- .section .data ; NOT .global
+ARCFP_DATA int2_saved_reg
.type int2_saved_reg, @object
.size int2_saved_reg, 4
int2_saved_reg:
diff --git a/arch/arc/kernel/setup.c b/arch/arc/kernel/setup.c
index d470aa1..0deec55 100644
--- a/arch/arc/kernel/setup.c
+++ b/arch/arc/kernel/setup.c
@@ -61,6 +61,33 @@ void __init read_arc_build_cfg_regs(void)
cpu->extn.ext_arith = read_aux_reg(ARC_REG_EXTARITH_BCR);
cpu->extn.crc = read_aux_reg(ARC_REG_CRC_BCR);

+ /* Note that we read the CCM BCRs independent of kernel config
+ * This is to catch the cases where user doesn't know that
+ * CCMs are present in hardware build
+ */
+ {
+ struct bcr_iccm iccm;
+ struct bcr_dccm dccm;
+ struct bcr_dccm_base dccm_base;
+ unsigned int bcr_32bit_val;
+
+ bcr_32bit_val = read_aux_reg(ARC_REG_ICCM_BCR);
+ if (bcr_32bit_val) {
+ iccm = *((struct bcr_iccm *)&bcr_32bit_val);
+ cpu->iccm.base_addr = iccm.base << 16;
+ cpu->iccm.sz = 0x2000 << (iccm.sz - 1);
+ }
+
+ bcr_32bit_val = read_aux_reg(ARC_REG_DCCM_BCR);
+ if (bcr_32bit_val) {
+ dccm = *((struct bcr_dccm *)&bcr_32bit_val);
+ cpu->dccm.sz = 0x800 << (dccm.sz);
+
+ READ_BCR(ARC_REG_DCCMBASE_BCR, dccm_base);
+ cpu->dccm.base_addr = dccm_base.addr << 8;
+ }
+ }
+
READ_BCR(ARC_REG_XY_MEM_BCR, cpu->extn_xymem);

read_decode_mmu_bcr();
@@ -208,6 +235,30 @@ char *arc_extn_mumbojumbo(int cpu_id, char *buf, int len)
return buf;
}

+void __init arc_chk_ccms(void)
+{
+#if defined(CONFIG_ARC_HAS_DCCM) || defined(CONFIG_ARC_HAS_ICCM)
+ struct cpuinfo_arc *cpu = &cpuinfo_arc700[smp_processor_id()];
+
+#ifdef CONFIG_ARC_HAS_DCCM
+ /*
+ * DCCM can be arbit placed in hardware.
+ * Make sure it's placement/sz matches what Linux is built with
+ */
+ if ((unsigned int)__arc_dccm_base != cpu->dccm.base_addr)
+ panic("Linux built with incorrect DCCM Base address\n");
+
+ if (DCCM_COMPILE_SZ != cpu->dccm.sz)
+ panic("Linux built with incorrect DCCM Size\n");
+#endif
+
+#ifdef CONFIG_ARC_HAS_ICCM
+ if (ICCM_COMPILE_SZ != cpu->iccm.sz)
+ panic("Linux built with incorrect ICCM Size\n");
+#endif
+#endif
+}
+
/*
* Ensure that FP hardware and kernel config match
* -If hardware contains DPFP, kernel needs to save/restore FPU state
@@ -252,7 +303,7 @@ void __init setup_processor(void)

arc_mmu_init();
arc_cache_init();
-
+ arc_chk_ccms();

printk(arc_extn_mumbojumbo(cpu_id, str, sizeof(str)));

diff --git a/arch/arc/kernel/vmlinux.lds.S b/arch/arc/kernel/vmlinux.lds.S
index 56c074e..3b6a21a 100644
--- a/arch/arc/kernel/vmlinux.lds.S
+++ b/arch/arc/kernel/vmlinux.lds.S
@@ -24,6 +24,12 @@ jiffies = jiffies_64;

SECTIONS
{
+ /*
+ * ICCM starts at 0x8000_0000. So if kernel is relocated to some other
+ * address, make sure peripheral at 0x8z doesn't clash with ICCM
+ * Essentially vector is also in ICCM.
+ */
+
. = CONFIG_LINUX_LINK_BASE;

_int_vec_base_lds = .;
@@ -32,6 +38,13 @@ SECTIONS
. = ALIGN(PAGE_SIZE);
}

+#ifdef CONFIG_ARC_HAS_ICCM
+ .text.arcfp : {
+ *(.text.arcfp)
+ . = ALIGN(CONFIG_ARC_ICCM_SZ * 1024);
+ }
+#endif
+
/*
* The reason for having a seperate subsection .init.ramfs is to
* prevent objump from including it in kernel dumps
@@ -135,4 +148,12 @@ SECTIONS
.debug_loc 0 : { *(.debug_loc) }
.debug_macinfo 0 : { *(.debug_macinfo) }

+#ifdef CONFIG_ARC_HAS_DCCM
+ . = CONFIG_ARC_DCCM_BASE;
+ __arc_dccm_base = .;
+ .data.arcfp : {
+ *(.data.arcfp)
+ }
+ . = ALIGN(CONFIG_ARC_DCCM_SZ * 1024);
+#endif
}
diff --git a/arch/arc/mm/tlbex.S b/arch/arc/mm/tlbex.S
index e933e4c..d7631d2 100644
--- a/arch/arc/mm/tlbex.S
+++ b/arch/arc/mm/tlbex.S
@@ -54,8 +54,7 @@
; For details refer to comments before TLBMISS_FREEUP_REGS below
;--------------------------------------------------------------------------

- .section .data
- .global ex_saved_reg1
+ARCFP_DATA ex_saved_reg1
.align 1 << L1_CACHE_SHIFT ; IMP: Must be Cache Line aligned
.type ex_saved_reg1, @object
#ifdef CONFIG_SMP
@@ -259,7 +258,7 @@ ex_saved_reg1:
#endif
.endm

-.section .text, "ax",@progbits ;Fast Path Code, candidate for ICCM
+ARCFP_CODE ;Fast Path Code, candidate for ICCM

;-----------------------------------------------------------------------------
; I-TLB Miss Exception Handler
--
1.7.4.1

2012-11-12 11:52:44

by Vineet Gupta

[permalink] [raw]
Subject: [RFC Patch v1 53/55] ARC: Hostlink Pseudo-Driver for Metaware Debugger

From: Vineet Gupta <[email protected]>

This allows ARC Target to do I/O to host in absence of any peripherals
whatsoever, assisted by Metaware Hostlink facility.

Further we have a FUSE based filesystem which makes us mount/access host
filesystem on target and do fops.

Signed-off-by: Vineet Gupta <[email protected]>
---
arch/arc/Kconfig | 9 +++
arch/arc/kernel/Makefile | 1 +
arch/arc/kernel/arc_hostlink.c | 114 ++++++++++++++++++++++++++++++++++++++++
3 files changed, 124 insertions(+), 0 deletions(-)
create mode 100644 arch/arc/kernel/arc_hostlink.c

diff --git a/arch/arc/Kconfig b/arch/arc/Kconfig
index 79e8f2f..4653070 100644
--- a/arch/arc/Kconfig
+++ b/arch/arc/Kconfig
@@ -394,6 +394,15 @@ config HZ
int "Timer Frequency"
default 100

+config ARC_METAWARE_HLINK
+ bool "Support for Metaware debugger assisted Host access"
+ default n
+ help
+ This options allows a Linux userland apps to directly access
+ host file system (open/creat/read/write etc) with help from
+ Metaware Debugger. This can come in handy for Linux-host communication
+ when there is no real usable peripheral such as EMAC.
+
menuconfig ARC_DBG
bool "ARC debugging"
default y
diff --git a/arch/arc/kernel/Makefile b/arch/arc/kernel/Makefile
index a2b7493..ce14b45 100644
--- a/arch/arc/kernel/Makefile
+++ b/arch/arc/kernel/Makefile
@@ -18,6 +18,7 @@ obj-$(CONFIG_ARC_DW2_UNWIND) += unwind.o
obj-$(CONFIG_KPROBES) += kprobes.o
obj-$(CONFIG_ARC_MISALIGN_ACCESS) += unaligned.o
obj-$(CONFIG_KGDB) += kgdb.o
+obj-$(CONFIG_ARC_METAWARE_HLINK) += arc_hostlink.o

obj-$(CONFIG_ARC_FPU_SAVE_RESTORE) += fpu.o
CFLAGS_fpu.o += -mdpfp
diff --git a/arch/arc/kernel/arc_hostlink.c b/arch/arc/kernel/arc_hostlink.c
new file mode 100644
index 0000000..de3bc42
--- /dev/null
+++ b/arch/arc/kernel/arc_hostlink.c
@@ -0,0 +1,114 @@
+/*
+ * arc_hostlink.c: Pseudo-driver for Metaware provided "hostlink" facility
+ *
+ * Allows Linux userland access to host in absence of any peripherals.
+ *
+ * Copyright (C) 2004, 2007-2010, 2011-2012 Synopsys, Inc. (http://www.synopsys.com)
+ *
+ * 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.
+ */
+
+#include <linux/fs.h> /* file_operations */
+#include <linux/device.h> /* class_create */
+#include <linux/cdev.h> /* cdev */
+#include <linux/mm.h> /* VM_IO */
+#include <linux/module.h>
+#include <linux/uaccess.h>
+
+static unsigned char __HOSTLINK__[4 * PAGE_SIZE] __aligned(PAGE_SIZE);
+
+static int arc_hl_mmap(struct file *fp, struct vm_area_struct *vma);
+static long arc_hl_ioctl(struct file *file, unsigned int cmd,
+ unsigned long arg);
+
+static const struct file_operations arc_hl_fops = {
+ .owner = THIS_MODULE,
+ .unlocked_ioctl = arc_hl_ioctl,
+ .mmap = arc_hl_mmap,
+};
+
+static int arc_hl_major;
+static int arc_hl_minor;
+static int arc_hl_nr_devs = 1;
+static const char arc_hl_devnm[] = "hostlink";
+static struct cdev arc_hl_cdev;
+static struct class *arc_hl_class;
+
+static int __init arc_hl_linux_glue(void)
+{
+ dev_t arc_hl_dev;
+ int i;
+
+ if (arc_hl_major) { /* Preallocated MAJOR */
+
+ arc_hl_dev = MKDEV(arc_hl_major, arc_hl_minor);
+ register_chrdev_region(arc_hl_dev, arc_hl_nr_devs,
+ arc_hl_devnm);
+ } else { /* allocates Major to devices */
+ alloc_chrdev_region(&arc_hl_dev, 0, arc_hl_nr_devs,
+ arc_hl_devnm);
+ arc_hl_major = MAJOR(arc_hl_dev);
+ }
+
+ /* Populate sysfs entries: creates /sys/class/ sub-node for device */
+
+ arc_hl_class = class_create(THIS_MODULE, arc_hl_devnm);
+
+ /* connect file ops with cdev */
+
+ cdev_init(&arc_hl_cdev, &arc_hl_fops);
+ arc_hl_cdev.owner = THIS_MODULE;
+
+ /* Connect major/minor number to cdev
+ * makes device available.
+ * device nodes created with 'mknod` are probably already active.
+ */
+
+ cdev_add(&arc_hl_cdev, arc_hl_dev, arc_hl_nr_devs);
+
+ /* creates /sys/devices/virtual/<dev_name>/<names[i]> node with
+ * link from/sys/class/<dev_name>, needed by mdev.
+ */
+
+ for (i = 0; i < arc_hl_nr_devs; i++)
+ device_create(arc_hl_class, NULL, MKDEV(MAJOR(arc_hl_dev), i),
+ NULL, arc_hl_devnm);
+
+ pr_info("Hostlink dev to mknod is %d:%d\n",
+ arc_hl_major, arc_hl_minor);
+ return 0;
+}
+
+static int __init arc_hl_init(void)
+{
+ arc_hl_linux_glue();
+
+ pr_info("Hlink buffer mmap @ 0x%p\n", __HOSTLINK__);
+
+ return 0;
+}
+
+static int arc_hl_mmap(struct file *fp, struct vm_area_struct *vma)
+{
+ vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
+
+ if (io_remap_pfn_range(vma, vma->vm_start, vma->vm_pgoff,
+ vma->vm_end - vma->vm_start,
+ vma->vm_page_prot)) {
+ pr_warn("Hostlink buffer mmap ERROR\n");
+ return -EAGAIN;
+ }
+ return 0;
+}
+
+static long arc_hl_ioctl(struct file *file, unsigned int cmd,
+ unsigned long arg)
+{
+ /* we only support, returning the physical addr to mmap in user space */
+ put_user((unsigned int)__HOSTLINK__, (int __user *)arg);
+ return 0;
+}
+
+module_init(arc_hl_init);
--
1.7.4.1

2012-11-12 11:52:51

by Vineet Gupta

[permalink] [raw]
Subject: [RFC Patch v1 54/55] ARC: [plat-arcfpga] defconfig

From: Vineet Gupta <[email protected]>

Signed-off-by: Vineet Gupta <[email protected]>
---
arch/arc/configs/fpga_defconfig | 137 +++++++++++++++++++++++++++++++++++++--
1 files changed, 130 insertions(+), 7 deletions(-)

diff --git a/arch/arc/configs/fpga_defconfig b/arch/arc/configs/fpga_defconfig
index cfdca3c..d43e738 100644
--- a/arch/arc/configs/fpga_defconfig
+++ b/arch/arc/configs/fpga_defconfig
@@ -17,6 +17,7 @@ CONFIG_STACKTRACE_SUPPORT=y
CONFIG_HAVE_LATENCYTOP_SUPPORT=y
# CONFIG_NO_DMA is not set
CONFIG_DEFCONFIG_LIST="/lib/modules/$UNAME_RELEASE/.config"
+CONFIG_HAVE_IRQ_WORK=y

#
# General setup
@@ -31,6 +32,7 @@ CONFIG_DEFAULT_HOSTNAME="(none)"
# CONFIG_SWAP is not set
# CONFIG_SYSVIPC is not set
# CONFIG_FHANDLE is not set
+# CONFIG_AUDIT is not set
CONFIG_HAVE_GENERIC_HARDIRQS=y

#
@@ -53,6 +55,7 @@ CONFIG_HIGH_RES_TIMERS=y
#
CONFIG_TICK_CPU_ACCOUNTING=y
# CONFIG_BSD_PROCESS_ACCT is not set
+# CONFIG_TASKSTATS is not set

#
# RCU Subsystem
@@ -66,8 +69,7 @@ CONFIG_LOG_BUF_SHIFT=17
CONFIG_NAMESPACES=y
# CONFIG_UTS_NS is not set
# CONFIG_PID_NS is not set
-CONFIG_UIDGID_CONVERTED=y
-# CONFIG_UIDGID_STRICT_TYPE_CHECKS is not set
+CONFIG_NET_NS=y
# CONFIG_SCHED_AUTOGROUP is not set
# CONFIG_SYSFS_DEPRECATED is not set
# CONFIG_RELAY is not set
@@ -97,13 +99,16 @@ CONFIG_BUG=y
# CONFIG_SIGNALFD is not set
# CONFIG_TIMERFD is not set
# CONFIG_EVENTFD is not set
-# CONFIG_SHMEM is not set
+CONFIG_SHMEM=y
# CONFIG_AIO is not set
CONFIG_EMBEDDED=y
+CONFIG_HAVE_PERF_EVENTS=y
+CONFIG_PERF_USE_VMALLOC=y

#
# Kernel Performance Events And Counters
#
+# CONFIG_PERF_EVENTS is not set
# CONFIG_VM_EVENT_COUNTERS is not set
# CONFIG_SLUB_DEBUG is not set
# CONFIG_COMPAT_BRK is not set
@@ -111,6 +116,11 @@ CONFIG_EMBEDDED=y
CONFIG_SLUB=y
# CONFIG_SLOB is not set
# CONFIG_PROFILING is not set
+CONFIG_HAVE_OPROFILE=y
+# CONFIG_KPROBES is not set
+CONFIG_HAVE_KPROBES=y
+CONFIG_HAVE_KRETPROBES=y
+CONFIG_HAVE_ARCH_TRACEHOOK=y
CONFIG_GENERIC_SMP_IDLE_THREAD=y
CONFIG_ARCH_WANT_IPC_PARSE_VERSION=y
CONFIG_GENERIC_KERNEL_THREAD=y
@@ -121,7 +131,12 @@ CONFIG_MODULES_USE_ELF_RELA=y
#
# CONFIG_HAVE_GENERIC_DMA_COHERENT is not set
CONFIG_BASE_SMALL=1
-# CONFIG_MODULES is not set
+CONFIG_MODULES=y
+# CONFIG_MODULE_FORCE_LOAD is not set
+# CONFIG_MODULE_UNLOAD is not set
+# CONFIG_MODVERSIONS is not set
+# CONFIG_MODULE_SRCVERSION_ALL is not set
+# CONFIG_MODULE_SIG is not set
CONFIG_BLOCK=y
# CONFIG_LBDAF is not set
# CONFIG_BLK_DEV_BSG is not set
@@ -165,6 +180,8 @@ CONFIG_ARC_CACHE_LINE_SHIFT=6
CONFIG_ARC_HAS_ICACHE=y
CONFIG_ARC_HAS_DCACHE=y
CONFIG_ARC_CACHE_PAGES=y
+# CONFIG_ARC_HAS_ICCM is not set
+# CONFIG_ARC_HAS_DCCM is not set
CONFIG_ARC_HAS_HW_MPY=y
# CONFIG_ARC_MMU_V1 is not set
# CONFIG_ARC_MMU_V2 is not set
@@ -172,6 +189,7 @@ CONFIG_ARC_MMU_V3=y
CONFIG_ARC_PAGE_SIZE_8K=y
# CONFIG_ARC_PAGE_SIZE_16K is not set
# CONFIG_ARC_PAGE_SIZE_4K is not set
+# CONFIG_ARC_COMPACT_IRQ_LEVELS is not set
# CONFIG_ARC_FPU_SAVE_RESTORE is not set
CONFIG_ARC_CPU_REL_4_10=y
CONFIG_ARC_HAS_LLSC=y
@@ -183,12 +201,17 @@ CONFIG_ARC_HAS_RTSC=y
#
CONFIG_ARC_BOARD_ANGEL4=y
# CONFIG_ARC_BOARD_ML509 is not set
+# CONFIG_ISS_SMP_EXTN is not set
CONFIG_ARC_SERIAL_BAUD=115200
+# CONFIG_ARC_HAS_BVCI_LAT_UNIT is not set
CONFIG_ARC_PLAT_CLK=80000000
CONFIG_LINUX_LINK_BASE=0x80000000
CONFIG_ARC_PLAT_SDRAM_SIZE=0x10000000
+CONFIG_ARC_CURR_IN_REG=y
+# CONFIG_ARC_MISALIGN_ACCESS is not set
# CONFIG_ARC_STACK_NONEXEC is not set
CONFIG_HZ=100
+# CONFIG_ARC_METAWARE_HLINK is not set
# CONFIG_ARC_DBG is not set
CONFIG_CMDLINE="print-fatal-signals=1"
# CONFIG_CMDLINE_UBOOT is not set
@@ -208,7 +231,71 @@ CONFIG_DEFAULT_MMAP_MIN_ADDR=4096
# CONFIG_CROSS_MEMORY_ATTACH is not set
CONFIG_NEED_PER_CPU_KM=y
# CONFIG_CLEANCACHE is not set
-# CONFIG_NET is not set
+CONFIG_NET=y
+
+#
+# Networking options
+#
+CONFIG_PACKET=y
+# CONFIG_PACKET_DIAG is not set
+CONFIG_UNIX=y
+# CONFIG_UNIX_DIAG is not set
+# CONFIG_XFRM_USER is not set
+# CONFIG_NET_KEY is not set
+CONFIG_INET=y
+# CONFIG_IP_MULTICAST is not set
+# CONFIG_IP_ADVANCED_ROUTER is not set
+# CONFIG_IP_PNP is not set
+# CONFIG_NET_IPIP is not set
+# CONFIG_NET_IPGRE_DEMUX is not set
+# CONFIG_ARPD is not set
+# CONFIG_SYN_COOKIES is not set
+# CONFIG_INET_AH is not set
+# CONFIG_INET_ESP is not set
+# CONFIG_INET_IPCOMP is not set
+# CONFIG_INET_XFRM_TUNNEL is not set
+# CONFIG_INET_TUNNEL is not set
+# CONFIG_INET_XFRM_MODE_TRANSPORT is not set
+# CONFIG_INET_XFRM_MODE_TUNNEL is not set
+# CONFIG_INET_XFRM_MODE_BEET is not set
+# CONFIG_INET_LRO is not set
+CONFIG_INET_DIAG=y
+CONFIG_INET_TCP_DIAG=y
+# CONFIG_INET_UDP_DIAG is not set
+# CONFIG_TCP_CONG_ADVANCED is not set
+CONFIG_TCP_CONG_CUBIC=y
+CONFIG_DEFAULT_TCP_CONG="cubic"
+# CONFIG_IPV6 is not set
+# CONFIG_NETWORK_SECMARK is not set
+# CONFIG_NETFILTER is not set
+# CONFIG_ATM is not set
+# CONFIG_L2TP is not set
+# CONFIG_BRIDGE is not set
+# CONFIG_VLAN_8021Q is not set
+# CONFIG_DECNET is not set
+# CONFIG_LLC2 is not set
+# CONFIG_IPX is not set
+# CONFIG_ATALK is not set
+# CONFIG_PHONET is not set
+# CONFIG_NET_SCHED is not set
+# CONFIG_DCB is not set
+# CONFIG_BATMAN_ADV is not set
+# CONFIG_OPENVSWITCH is not set
+CONFIG_BQL=y
+
+#
+# Network testing
+#
+# CONFIG_NET_PKTGEN is not set
+# CONFIG_HAMRADIO is not set
+# CONFIG_CAN is not set
+# CONFIG_IRDA is not set
+# CONFIG_BT is not set
+# CONFIG_WIRELESS is not set
+# CONFIG_WIMAX is not set
+# CONFIG_RFKILL is not set
+# CONFIG_NET_9P is not set
+# CONFIG_CAIF is not set

#
# Device Drivers
@@ -234,6 +321,7 @@ CONFIG_EXTRA_FIRMWARE=""
# Bus devices
#
# CONFIG_OMAP_OCP2SCP is not set
+# CONFIG_CONNECTOR is not set
# CONFIG_MTD is not set
# CONFIG_PARPORT is not set
# CONFIG_BLK_DEV is not set
@@ -268,6 +356,8 @@ CONFIG_SCSI_MOD=y
# CONFIG_SCSI_NETLINK is not set
# CONFIG_ATA is not set
# CONFIG_MD is not set
+# CONFIG_NETDEVICES is not set
+# CONFIG_ISDN is not set

#
# Input device support
@@ -314,6 +404,7 @@ CONFIG_UNIX98_PTYS=y
# CONFIG_DEVPTS_MULTIPLE_INSTANCES is not set
# CONFIG_LEGACY_PTYS is not set
# CONFIG_SERIAL_NONSTANDARD is not set
+# CONFIG_N_GSM is not set
# CONFIG_TRACE_SINK is not set
# CONFIG_DEVKMEM is not set

@@ -461,8 +552,16 @@ CONFIG_DUMMY_CONSOLE=y
#
# File systems
#
-# CONFIG_EXT2_FS is not set
-# CONFIG_EXT3_FS is not set
+CONFIG_EXT2_FS=y
+CONFIG_EXT2_FS_XATTR=y
+# CONFIG_EXT2_FS_POSIX_ACL is not set
+# CONFIG_EXT2_FS_SECURITY is not set
+# CONFIG_EXT2_FS_XIP is not set
+CONFIG_EXT3_FS=y
+CONFIG_EXT3_DEFAULTS_TO_ORDERED=y
+CONFIG_EXT3_FS_XATTR=y
+# CONFIG_EXT3_FS_POSIX_ACL is not set
+# CONFIG_EXT3_FS_SECURITY is not set
# CONFIG_EXT4_FS is not set
# CONFIG_REISERFS_FS is not set
# CONFIG_JFS_FS is not set
@@ -504,9 +603,28 @@ CONFIG_PROC_FS=y
CONFIG_PROC_SYSCTL=y
CONFIG_PROC_PAGE_MONITOR=y
CONFIG_SYSFS=y
+CONFIG_TMPFS=y
+# CONFIG_TMPFS_POSIX_ACL is not set
+# CONFIG_TMPFS_XATTR is not set
# CONFIG_HUGETLB_PAGE is not set
# CONFIG_CONFIGFS_FS is not set
# CONFIG_MISC_FILESYSTEMS is not set
+CONFIG_NETWORK_FILESYSTEMS=y
+CONFIG_NFS_FS=y
+# CONFIG_NFS_V2 is not set
+CONFIG_NFS_V3=y
+# CONFIG_NFS_V3_ACL is not set
+# CONFIG_NFS_V4 is not set
+# CONFIG_NFS_SWAP is not set
+# CONFIG_NFSD is not set
+CONFIG_LOCKD=y
+CONFIG_LOCKD_V4=y
+CONFIG_NFS_COMMON=y
+CONFIG_SUNRPC=y
+# CONFIG_SUNRPC_DEBUG is not set
+# CONFIG_CIFS is not set
+# CONFIG_NCP_FS is not set
+# CONFIG_CODA_FS is not set
# CONFIG_NLS is not set

#
@@ -562,8 +680,11 @@ CONFIG_STACKTRACE=y
# CONFIG_FAULT_INJECTION is not set
# CONFIG_LATENCYTOP is not set
# CONFIG_DEBUG_PAGEALLOC is not set
+# CONFIG_RBTREE_TEST is not set
+# CONFIG_INTERVAL_TREE_TEST is not set
# CONFIG_ATOMIC64_SELFTEST is not set
# CONFIG_SAMPLES is not set
+CONFIG_HAVE_ARCH_KGDB=y
# CONFIG_TEST_KSTRTOX is not set
CONFIG_EARLY_PRINTK=y
# CONFIG_DEBUG_STACKOVERFLOW is not set
@@ -600,6 +721,8 @@ CONFIG_ZLIB_INFLATE=y
CONFIG_DECOMPRESS_GZIP=y
CONFIG_HAS_IOMEM=y
CONFIG_HAS_DMA=y
+CONFIG_DQL=y
+CONFIG_NLATTR=y
CONFIG_GENERIC_ATOMIC64=y
CONFIG_ARCH_HAS_ATOMIC64_DEC_IF_POSITIVE=y
# CONFIG_AVERAGE is not set
--
1.7.4.1

2012-11-12 11:52:58

by Vineet Gupta

[permalink] [raw]
Subject: [RFC Patch v1 55/55] ARC: Add self to MAINTAINERS

From: Vineet Gupta <[email protected]>

Signed-off-by: Vineet Gupta <[email protected]>
---
MAINTAINERS | 6 ++++++
1 files changed, 6 insertions(+), 0 deletions(-)

diff --git a/MAINTAINERS b/MAINTAINERS
index 1fa9074..e933489 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -7073,6 +7073,12 @@ F: lib/swiotlb.c
F: arch/*/kernel/pci-swiotlb.c
F: include/linux/swiotlb.h

+SYNOPSYS ARC ARCHITECTURE
+M: Vineet Gupta <[email protected]>
+L: [email protected]
+S: Supported
+F: arch/arc/
+
SYSV FILESYSTEM
M: Christoph Hellwig <[email protected]>
S: Maintained
--
1.7.4.1

2012-11-12 11:51:49

by Vineet Gupta

[permalink] [raw]
Subject: [RFC Patch v1 46/55] ARC: kgdb support

From: Mischa Jonker <[email protected]>

Signed-off-by: Mischa Jonker <[email protected]>
Signed-off-by: Vineet Gupta <[email protected]>
---
arch/arc/Kconfig | 5 +-
arch/arc/include/asm/kgdb.h | 61 +++++++++++++
arch/arc/kernel/Makefile | 2 +
arch/arc/kernel/kgdb.c | 205 +++++++++++++++++++++++++++++++++++++++++++
arch/arc/kernel/traps.c | 6 ++
5 files changed, 277 insertions(+), 2 deletions(-)
create mode 100644 arch/arc/include/asm/kgdb.h
create mode 100644 arch/arc/kernel/kgdb.c

diff --git a/arch/arc/Kconfig b/arch/arc/Kconfig
index 81d78fc..b878d3e 100644
--- a/arch/arc/Kconfig
+++ b/arch/arc/Kconfig
@@ -20,6 +20,7 @@ config ARC
select GENERIC_KERNEL_THREAD
select GENERIC_PENDING_IRQ if SMP
select GENERIC_SMP_IDLE_THREAD
+ select HAVE_ARCH_KGDB
select HAVE_ARCH_TRACEHOOK
select HAVE_GENERIC_HARDIRQS
select HAVE_KPROBES
@@ -383,12 +384,12 @@ config ARC_DW2_UNWIND

config ARC_DBG_TLB_PARANOIA
bool "Paranoia Checks in Low Level TLB Handlers"
- depends on ARC_DBG && !SMP
+ depends on ARC_DBG
default n

config ARC_DBG_EVENT_TIMELINE
bool "Low level event capture"
- depends on ARC_DBG
+ depends on ARC_DBG && !SMP
default n
help
Capture low level events: IRQ/Trap/Exception
diff --git a/arch/arc/include/asm/kgdb.h b/arch/arc/include/asm/kgdb.h
new file mode 100644
index 0000000..f3c4934
--- /dev/null
+++ b/arch/arc/include/asm/kgdb.h
@@ -0,0 +1,61 @@
+/*
+ * kgdb support for ARC
+ *
+ * Copyright (C) 2012 Synopsys, Inc. (http://www.synopsys.com)
+ *
+ * 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.
+ */
+
+#ifndef __ARC_KGDB_H__
+#define __ARC_KGDB_H__
+
+#ifdef CONFIG_KGDB
+
+#include <asm/user.h>
+
+/* to ensure compatibility with Linux 2.6.35, we don't implement the get/set
+ * register API yet */
+#undef DBG_MAX_REG_NUM
+
+#define GDB_MAX_REGS 39
+
+#define BREAK_INSTR_SIZE 2
+#define CACHE_FLUSH_IS_SAFE 1
+#define NUMREGBYTES (GDB_MAX_REGS * 4)
+#define BUFMAX 2048
+
+static inline void arch_kgdb_breakpoint(void)
+{
+ __asm__ __volatile__ ("trap_s 0x4\n");
+}
+
+extern void kgdb_trap(struct pt_regs *regs, int param);
+
+enum arc700_linux_regnums {
+ _R0 = 0,
+ _R1, _R2, _R3, _R4, _R5, _R6, _R7, _R8, _R9, _R10, _R11, _R12, _R13,
+ _R14, _R15, _R16, _R17, _R18, _R19, _R20, _R21, _R22, _R23, _R24,
+ _R25, _R26,
+ _BTA = 27,
+ _LP_START = 28,
+ _LP_END = 29,
+ _LP_COUNT = 30,
+ _STATUS32 = 31,
+ _BLINK = 32,
+ _FP = 33,
+ __SP = 34,
+ _EFA = 35,
+ _RET = 36,
+ _ORIG_R8 = 37,
+ _STOP_PC = 38
+};
+
+#else
+static inline void kgdb_trap(struct pt_regs *regs, int param)
+{
+}
+#endif
+
+#endif /* __ARC_KGDB_H__ */
diff --git a/arch/arc/kernel/Makefile b/arch/arc/kernel/Makefile
index 52ef6ce..a2b7493 100644
--- a/arch/arc/kernel/Makefile
+++ b/arch/arc/kernel/Makefile
@@ -17,6 +17,8 @@ obj-$(CONFIG_SMP) += smp.o
obj-$(CONFIG_ARC_DW2_UNWIND) += unwind.o
obj-$(CONFIG_KPROBES) += kprobes.o
obj-$(CONFIG_ARC_MISALIGN_ACCESS) += unaligned.o
+obj-$(CONFIG_KGDB) += kgdb.o
+
obj-$(CONFIG_ARC_FPU_SAVE_RESTORE) += fpu.o
CFLAGS_fpu.o += -mdpfp

diff --git a/arch/arc/kernel/kgdb.c b/arch/arc/kernel/kgdb.c
new file mode 100644
index 0000000..2888ba5
--- /dev/null
+++ b/arch/arc/kernel/kgdb.c
@@ -0,0 +1,205 @@
+/*
+ * kgdb support for ARC
+ *
+ * Copyright (C) 2012 Synopsys, Inc. (http://www.synopsys.com)
+ *
+ * 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.
+ */
+
+#include <linux/kgdb.h>
+#include <asm/disasm.h>
+#include <asm/cacheflush.h>
+
+static void to_gdb_regs(unsigned long *gdb_regs, struct pt_regs *kernel_regs,
+ struct callee_regs *cregs)
+{
+ int regno;
+
+ for (regno = 0; regno <= 26; regno++)
+ gdb_regs[_R0 + regno] = get_reg(regno, kernel_regs, cregs);
+
+ for (regno = 27; regno < GDB_MAX_REGS; regno++)
+ gdb_regs[regno] = 0;
+
+ gdb_regs[_FP] = kernel_regs->fp;
+ gdb_regs[__SP] = kernel_regs->sp;
+ gdb_regs[_BLINK] = kernel_regs->blink;
+ gdb_regs[_RET] = kernel_regs->ret;
+ gdb_regs[_STATUS32] = kernel_regs->status32;
+ gdb_regs[_LP_COUNT] = kernel_regs->lp_count;
+ gdb_regs[_LP_END] = kernel_regs->lp_end;
+ gdb_regs[_LP_START] = kernel_regs->lp_start;
+ gdb_regs[_BTA] = kernel_regs->bta;
+ gdb_regs[_STOP_PC] = kernel_regs->ret;
+}
+
+static void from_gdb_regs(unsigned long *gdb_regs, struct pt_regs *kernel_regs,
+ struct callee_regs *cregs)
+{
+ int regno;
+
+ for (regno = 0; regno <= 26; regno++)
+ set_reg(regno, gdb_regs[regno + _R0], kernel_regs, cregs);
+
+ kernel_regs->fp = gdb_regs[_FP];
+ kernel_regs->sp = gdb_regs[__SP];
+ kernel_regs->blink = gdb_regs[_BLINK];
+ kernel_regs->ret = gdb_regs[_RET];
+ kernel_regs->status32 = gdb_regs[_STATUS32];
+ kernel_regs->lp_count = gdb_regs[_LP_COUNT];
+ kernel_regs->lp_end = gdb_regs[_LP_END];
+ kernel_regs->lp_start = gdb_regs[_LP_START];
+ kernel_regs->bta = gdb_regs[_BTA];
+}
+
+
+void pt_regs_to_gdb_regs(unsigned long *gdb_regs, struct pt_regs *kernel_regs)
+{
+ to_gdb_regs(gdb_regs, kernel_regs, (struct callee_regs *)
+ current->thread.callee_reg);
+}
+
+void gdb_regs_to_pt_regs(unsigned long *gdb_regs, struct pt_regs *kernel_regs)
+{
+ from_gdb_regs(gdb_regs, kernel_regs, (struct callee_regs *)
+ current->thread.callee_reg);
+}
+
+void sleeping_thread_to_gdb_regs(unsigned long *gdb_regs,
+ struct task_struct *task)
+{
+ if (task)
+ to_gdb_regs(gdb_regs, task_pt_regs(task),
+ (struct callee_regs *) task->thread.callee_reg);
+}
+
+struct single_step_data_t {
+ uint16_t opcode[2];
+ unsigned long address[2];
+ int is_branch;
+ int armed;
+} single_step_data;
+
+static void undo_single_step(struct pt_regs *regs)
+{
+ if (single_step_data.armed) {
+ int i;
+
+ for (i = 0; i < (single_step_data.is_branch ? 2 : 1); i++) {
+ memcpy((void *) single_step_data.address[i],
+ &single_step_data.opcode[i],
+ BREAK_INSTR_SIZE);
+
+ flush_icache_range(single_step_data.address[i],
+ single_step_data.address[i] +
+ BREAK_INSTR_SIZE);
+ }
+ single_step_data.armed = 0;
+ }
+}
+
+static void place_trap(unsigned long address, void *save)
+{
+ memcpy(save, (void *) address, BREAK_INSTR_SIZE);
+ memcpy((void *) address, &arch_kgdb_ops.gdb_bpt_instr,
+ BREAK_INSTR_SIZE);
+ flush_icache_range(address, address + BREAK_INSTR_SIZE);
+}
+
+static void do_single_step(struct pt_regs *regs)
+{
+ single_step_data.is_branch = disasm_next_pc((unsigned long)
+ regs->ret, regs, (struct callee_regs *)
+ current->thread.callee_reg,
+ &single_step_data.address[0],
+ &single_step_data.address[1]);
+
+ place_trap(single_step_data.address[0], &single_step_data.opcode[0]);
+
+ if (single_step_data.is_branch) {
+ place_trap(single_step_data.address[1],
+ &single_step_data.opcode[1]);
+ }
+
+ single_step_data.armed++;
+}
+
+int kgdb_arch_handle_exception(int e_vector, int signo, int err_code,
+ char *remcomInBuffer, char *remcomOutBuffer,
+ struct pt_regs *regs)
+{
+ unsigned long addr;
+ char *ptr;
+
+ undo_single_step(regs);
+
+ switch (remcomInBuffer[0]) {
+ case 's':
+ case 'c':
+ ptr = &remcomInBuffer[1];
+ if (kgdb_hex2long(&ptr, &addr))
+ regs->ret = addr;
+
+ case 'D':
+ case 'k':
+ atomic_set(&kgdb_cpu_doing_single_step, -1);
+
+ if (remcomInBuffer[0] == 's') {
+ do_single_step(regs);
+ atomic_set(&kgdb_cpu_doing_single_step,
+ smp_processor_id());
+ }
+
+ return 0;
+ }
+ return -1;
+}
+
+unsigned long kgdb_arch_pc(int exception, struct pt_regs *regs)
+{
+ return instruction_pointer(regs);
+}
+
+int kgdb_arch_init(void)
+{
+ single_step_data.armed = 0;
+ return 0;
+}
+
+void kgdb_trap(struct pt_regs *regs, int param)
+{
+ /* trap_s 3 is used for breakpoints that overwrite existing
+ * instructions, while trap_s 4 is used for compiled breakpoints.
+ *
+ * with trap_s 3 breakpoints the original instruction needs to be
+ * restored and continuation needs to start at the location of the
+ * breakpoint.
+ *
+ * with trap_s 4 (compiled) breakpoints, continuation needs to
+ * start after the breakpoint.
+ */
+ if (param == 3)
+ instruction_pointer(regs) -= BREAK_INSTR_SIZE;
+
+ kgdb_handle_exception(1, SIGTRAP, 0, regs);
+}
+
+void kgdb_arch_exit(void)
+{
+}
+
+void kgdb_arch_set_pc(struct pt_regs *regs, unsigned long ip)
+{
+ instruction_pointer(regs) = ip;
+}
+
+struct kgdb_arch arch_kgdb_ops = {
+ /* breakpoint instruction: TRAP_S 0x3 */
+#ifdef CONFIG_CPU_BIG_ENDIAN
+ .gdb_bpt_instr = {0x78, 0x7e},
+#else
+ .gdb_bpt_instr = {0x7e, 0x78},
+#endif
+};
diff --git a/arch/arc/kernel/traps.c b/arch/arc/kernel/traps.c
index d2bc5bd..689c3fb 100644
--- a/arch/arc/kernel/traps.c
+++ b/arch/arc/kernel/traps.c
@@ -20,6 +20,7 @@
#include <asm/setup.h>
#include <asm/kprobes.h>
#include <asm/unaligned.h>
+#include <asm/kgdb.h>

void __init trap_init(void)
{
@@ -141,6 +142,11 @@ void do_non_swi_trap(unsigned long cause, unsigned long address,
trap_is_kprobe(param, address, regs);
break;

+ case 3:
+ case 4:
+ kgdb_trap(regs, param);
+ break;
+
default:
break;
}
--
1.7.4.1

2012-11-12 11:56:39

by Vineet Gupta

[permalink] [raw]
Subject: [RFC Patch v1 38/55] ARC: Low level event capture/logging

From: Vineet Gupta <[email protected]>

Poorman's version of LTT

Signed-off-by: Vineet Gupta <[email protected]>
---
arch/arc/Kconfig | 7 +
arch/arc/Makefile | 3 +
arch/arc/include/asm/event-log-asm.h | 185 +++++++++++++++++++++
arch/arc/include/asm/event-log.h | 102 ++++++++++++
arch/arc/kernel/Makefile | 1 +
arch/arc/kernel/asm-offsets.c | 15 ++
arch/arc/kernel/entry.S | 31 ++++
arch/arc/kernel/event-log.c | 304 ++++++++++++++++++++++++++++++++++
arch/arc/kernel/signal.c | 3 +
arch/arc/mm/tlb.c | 3 +
arch/arc/mm/tlbex.S | 7 +
11 files changed, 661 insertions(+), 0 deletions(-)
create mode 100644 arch/arc/include/asm/event-log-asm.h
create mode 100644 arch/arc/include/asm/event-log.h
create mode 100644 arch/arc/kernel/event-log.c

diff --git a/arch/arc/Kconfig b/arch/arc/Kconfig
index e096545..15d740c 100644
--- a/arch/arc/Kconfig
+++ b/arch/arc/Kconfig
@@ -323,6 +323,13 @@ config ARC_DBG_TLB_PARANOIA
depends on ARC_DBG
default n

+config ARC_DBG_EVENT_TIMELINE
+ bool "Low level event capture"
+ depends on ARC_DBG
+ default n
+ help
+ Capture low level events: IRQ/Trap/Exception
+
config ARC_DBG_TLB_MISS_COUNT
bool "Profile TLB Misses"
default n
diff --git a/arch/arc/Makefile b/arch/arc/Makefile
index 7ef6767..a533546 100644
--- a/arch/arc/Makefile
+++ b/arch/arc/Makefile
@@ -47,6 +47,9 @@ endif
disable_small_data := y
cflags-$(disable_small_data) += -mno-sdata -fcall-used-gp

+# Low level event tracing with Metaware debugger assist needs symbol info
+cflags-$(CONFIG_ARC_DBG_EVENT_TIMELINE) += -g
+
cflags-$(CONFIG_CPU_BIG_ENDIAN) += -mbig-endian
ldflags-$(CONFIG_CPU_BIG_ENDIAN) += -EB

diff --git a/arch/arc/include/asm/event-log-asm.h b/arch/arc/include/asm/event-log-asm.h
new file mode 100644
index 0000000..bc29e7d
--- /dev/null
+++ b/arch/arc/include/asm/event-log-asm.h
@@ -0,0 +1,185 @@
+/*
+ * Low level Event Capture API callable from Assembly Code
+ * vineetg: Feb 2008
+ *
+ * TBD: SMP Safe
+ *
+ * Copyright (C) 2004, 2007-2010, 2011-2012 Synopsys, Inc. (http://www.synopsys.com)
+ *
+ * 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.
+ */
+
+#ifndef __ASM_ARC_EVENT_LOG_ASM_H
+#define __ASM_ARC_EVENT_LOG_ASM_H
+
+#include <asm/event-log.h>
+
+#ifdef __ASSEMBLY__
+
+#ifndef CONFIG_ARC_DBG_EVENT_TIMELINE
+
+.macro TAKE_SNAP_ASM reg_scratch, reg_ptr, type
+.endm
+
+.macro TAKE_SNAP_C_FROM_ASM type
+.endm
+
+#else
+
+#include <asm/asm-offsets.h>
+
+/*
+ * Macro to invoke the ASM event logger routine from assmebly code
+ * This is generated in-place in caller.
+ *
+ * @reg_scratch and @reg_ptr:
+ * Registers provided by caller for coding the macro itself.
+ * At this point if call, say Low level ISR, the Reg-File might not have
+ * been saved, so only use reg safe.
+ * @type:
+ * The low level event, defined in event-log.h
+ */
+.macro TAKE_SNAP_ASM reg_scratch, reg_ptr, type
+
+ /*
+ * Earlier we used to save only reg_scratch and clobber reg_ptr and rely
+ * on caller to understand this. Too much trouble.
+ * Now we save both
+ */
+ st \reg_scratch, [tmp_save_reg]
+ st \reg_ptr, [tmp_save_reg2]
+
+ ld \reg_ptr, [timeline_ctr]
+
+ /* HACK to detect if the circular log buffer is being overflowed */
+ brne \reg_ptr, MAX_SNAPS, 1f
+ flag 1
+ nop
+1:
+#ifdef CONFIG_ARC_HAS_HW_MPY
+ mpyu \reg_ptr, \reg_ptr, EVLOG_RECORD_SZ
+#else
+#error "even logger broken for !CONFIG_ARC_HAS_HW_MPY
+#endif
+
+ add \reg_ptr, timeline_log, \reg_ptr
+
+ /*############ Common data ########## */
+
+ /* TIMER1 count in timeline_log[timeline_ctr].time */
+ lr \reg_scratch, [ARC_REG_TIMER1_CNT]
+ st \reg_scratch, [\reg_ptr, EVLOG_FIELD_TIME]
+
+ /* current task ptr in timeline_log[timeline_ctr].task */
+ ld \reg_scratch, [_current_task]
+ ld \reg_scratch, [\reg_scratch, TASK_TGID]
+ st \reg_scratch, [\reg_ptr, EVLOG_FIELD_TASK]
+
+ /* Type of event (Intr/Excp/Trap etc) */
+ mov \reg_scratch, \type
+ st \reg_scratch, [\reg_ptr, EVLOG_FIELD_EVENT_ID]
+
+ /* save SP at time of exception */
+ st sp, [\reg_ptr, EVLOG_FIELD_SP]
+
+ st 0, [\reg_ptr, EVLOG_FIELD_EXTRA]
+ st 0, [\reg_ptr, EVLOG_FIELD_CAUSE]
+ st 0, [\reg_ptr, EVLOG_FIELD_EXTRA3]
+
+ lr \reg_scratch, [status32]
+ st \reg_scratch, [\reg_ptr, EVLOG_FIELD_EXTRA2]
+
+ /* ############ Event specific data ########## */
+ mov \reg_scratch, \type
+ and.f 0, \reg_scratch, EVENT_CLASS_EXIT
+ bz 1f
+
+ /* Stuff to do for all kernel exit events */
+ ld \reg_scratch, [sp, PT_status32]
+ st \reg_scratch, [\reg_ptr, EVLOG_FIELD_EXTRA]
+
+ /* preempt count in log->sp */
+ and \reg_scratch, sp, ~(0x2000 - 1)
+ ld \reg_scratch, [\reg_scratch, THREAD_INFO_PREEMPT_COUNT]
+ st \reg_scratch, [\reg_ptr, EVLOG_FIELD_SP]
+
+ ld \reg_scratch, [sp, PT_ret]
+ st \reg_scratch, [\reg_ptr, EVLOG_FIELD_EFA]
+
+ mov \reg_scratch, \type
+1:
+2:
+ /* for Trap, Syscall number */
+ cmp \reg_scratch, SNAP_TRAP_IN
+ bnz 3f
+ st r8, [\reg_ptr, EVLOG_FIELD_CAUSE]
+ lr \reg_scratch, [erstatus]
+ st \reg_scratch, [\reg_ptr, EVLOG_FIELD_EXTRA]
+ j 99f
+3:
+5:
+ /* For Exceptions (TLB/ProtV etc) */
+ cmp \reg_scratch, SNAP_EXCP_IN
+ bnz 6f
+
+ lr \reg_scratch, [ecr]
+ st \reg_scratch, [\reg_ptr, EVLOG_FIELD_CAUSE]
+ lr \reg_scratch, [eret]
+ st \reg_scratch, [\reg_ptr, EVLOG_FIELD_EFA]
+ lr \reg_scratch, [erstatus]
+ st \reg_scratch, [\reg_ptr, EVLOG_FIELD_EXTRA]
+ lr \reg_scratch, [efa]
+ st \reg_scratch, [\reg_ptr, EVLOG_FIELD_EXTRA3]
+ j 99f
+
+6: /* for Interrupts, IRQ */
+ cmp \reg_scratch, SNAP_INTR_OUT
+ bnz 7f
+ lr \reg_scratch, [icause1]
+ st \reg_scratch, [\reg_ptr, EVLOG_FIELD_CAUSE]
+ j 99f
+
+7:
+ cmp \reg_scratch, SNAP_INTR_OUT2
+ bnz 8f
+ lr \reg_scratch, [icause2]
+ st \reg_scratch, [\reg_ptr, EVLOG_FIELD_CAUSE]
+ j 99f
+
+8:
+ cmp \reg_scratch, SNAP_EXCP_OUT_FAST
+ bnz 9f
+ lr \reg_scratch, [erstatus]
+ st \reg_scratch, [\reg_ptr, EVLOG_FIELD_EXTRA]
+ j 99f
+
+ /* place holder for next */
+9:
+
+99:
+ /* increment timeline_ctr with mode on max */
+ ld \reg_scratch, [timeline_ctr]
+ add \reg_scratch, \reg_scratch, 1
+ and \reg_scratch, \reg_scratch, MAX_SNAPS_MASK
+ st \reg_scratch, [timeline_ctr]
+
+ /* Restore back orig scratch reg */
+ ld \reg_scratch, [tmp_save_reg]
+ ld \reg_ptr, [tmp_save_reg2]
+.endm
+
+/*
+ * Macro to invoke the "C" event logger routine from assmebly code
+ */
+.macro TAKE_SNAP_C_FROM_ASM type
+ mov r0, \type
+ bl take_snap2
+.endm
+
+#endif /* CONFIG_ARC_DBG_EVENT_TIMELINE */
+
+#endif /* __ASSEMBLY__ */
+
+#endif
diff --git a/arch/arc/include/asm/event-log.h b/arch/arc/include/asm/event-log.h
new file mode 100644
index 0000000..423f549
--- /dev/null
+++ b/arch/arc/include/asm/event-log.h
@@ -0,0 +1,102 @@
+/*
+ * Copyright (C) 2004, 2007-2010, 2011-2012 Synopsys, Inc. (http://www.synopsys.com)
+ *
+ * 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.
+ *
+ * vineetg: Dec 2009
+ * Reworked the numbering scheme into Event Classes for making it easier to
+ * do class specific things in the snapshot routines
+ *
+ * vineetg: Feb 2008
+ * System Event Logging APIs
+ */
+
+#ifndef __ASM_ARC_EVENT_LOG_H
+#define __ASM_ARC_EVENT_LOG_H
+
+/*######################################################################
+ *
+ * Event Logging API
+ *
+ *#####################################################################*/
+
+/* Size of the log buffer */
+#define MAX_SNAPS 1024
+#define MAX_SNAPS_MASK (MAX_SNAPS-1)
+
+/* Helpers to setup Event IDs:
+ * 8 classes of events possible
+ * 23 unique events for each Class
+ * Right now we have only 3 classes:
+ * Entry into kernel, exit from kernel and everything else is custom event
+ *
+ * Need for this fancy numbering scheme so that in event logger, class specific
+ * things, common for all events in class, could be easily done
+ */
+#define EVENT_ID(x) (0x100 << x)
+#define EVENT_CLASS_ENTER 0x01 /* Need to start from 1, not 0 */
+#define EVENT_CLASS_EXIT 0x02
+#define EVENT_CLASS_CUSTOM 0x80
+
+#define KERNEL_ENTER_EVENT(x) (EVENT_ID(x)|EVENT_CLASS_ENTER)
+#define KERNEL_EXIT_EVENT(x) (EVENT_ID(x)|EVENT_CLASS_EXIT)
+#define CUSTOM_EVENT(x) (EVENT_ID(x)|EVENT_CLASS_CUSTOM)
+
+/* Actual Event IDs used in kernel code */
+#define SNAP_INTR_IN KERNEL_ENTER_EVENT(0)
+#define SNAP_EXCP_IN KERNEL_ENTER_EVENT(1)
+#define SNAP_TRAP_IN KERNEL_ENTER_EVENT(2)
+#define SNAP_INTR_IN2 KERNEL_ENTER_EVENT(3)
+
+#define SNAP_INTR_OUT KERNEL_EXIT_EVENT(0)
+#define SNAP_EXCP_OUT KERNEL_EXIT_EVENT(1)
+#define SNAP_TRAP_OUT KERNEL_EXIT_EVENT(2)
+#define SNAP_INTR_OUT2 KERNEL_EXIT_EVENT(3)
+#define SNAP_EXCP_OUT_FAST KERNEL_EXIT_EVENT(4)
+
+#define SNAP_PRE_CTXSW_2_U CUSTOM_EVENT(0)
+#define SNAP_PRE_CTXSW_2_K CUSTOM_EVENT(1)
+#define SNAP_DO_PF_ENTER CUSTOM_EVENT(2)
+#define SNAP_DO_PF_EXIT CUSTOM_EVENT(3)
+#define SNAP_TLB_FLUSH_ALL CUSTOM_EVENT(4)
+#define SNAP_PREEMPT_SCH_IRQ CUSTOM_EVENT(5)
+#define SNAP_PREEMPT_SCH CUSTOM_EVENT(6)
+#define SNAP_SIGRETURN CUSTOM_EVENT(7)
+#define SNAP_BEFORE_SIG CUSTOM_EVENT(8)
+
+#define SNAP_SENTINEL CUSTOM_EVENT(22)
+
+#ifndef CONFIG_ARC_DBG_EVENT_TIMELINE
+
+#define take_snap(type, extra, ptreg)
+#define sort_snaps(halt_after_sort)
+
+#else
+
+#ifndef __ASSEMBLY__
+
+typedef struct {
+
+ /* 0 */ char nm[16];
+ /* 16 */ unsigned int extra; /* Traps: Syscall num, Intr: IRQ, Excep */
+ /* 20 */ unsigned int fault_addr;
+ /* 24 */ unsigned int cause;
+ /* 28 */ unsigned int task;
+ /* 32 */ unsigned long time;
+ /* 36 */ unsigned int event;
+ /* 40 */ unsigned int sp;
+ /* 44 */ unsigned int extra2;
+ /* 40 */ unsigned int extra3;
+
+} timeline_log_t;
+
+void take_snap(int type, unsigned int extra, unsigned int extra2);
+void sort_snaps(int halt_after_sort);
+
+#endif /* __ASSEMBLY__ */
+
+#endif /* CONFIG_ARC_DBG_EVENT_TIMELINE */
+
+#endif /* __ASM_ARC_EVENT_PROFILE_H */
diff --git a/arch/arc/kernel/Makefile b/arch/arc/kernel/Makefile
index 9151bbe..ec1f130 100644
--- a/arch/arc/kernel/Makefile
+++ b/arch/arc/kernel/Makefile
@@ -12,6 +12,7 @@ obj-y := arcksyms.o setup.o irq.o time.o reset.o ptrace.o entry.o process.o \
signal.o traps.o sys.o troubleshoot.o stacktrace.o

obj-$(CONFIG_MODULES) += arcksyms.o module.o
+obj-$(CONFIG_ARC_DBG_EVENT_TIMELINE) += event-log.o
obj-$(CONFIG_ARC_FPU_SAVE_RESTORE) += fpu.o
CFLAGS_fpu.o += -mdpfp

diff --git a/arch/arc/kernel/asm-offsets.c b/arch/arc/kernel/asm-offsets.c
index b0e7254..0c06b7a 100644
--- a/arch/arc/kernel/asm-offsets.c
+++ b/arch/arc/kernel/asm-offsets.c
@@ -13,6 +13,7 @@
#include <linux/thread_info.h>
#include <asm/page.h>
#include <linux/kbuild.h>
+#include <asm/event-log.h>

int main(void)
{
@@ -45,5 +46,19 @@ int main(void)

DEFINE(MM_CTXT_ASID, offsetof(mm_context_t, asid));

+#ifdef CONFIG_ARC_DBG_EVENT_TIMELINE
+ BLANK();
+ DEFINE(EVLOG_FIELD_EXTRA, offsetof(timeline_log_t, extra));
+ DEFINE(EVLOG_FIELD_EFA, offsetof(timeline_log_t, fault_addr));
+ DEFINE(EVLOG_FIELD_CAUSE, offsetof(timeline_log_t, cause));
+ DEFINE(EVLOG_FIELD_TASK, offsetof(timeline_log_t, task));
+ DEFINE(EVLOG_FIELD_TIME, offsetof(timeline_log_t, time));
+ DEFINE(EVLOG_FIELD_EVENT_ID, offsetof(timeline_log_t, event));
+ DEFINE(EVLOG_FIELD_SP, offsetof(timeline_log_t, sp));
+ DEFINE(EVLOG_RECORD_SZ, sizeof(timeline_log_t));
+ DEFINE(EVLOG_FIELD_EXTRA2, offsetof(timeline_log_t, extra2));
+ DEFINE(EVLOG_FIELD_EXTRA3, offsetof(timeline_log_t, extra3));
+#endif
+
return 0;
}
diff --git a/arch/arc/kernel/entry.S b/arch/arc/kernel/entry.S
index 13a0052..5babb8a 100644
--- a/arch/arc/kernel/entry.S
+++ b/arch/arc/kernel/entry.S
@@ -148,6 +148,7 @@ VECTOR reserved ; Reserved Exceptions
#include <asm/errno.h>
#include <asm/arcregs.h>
#include <asm/irqflags.h>
+#include <asm/event-log-asm.h>

;##################### Scratch Mem for IRQ stack switching #############

@@ -213,6 +214,8 @@ ARC_ENTRY handle_interrupt_level2
st r9, [r10, THREAD_INFO_PREEMPT_COUNT]

1:
+ TAKE_SNAP_C_FROM_ASM SNAP_INTR_IN2
+
;------------------------------------------------------
; setup params for Linux common ISR and invoke it
;------------------------------------------------------
@@ -245,6 +248,9 @@ ARC_ENTRY handle_interrupt_level1
SWITCH_TO_KERNEL_STK
SAVE_ALL_INT1

+ ; snapshot routine takes care of disabling nested intr
+ TAKE_SNAP_C_FROM_ASM SNAP_INTR_IN
+
lr r0, [icause1]
and r0, r0, 0x1f

@@ -267,6 +273,8 @@ ARC_ENTRY instr_service

EXCPN_PROLOG_FREEUP_REG r9

+ TAKE_SNAP_ASM r8, r9, SNAP_EXCP_IN
+
lr r9, [erstatus]

SWITCH_TO_KERNEL_STK
@@ -291,6 +299,8 @@ ARC_ENTRY mem_service

EXCPN_PROLOG_FREEUP_REG r9

+ TAKE_SNAP_ASM r8, r9, SNAP_EXCP_IN
+
lr r9, [erstatus]

SWITCH_TO_KERNEL_STK
@@ -342,6 +352,8 @@ ARC_ENTRY EV_TLBProtV

EXCPN_PROLOG_FREEUP_REG r9

+ TAKE_SNAP_ASM r8, r9, SNAP_EXCP_IN
+
;Which mode (user/kernel) was the system in when Exception occured
lr r9, [erstatus]

@@ -400,6 +412,8 @@ ARC_ENTRY EV_PrivilegeV

EXCPN_PROLOG_FREEUP_REG r9

+ TAKE_SNAP_ASM r8, r9, SNAP_EXCP_IN
+
lr r9, [erstatus]

SWITCH_TO_KERNEL_STK
@@ -526,6 +540,8 @@ ARC_ENTRY EV_Trap
; Need at least 1 reg to code the early exception prolog
EXCPN_PROLOG_FREEUP_REG r9

+ TAKE_SNAP_ASM r10, r9, SNAP_TRAP_IN
+
;Which mode (user/kernel) was the system in when intr occured
lr r9, [erstatus]

@@ -733,6 +749,7 @@ not_exception:
st r9, [r10, THREAD_INFO_PREEMPT_COUNT]

149:
+ TAKE_SNAP_C_FROM_ASM SNAP_INTR_OUT2
;return from level 2
RESTORE_ALL_INT2
debug_marker_l2:
@@ -745,6 +762,7 @@ not_level2_interrupt:
bbit0 r10, STATUS_A1_BIT, not_level1_interrupt

;return from level 1
+ TAKE_SNAP_C_FROM_ASM SNAP_INTR_OUT

RESTORE_ALL_INT1
debug_marker_l1:
@@ -754,6 +772,19 @@ not_level1_interrupt:

;this case is for syscalls or Exceptions (with fake rtie)

+#ifdef CONFIG_ARC_DBG_EVENT_TIMELINE
+ ld r8, [sp, PT_orig_r8]
+ cmp r8, NR_syscalls+1
+ jeq 149f
+ TAKE_SNAP_ASM r9, r10, SNAP_TRAP_OUT
+ j 150f
+
+149:
+ TAKE_SNAP_ASM r9, r10, SNAP_EXCP_OUT
+150:
+
+#endif
+
RESTORE_ALL_SYS
debug_marker_syscall:
rtie
diff --git a/arch/arc/kernel/event-log.c b/arch/arc/kernel/event-log.c
new file mode 100644
index 0000000..f8ab807
--- /dev/null
+++ b/arch/arc/kernel/event-log.c
@@ -0,0 +1,304 @@
+/*
+ * event-log.c : Poorman's version of LTT for low level event capturing
+ *
+ * captures IRQ/Exceptions/Sys-Calls/arbitrary function call
+ * XXX: Not SMP Safe
+ *
+ * Copyright (C) 2004, 2007-2010, 2011-2012 Synopsys, Inc. (http://www.synopsys.com)
+ *
+ * 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.
+ *
+ * vineetg Jan 2009
+ * -Converted strcpy to strncpy
+ *
+ * vineetg: Feb 2008: Event capturing Framework
+ * -Captures the event-id and related info in circular log buffer
+ *
+ * -USAGE:
+ * Events are defined in API file, include/asm-arc/event-log.h
+ * To log the event, "C" code calls API
+ * take_snap(event-id, event-specific-info)
+ * To log the event, ASM caller calls a "asm" macro
+ * TAKE_SNAP_ASM reg-x, reg-y, event-id
+ * To stop the capture and sort the log buffer,
+ * sort_snaps(halt-after-sort)
+ *
+ * -The reason for 2 APIs is that in low level handlers
+ * which we are interested in capturing, often don't have
+ * stack switched, thus a "C" API wont work. Also there
+ * is a very strict requirement of which registers are usable
+ * hence the 2 regs
+ *
+ * -Done primarily to chase the Random Segmentation Faults
+ * when we switched from gcc 3.4 to 4.2
+ */
+
+#include <linux/sort.h>
+#include <linux/sched.h>
+#include <linux/interrupt.h>
+#include <asm/current.h>
+#include <asm/event-log.h>
+#include <linux/module.h>
+#include <linux/reboot.h>
+
+#ifdef CONFIG_ARC_CURR_IN_REG
+/*
+ * current on ARC is a register variable "r25" setup on entry to kernel and
+ * restored back to user value on return.
+ * However if the event snap shotting return is called very late from
+ * ISR/Exception return code, r25 might already have been restored to user
+ * value, hence would no longer point to current task. This can cause weird
+ * de-referencing crashes. Safest option is to undef it and instead define
+ * it in terms of current_thread_info() which is derived from SP
+ */
+#undef current
+#define current (current_thread_info()->task)
+#endif
+
+/*
+ * Log buffer which stores the event info
+ *
+ * There is race condition when the counter goes 1 more than
+ * max-value (if IRQ sneaks in in the logging routine. Since
+ * we don't want to do fancy intr-enable-disable etc,
+ * we keep 1 extra element in log buffer
+ */
+timeline_log_t timeline_log[MAX_SNAPS + 1];
+
+/* counter in log bugger for next entry */
+int timeline_ctr;
+
+/* Used in the low level asm handler to free up a reg */
+int tmp_save_reg, tmp_save_reg2;
+int l2_ctr;
+
+/* Event capture API */
+void take_snap(int event, unsigned int arg1, unsigned int arg2)
+{
+ timeline_log_t *entry = &timeline_log[timeline_ctr];
+
+ entry->time = read_aux_reg(ARC_REG_TIMER1_CNT);
+ entry->task = current->pid;
+ entry->event = event;
+ entry->extra2 = read_aux_reg(0xa); /* status32 */
+
+ entry->cause = read_aux_reg(0x403); /* ecr */
+ entry->fault_addr = read_aux_reg(0x404); /* efa */
+
+ entry->extra = arg1;
+ entry->sp = arg2;
+
+ entry->extra3 =
+ (unsigned int)__builtin_return_address(0);
+
+ if (timeline_ctr == (MAX_SNAPS - 1))
+ timeline_ctr = 0;
+ else
+ timeline_ctr++;
+
+}
+EXPORT_SYMBOL(take_snap);
+
+void take_snap2(int event)
+{
+ unsigned long x, flags = 0, stat32;
+ timeline_log_t *entry = &timeline_log[timeline_ctr];
+
+ stat32 = read_aux_reg(0xa); /* status32 */
+
+ /* In case this is for Level 1 ISR, disable further Interrupts
+ * so that timeline_ctr is not clobbered
+ */
+ if (event == SNAP_INTR_IN)
+ local_irq_save(flags);
+
+ entry->time = read_aux_reg(ARC_REG_TIMER1_CNT);
+ entry->task = current->pid;
+ entry->event = event;
+ entry->extra2 = stat32;
+
+ entry->sp = current_thread_info()->preempt_count;
+
+ if (event == SNAP_INTR_IN2) {
+ entry->cause = read_aux_reg(0x40B); /* icause2 */
+ entry->extra = read_aux_reg(0x0C); /* statsu32_l2 */
+ __asm__ __volatile__("mov %0, ilink2 \r\n" : "=r"(x));
+ entry->fault_addr = x;
+ } else if (event == SNAP_INTR_IN) {
+ entry->cause = read_aux_reg(0x40A); /* icause1 */
+ entry->extra = read_aux_reg(0x0B); /* statsu32_l1 */
+ __asm__ __volatile__("mov %0, ilink1 \r\n" : "=r"(x));
+ entry->fault_addr = x;
+ }
+
+ if (timeline_ctr == (MAX_SNAPS - 1))
+ timeline_ctr = 0;
+ else
+ timeline_ctr++;
+
+ if (current_thread_info()->preempt_count == 0xFFFFFFFF)
+ sort_snaps(1);
+
+ if (event == SNAP_INTR_IN)
+ local_irq_restore(flags);
+}
+EXPORT_SYMBOL(take_snap2);
+
+/* CMP routine called by event sort
+ * When comparing the click time entries of @a to @b:
+ * gt: returns 1
+ * lt: -1
+ * eq: returns 0
+ */
+static int snap_cmp(const void *a, const void *b)
+{
+ timeline_log_t *click_a, *click_b;
+
+ click_a = (timeline_log_t *) a;
+ click_b = (timeline_log_t *) b;
+
+ if (click_a->time == click_b->time)
+ return 0;
+ else if (click_a->time < click_b->time)
+ return -1;
+
+ return 1;
+}
+
+/* Event Sort API, so that counter Rollover is not visibel to user */
+void sort_snaps(int halt_after_sort)
+{
+ int i;
+ unsigned int flags, tmp;
+
+ /* TODO SMP */
+ local_irq_save(flags);
+
+ take_snap(SNAP_SENTINEL, 0, 0);
+
+ sort(timeline_log, MAX_SNAPS, sizeof(timeline_log_t), snap_cmp, NULL);
+
+ for (i = 0; i < MAX_SNAPS; i++) {
+ memset(timeline_log[i].nm, 0, 16);
+
+ switch (timeline_log[i].event) {
+ case SNAP_TLB_FLUSH_ALL:
+ strcpy(timeline_log[i].nm, "TLB FLUSH ALL");
+ break;
+ case 85:
+ strcpy(timeline_log[i].nm, "FORK");
+ break;
+ case 86:
+ strcpy(timeline_log[i].nm, "EXEC");
+ break;
+ case 99:
+ strcpy(timeline_log[i].nm, "Slow-TLB-Write");
+ break;
+ case SNAP_EXCP_IN:
+ switch (timeline_log[i].cause >> 16) {
+ case 0x21:
+ strcpy(timeline_log[i].nm, "I-TLB");
+ break;
+ case 0x22:
+ strcpy(timeline_log[i].nm, "D-TLB");
+ break;
+ case 0x23:
+ strcpy(timeline_log[i].nm, "PROT-V-TLB");
+ break;
+ default:
+ strcpy(timeline_log[i].nm, "?#?");
+ break;
+ }
+ break;
+ case SNAP_EXCP_OUT_FAST:
+ strcpy(timeline_log[i].nm, "TLB Refill");
+ break;
+ case SNAP_EXCP_OUT:
+ strcpy(timeline_log[i].nm, "Excp-RET");
+ break;
+ case SNAP_TRAP_IN:
+ strcpy(timeline_log[i].nm, "SyCall :");
+ switch (timeline_log[i].cause) {
+ case 1:
+ strcat(timeline_log[i].nm, "Exit");
+ break;
+ case 2:
+ strcat(timeline_log[i].nm, "fork");
+ break;
+ case 114:
+ strcat(timeline_log[i].nm, "wait4");
+ break;
+ default:
+ strcat(timeline_log[i].nm, "???");
+ }
+ break;
+ case SNAP_TRAP_OUT:
+ strcpy(timeline_log[i].nm, "SyCall-RET");
+ break;
+ case SNAP_PRE_CTXSW_2_U:
+ strcpy(timeline_log[i].nm, "2-U-Ctx-sw");
+ break;
+ case SNAP_SENTINEL:
+ memset(&timeline_log[i], 0, sizeof(timeline_log[i]));
+ strcpy(timeline_log[i].nm, "----------");
+ break;
+ case SNAP_PRE_CTXSW_2_K:
+ strcpy(timeline_log[i].nm, "2-K-Ctx-sw");
+ break;
+ case SNAP_INTR_OUT:
+ strcpy(timeline_log[i].nm, "IRQ-OUT");
+ break;
+ case SNAP_INTR_OUT2:
+ strcpy(timeline_log[i].nm, "IRQ(2)-OUT");
+ break;
+ case SNAP_INTR_IN:
+ strcpy(timeline_log[i].nm, "IRQ-in");
+ break;
+ case SNAP_INTR_IN2:
+ strcpy(timeline_log[i].nm, "IRQ(2)-in");
+ break;
+ case SNAP_DO_PF_EXIT:
+ strcpy(timeline_log[i].nm, "PF-RET");
+ break;
+ case SNAP_PREEMPT_SCH_IRQ:
+ strcpy(timeline_log[i].nm, "Prem-Sch IRQ");
+ break;
+ case SNAP_PREEMPT_SCH:
+ strcpy(timeline_log[i].nm, "Prem-Sch");
+ break;
+ case SNAP_DO_PF_ENTER:
+ tmp = timeline_log[i].cause >> 16;
+ switch (tmp) {
+ case 0x21:
+ strcpy(timeline_log[i].nm, "PF-in:I-TLB");
+ break;
+ case 0x22:
+ strcpy(timeline_log[i].nm, "PF-in:D-TLB");
+ break;
+ case 0x23:
+ strcpy(timeline_log[i].nm, "PF-in:PROTV");
+ break;
+ default:
+ strcpy(timeline_log[i].nm, "PF-in:???");
+ break;
+ }
+ break;
+ case SNAP_SIGRETURN:
+ strcpy(timeline_log[i].nm, "sigreturn");
+ break;
+ case SNAP_BEFORE_SIG:
+ strcpy(timeline_log[i].nm, "before sig");
+ break;
+ }
+
+ }
+
+ if (halt_after_sort)
+ __asm__("flag 1");
+ else
+ local_irq_restore(flags);
+
+}
+EXPORT_SYMBOL(sort_snaps);
diff --git a/arch/arc/kernel/signal.c b/arch/arc/kernel/signal.c
index bc834da..2ec908b 100644
--- a/arch/arc/kernel/signal.c
+++ b/arch/arc/kernel/signal.c
@@ -54,6 +54,7 @@
#include <linux/syscalls.h>
#include <linux/tracehook.h>
#include <asm/ucontext.h>
+#include <asm/event-log.h>

#define _BLOCKABLE (~(sigmask(SIGKILL) | sigmask(SIGSTOP)))

@@ -137,6 +138,8 @@ SYSCALL_DEFINE0(rt_sigreturn)
if (do_sigaltstack(&sf->uc.uc_stack, NULL, regs->sp) == -EFAULT)
goto badframe;

+ take_snap(SNAP_SIGRETURN, 0, 0);
+
return regs->r0;

badframe:
diff --git a/arch/arc/mm/tlb.c b/arch/arc/mm/tlb.c
index c10111d..0e5cb9f 100644
--- a/arch/arc/mm/tlb.c
+++ b/arch/arc/mm/tlb.c
@@ -56,6 +56,7 @@
#include <asm/setup.h>
#include <asm/mmu_context.h>
#include <asm/tlb.h>
+#include <asm/event-log.h>

/* Need for ARC MMU v2
*
@@ -191,6 +192,8 @@ noinline void local_flush_tlb_all(void)
unsigned int entry;
struct cpuinfo_arc_mmu *mmu = &cpuinfo_arc700[smp_processor_id()].mmu;

+ take_snap(SNAP_TLB_FLUSH_ALL, 0, 0);
+
local_irq_save(flags);

/* Load PD0 and PD1 with template for a Blank Entry */
diff --git a/arch/arc/mm/tlbex.S b/arch/arc/mm/tlbex.S
index fc5b971..86804d3 100644
--- a/arch/arc/mm/tlbex.S
+++ b/arch/arc/mm/tlbex.S
@@ -41,6 +41,7 @@
#include <asm/entry.h>
#include <asm/tlb.h>
#include <asm/pgtable.h>
+#include <asm/event-log-asm.h>
#include <asm/arcregs.h>
#include <asm/cache.h>
#include <asm/processor.h>
@@ -194,6 +195,9 @@ ex_saved_reg1:
st_s r2, [r0, 8]
st_s r3, [r0, 12]

+ ; take a snapshot of upon entering FAST Path TLB Hdlr
+ TAKE_SNAP_ASM r0, r1, SNAP_EXCP_IN
+
; VERIFY if the ASID in MMU-PID Reg is same as
; one in Linux data structures

@@ -317,6 +321,9 @@ do_slow_path_pf:
; That requires freeing up r9
EXCPN_PROLOG_FREEUP_REG r9

+ ; take a snapshot of upon entering SLOW Path TLB Hdlr
+ TAKE_SNAP_ASM r8, r9, SNAP_DO_PF_ENTER
+
lr r9, [erstatus]

SWITCH_TO_KERNEL_STK
--
1.7.4.1

2012-11-12 13:50:28

by Arnd Bergmann

[permalink] [raw]
Subject: Re: [RFC Patch v1 32/55] ARC: [optim] Cache "current" in Register r25

On Monday 12 November 2012, [email protected] wrote:
>
> endmenu # "Platform Board Configuration"
>
> +config ARC_CURR_IN_REG
> + bool "Dedicate Register r25 for current_task pointer"
> + default y
> + help
> + This reserved Register R25 to point to Current Task in
> + kernel mode. This saves memory access for each such access
> +

This looks like a useful feature if you have 32 GPRs, but why make it
optional? Are there cases where you would disable it?

Another alternative to caching "current" would be to cache the per-cpu
offset, and calculate current from that one.

Arnd

2012-11-12 13:51:19

by Arnd Bergmann

[permalink] [raw]
Subject: Re: [RFC Patch v1 33/55] ARC: ptrace support

On Monday 12 November 2012, [email protected] wrote:
> + case PTRACE_PEEKUSR:
> + pos = addr; /* offset in struct user_regs_struct */
> + count = 4; /* 1 register only */
> + u_addr = (unsigned int __user *)data;
> + kbuf = NULL;
> + ret = genregs_get(child, NULL, pos, count, kbuf, u_addr);
> + break;
> +
> + case PTRACE_POKEUSR:
> + pos = addr; /* offset in struct user_regs_struct */
> + count = 4; /* 1 register only */
> +
> + /* Ideally @data would have abeen a user space buffer, from
> + * where, we do a copy_from_user.
> + * However this request only involves one word, which courtesy
> + * our ABI can be passed in a reg.
> + * regset interface however expects some buffer to copyin from
> + */
> + kbuf = &data;
> + u_addr = NULL;
> +
> + ret = genregs_set(child, NULL, pos, count, kbuf, u_addr);
> + break;
> +

I think these should be moved into regsets, as we have done for all
other new architectures. Your arch_ptrace then becomes a trivial wrapper
around ptrace_request.

Arnd

2012-11-12 13:53:27

by Arnd Bergmann

[permalink] [raw]
Subject: Re: [RFC Patch v1 37/55] ARC: dynamic loadable module support

On Monday 12 November 2012, [email protected] wrote:
> +void *module_alloc(unsigned long size)
> +{
> + if (size == 0)
> + return NULL;
> +
> + return vmalloc(size);
> +
> +}
> +
> +void module_free(struct module *module, void *region)
> +{
> + vfree(region);
> +}
> +
> +int module_frob_arch_sections(Elf_Ehdr *hdr, Elf_Shdr *sechdrs,
> + char *secstr, struct module *mod)
> +{
> + return 0;
> +}
> +
> +void module_arch_cleanup(struct module *mod)
> +{
> +}
> +
> +int module_finalize(const Elf32_Ehdr *hdr, const Elf_Shdr *sechdrs,
> + struct module *mod)
> +{
> + return 0;
> +}

All of these are identical to the default implementation in kernel/module.c
and you can simply drop your own definitions.

Arnd

2012-11-12 13:55:34

by Arnd Bergmann

[permalink] [raw]
Subject: Re: [RFC Patch v1 38/55] ARC: Low level event capture/logging

On Monday 12 November 2012, [email protected] wrote:
> +EXPORT_SYMBOL(take_snap);
> +
> ...
> +EXPORT_SYMBOL(take_snap2);

Where are these functions called?

Shouldn't this all just be part of the perf module?

If not, can you make the exports GPL-only?

Arnd

2012-11-12 14:00:22

by Arnd Bergmann

[permalink] [raw]
Subject: Re: [RFC Patch v1 45/55] ARC: unaligned access emulation

On Monday 12 November 2012, [email protected] wrote:
> +
> +config ARC_MISALIGN_ACCESS
> + bool "Emulate unaligned memory access (userspace only)"
> + default N
> + help
> + This enables misaligned 16 & 32 bit memory access from user space.
> + Use ONLY-IF-ABS-NECESSARY as it will be very slow and also can hide
> + potential bugs in code

parisc and ia64 both have the same feature, and they use sysctl to configure
it at run-time. Maybe you should pick one of the two sysctl implementations
and hook that up to your implementation as well.

Arnd

2012-11-12 14:02:56

by Arnd Bergmann

[permalink] [raw]
Subject: Re: [RFC Patch v1 48/55] ARC: [plat-arfpga] BVCI Latency Unit setup

On Monday 12 November 2012, [email protected] wrote:
> + /* TBD: rewrite this using I/O macros */
> + volatile unsigned int *base = (unsigned int *)BVCI_LAT_UNIT_BASE;
> + volatile unsigned int *lat_unit = (unsigned int *)base + 21;
> + volatile unsigned int *lat_val = (unsigned int *)base + 22;

Yes, you should do as the comment says.

Arnd

2012-11-12 14:10:15

by Arnd Bergmann

[permalink] [raw]
Subject: Re: [RFC Patch v1 52/55] ARC: Support for single cycle Close Coupled Mem (CCM)

On Monday 12 November 2012, [email protected] wrote:
> +config ARC_DCCM_SZ
> + int "DCCM Size in KB"
> + default "64"
> + depends on ARC_HAS_DCCM
> +
> +config ARC_DCCM_BASE
> + hex "DCCM map address"
> + default "0xA0000000"
> + depends on ARC_HAS_DCCM
> +

I suppose these should be configured from the device tree at boot time, not
during the build process.

I also wonder if the interface should be unified with what we use for
accessing the sram on some of the ARM platforms.

Arnd

2012-11-15 10:23:22

by Vineet Gupta

[permalink] [raw]
Subject: Re: [RFC Patch v1 32/55] ARC: [optim] Cache "current" in Register r25

On Monday 12 November 2012 07:20 PM, Arnd Bergmann wrote:
> On Monday 12 November 2012, [email protected] wrote:
>> endmenu # "Platform Board Configuration"
>>
>> +config ARC_CURR_IN_REG
>> + bool "Dedicate Register r25 for current_task pointer"
>> + default y
>> + help
>> + This reserved Register R25 to point to Current Task in
>> + kernel mode. This saves memory access for each such access
>> +
> This looks like a useful feature if you have 32 GPRs, but why make it
> optional? Are there cases where you would disable it?

Since it touched the low level guts of port, it made sense to start with
a config option in initial implementation. But I've kept it that way
since it annotates the code nicely and implicitly documents how to do
this for any other register + data structure.

> Another alternative to caching "current" would be to cache the per-cpu
> offset, and calculate current from that one.

Good tip - I'll add this to my TODO list -

> Arnd

2012-11-15 10:24:20

by Vineet Gupta

[permalink] [raw]
Subject: Re: [RFC Patch v1 33/55] ARC: ptrace support

On Monday 12 November 2012 07:21 PM, Arnd Bergmann wrote:
> On Monday 12 November 2012, [email protected] wrote:
>> + case PTRACE_PEEKUSR:
>> + pos = addr; /* offset in struct user_regs_struct */
>> + count = 4; /* 1 register only */
>> + u_addr = (unsigned int __user *)data;
>> + kbuf = NULL;
>> + ret = genregs_get(child, NULL, pos, count, kbuf, u_addr);
>> + break;
>> +
>> + case PTRACE_POKEUSR:
>> + pos = addr; /* offset in struct user_regs_struct */
>> + count = 4; /* 1 register only */
>> +
>> + /* Ideally @data would have abeen a user space buffer, from
>> + * where, we do a copy_from_user.
>> + * However this request only involves one word, which courtesy
>> + * our ABI can be passed in a reg.
>> + * regset interface however expects some buffer to copyin from
>> + */
>> + kbuf = &data;
>> + u_addr = NULL;
>> +
>> + ret = genregs_set(child, NULL, pos, count, kbuf, u_addr);
>> + break;
>> +
> I think these should be moved into regsets, as we have done for all
> other new architectures. Your arch_ptrace then becomes a trivial wrapper
> around ptrace_request.

The regset stuff is already complete - and even this code uses the reget
callbacks. It's just that we need to carry on this dual ABI for some
time for our legacy gdbserver.

>
> Arnd

2012-11-15 10:30:04

by Vineet Gupta

[permalink] [raw]
Subject: Re: [RFC Patch v1 37/55] ARC: dynamic loadable module support

On Monday 12 November 2012 07:23 PM, Arnd Bergmann wrote:
> On Monday 12 November 2012, [email protected] wrote:
>> +void *module_alloc(unsigned long size)
>> +{
>> + if (size == 0)
>> + return NULL;
>> +
>> + return vmalloc(size);
>> +
>> +}
>> +
>> +void module_free(struct module *module, void *region)
>> +{
>> + vfree(region);
>> +}
>> +
>> +int module_frob_arch_sections(Elf_Ehdr *hdr, Elf_Shdr *sechdrs,
>> + char *secstr, struct module *mod)
>> +{
>> + return 0;
>> +}
>> +
>> +void module_arch_cleanup(struct module *mod)
>> +{
>> +}
>> +
>> +int module_finalize(const Elf32_Ehdr *hdr, const Elf_Shdr *sechdrs,
>> + struct module *mod)
>> +{
>> + return 0;
>> +}
> All of these are identical to the default implementation in kernel/module.c
> and you can simply drop your own definitions.

Alloc/free dropped. Rest 3 needed for ARC specific unwinding out of
loadable modules.

>
> Arnd

2012-11-15 10:43:39

by Vineet Gupta

[permalink] [raw]
Subject: Re: [RFC Patch v1 38/55] ARC: Low level event capture/logging

On Monday 12 November 2012 07:25 PM, Arnd Bergmann wrote:
> On Monday 12 November 2012, [email protected] wrote:
>> +EXPORT_SYMBOL(take_snap);
>> +
>> ...
>> +EXPORT_SYMBOL(take_snap2);
> Where are these functions called?

These are called from various parts of ARCH code, such as before
handling signal or TLB flush etc.

> Shouldn't this all just be part of the perf module?

These are for low level ARCH specific event snapshotting. Maybe
perf/ftrace already have some of these in the generic code which
eventually call the ARCH APIs. Our current perf support is just husk of
an implementation. Once we have the full perf / ftrace support this
module - except for the even t capture part could just go away.
OTOH, I've not seen much usage of this from loadable modules - so if you
deem correct, I can even remove the export.

> If not, can you make the exports GPL-only?

Am I right in understanding that this is more related to discouraging
non GPL modules "in general" than having to do with port itself.

>
> Arnd

2012-11-15 11:56:55

by Arnd Bergmann

[permalink] [raw]
Subject: Re: [RFC Patch v1 33/55] ARC: ptrace support

On Thursday 15 November 2012, Vineet Gupta wrote:
> On Monday 12 November 2012 07:21 PM, Arnd Bergmann wrote:
>
> > I think these should be moved into regsets, as we have done for all
> > other new architectures. Your arch_ptrace then becomes a trivial wrapper
> > around ptrace_request.
>
> The regset stuff is already complete - and even this code uses the reget
> callbacks. It's just that we need to carry on this dual ABI for some
> time for our legacy gdbserver.

Ok, but please don't submit that patch upstream then. It's ok to
keep it in your own tree for the transitional phase, I assume
that you have other patches in there as well that are not submitted
yet. Once the new gdb is in use everywhere, you can just drop that
patch.

The problem with having this upstream is that this would create
a precedent for having the interface, and if people start relying
on it being there, we can't remove from upstream any more. If you
just have it in your private repository, we can just argue that it
was never officially supported.

Arnd

2012-11-15 12:04:25

by Arnd Bergmann

[permalink] [raw]
Subject: Re: [RFC Patch v1 38/55] ARC: Low level event capture/logging

On Thursday 15 November 2012, Vineet Gupta wrote:
> On Monday 12 November 2012 07:25 PM, Arnd Bergmann wrote:
> > On Monday 12 November 2012, [email protected] wrote:
> >> +EXPORT_SYMBOL(take_snap);
> >> +
> >> ...
> >> +EXPORT_SYMBOL(take_snap2);
> > Where are these functions called?
>
> These are called from various parts of ARCH code, such as before
> handling signal or TLB flush etc.
>
> > Shouldn't this all just be part of the perf module?
>
> These are for low level ARCH specific event snapshotting. Maybe
> perf/ftrace already have some of these in the generic code which
> eventually call the ARCH APIs. Our current perf support is just husk of
> an implementation. Once we have the full perf / ftrace support this
> module - except for the even t capture part could just go away.
> OTOH, I've not seen much usage of this from loadable modules - so if you
> deem correct, I can even remove the export.
>
> > If not, can you make the exports GPL-only?
>
> Am I right in understanding that this is more related to discouraging
> non GPL modules "in general" than having to do with port itself.

Mostly yes. I understand that there are some reasons why people want
to mark symbols as generally available, e.g. for standard interfaces
that have traditionally been available to every module. My rule is
usually that any newly introduced symbols should be EXPORT_SYMBOL_GPL
by default, unless there is a (documented) reason to use EXPORT_SYMBOL
instead. This is particularly true for low-level interfaces like the
one here.

If you can just remove the export, that is probably the best solution.

On a related note, any global symbol (exported or not) should normally
have a prefix that identifies the subsystem it belongs to. A global
identifier like "take_snap" can potentially conflict with symbols in
other parts of the kernel.

Arnd