diff options
Diffstat (limited to 'arch/arc/kernel')
| -rw-r--r-- | arch/arc/kernel/Makefile | 1 | ||||
| -rw-r--r-- | arch/arc/kernel/ctx_sw.c | 13 | ||||
| -rw-r--r-- | arch/arc/kernel/ctx_sw_asm.S | 15 | ||||
| -rw-r--r-- | arch/arc/kernel/devtree.c | 101 | ||||
| -rw-r--r-- | arch/arc/kernel/entry.S | 96 | ||||
| -rw-r--r-- | arch/arc/kernel/head.S | 80 | ||||
| -rw-r--r-- | arch/arc/kernel/irq.c | 28 | ||||
| -rw-r--r-- | arch/arc/kernel/kgdb.c | 12 | ||||
| -rw-r--r-- | arch/arc/kernel/kprobes.c | 8 | ||||
| -rw-r--r-- | arch/arc/kernel/perf_event.c | 326 | ||||
| -rw-r--r-- | arch/arc/kernel/process.c | 23 | ||||
| -rw-r--r-- | arch/arc/kernel/ptrace.c | 6 | ||||
| -rw-r--r-- | arch/arc/kernel/reset.c | 1 | ||||
| -rw-r--r-- | arch/arc/kernel/setup.c | 65 | ||||
| -rw-r--r-- | arch/arc/kernel/signal.c | 25 | ||||
| -rw-r--r-- | arch/arc/kernel/smp.c | 149 | ||||
| -rw-r--r-- | arch/arc/kernel/stacktrace.c | 5 | ||||
| -rw-r--r-- | arch/arc/kernel/time.c | 53 | ||||
| -rw-r--r-- | arch/arc/kernel/traps.c | 3 | ||||
| -rw-r--r-- | arch/arc/kernel/troubleshoot.c | 10 | ||||
| -rw-r--r-- | arch/arc/kernel/vmlinux.lds.S | 2 | 
21 files changed, 706 insertions, 316 deletions
diff --git a/arch/arc/kernel/Makefile b/arch/arc/kernel/Makefile index c242ef07ba7..8004b4fa646 100644 --- a/arch/arc/kernel/Makefile +++ b/arch/arc/kernel/Makefile @@ -19,6 +19,7 @@ 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_PERF_EVENTS)		+= perf_event.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 34410eb1a30..c14a5bea0c7 100644 --- a/arch/arc/kernel/ctx_sw.c +++ b/arch/arc/kernel/ctx_sw.c @@ -17,6 +17,8 @@  #include <asm/asm-offsets.h>  #include <linux/sched.h> +#define KSP_WORD_OFF 	((TASK_THREAD + THREAD_KSP) / 4) +  struct task_struct *__sched  __switch_to(struct task_struct *prev_task, struct task_struct *next_task)  { @@ -45,7 +47,16 @@ __switch_to(struct task_struct *prev_task, struct task_struct *next_task)  #endif  		/* set ksp of outgoing task in tsk->thread.ksp */ +#if KSP_WORD_OFF <= 255  		"st.as   sp, [%3, %1]    \n\t" +#else +		/* +		 * Workaround for NR_CPUS=4k +		 * %1 is bigger than 255 (S9 offset for st.as) +		 */ +		"add2    r24, %3, %1     \n\t" +		"st      sp, [r24]       \n\t" +#endif  		"sync   \n\t" @@ -97,7 +108,7 @@ __switch_to(struct task_struct *prev_task, struct task_struct *next_task)  		/* FP/BLINK restore generated by gcc (standard func epilogue */  		: "=r"(tmp) -		: "n"((TASK_THREAD + THREAD_KSP) / 4), "r"(next), "r"(prev) +		: "n"(KSP_WORD_OFF), "r"(next), "r"(prev)  		: "blink"  	); diff --git a/arch/arc/kernel/ctx_sw_asm.S b/arch/arc/kernel/ctx_sw_asm.S index d8972345e4c..e248594097e 100644 --- a/arch/arc/kernel/ctx_sw_asm.S +++ b/arch/arc/kernel/ctx_sw_asm.S @@ -10,9 +10,11 @@   *  -This is the more "natural" hand written assembler   */ +#include <linux/linkage.h>  #include <asm/entry.h>       /* For the SAVE_* macros */  #include <asm/asm-offsets.h> -#include <asm/linkage.h> + +#define KSP_WORD_OFF 	((TASK_THREAD + THREAD_KSP) / 4)  ;################### Low Level Context Switch ########################## @@ -28,8 +30,13 @@ __switch_to:  	SAVE_CALLEE_SAVED_KERNEL  	/* Save the now KSP in task->thread.ksp */ -	st.as  sp, [r0, (TASK_THREAD + THREAD_KSP)/4] - +#if KSP_WORD_OFF  <= 255 +	st.as  sp, [r0, KSP_WORD_OFF] +#else +	/* Workaround for NR_CPUS=4k as ST.as can only take s9 offset */ +	add2	r24, r0, KSP_WORD_OFF +	st	sp, [r24] +#endif  	/*  	* Return last task in r0 (return reg)  	* On ARC, Return reg = First Arg reg = r0. @@ -55,4 +62,4 @@ __switch_to:  	ld.ab   blink, [sp, 4]  	j       [blink] -ARC_EXIT __switch_to +END(__switch_to) diff --git a/arch/arc/kernel/devtree.c b/arch/arc/kernel/devtree.c index 2340af0e1d6..fffdb5e41b2 100644 --- a/arch/arc/kernel/devtree.c +++ b/arch/arc/kernel/devtree.c @@ -14,10 +14,22 @@  #include <linux/memblock.h>  #include <linux/of.h>  #include <linux/of_fdt.h> -#include <asm/prom.h>  #include <asm/clk.h>  #include <asm/mach_desc.h> +static const void * __init arch_get_next_mach(const char *const **match) +{ +	static const struct machine_desc *mdesc = __arch_info_begin; +	const struct machine_desc *m = mdesc; + +	if (m >= __arch_info_end) +		return NULL; + +	mdesc++; +	*match = m->dt_compat; +	return m; +} +  /**   * setup_machine_fdt - Machine setup when an dtb was passed to the kernel   * @dt:		virtual address pointer to dt blob @@ -25,93 +37,24 @@   * If a dtb was passed to the kernel, then use it to choose the correct   * machine_desc and to setup the system.   */ -struct machine_desc * __init setup_machine_fdt(void *dt) +const struct machine_desc * __init setup_machine_fdt(void *dt)  { -	struct boot_param_header *devtree = dt; -	struct machine_desc *mdesc = NULL, *mdesc_best = NULL; -	unsigned int score, mdesc_score = ~1; +	const struct machine_desc *mdesc;  	unsigned long dt_root; -	const char *model, *compat; -	void *clk; -	char manufacturer[16]; -	unsigned long len; +	const void *clk; +	int len; -	/* check device tree validity */ -	if (be32_to_cpu(devtree->magic) != OF_DT_HEADER) +	if (!early_init_dt_scan(dt))  		return NULL; -	initial_boot_params = devtree; -	dt_root = of_get_flat_dt_root(); - -	/* -	 * The kernel could be multi-platform enabled, thus could have many -	 * "baked-in" machine descriptors. Search thru all for the best -	 * "compatible" string match. -	 */ -	for_each_machine_desc(mdesc) { -		score = of_flat_dt_match(dt_root, mdesc->dt_compat); -		if (score > 0 && score < mdesc_score) { -			mdesc_best = mdesc; -			mdesc_score = score; -		} -	} -	if (!mdesc_best) { -		const char *prop; -		long size; - -		pr_err("\n unrecognized device tree list:\n[ "); - -		prop = of_get_flat_dt_prop(dt_root, "compatible", &size); -		if (prop) { -			while (size > 0) { -				printk("'%s' ", prop); -				size -= strlen(prop) + 1; -				prop += strlen(prop) + 1; -			} -		} -		printk("]\n\n"); - +	mdesc = of_flat_dt_match_machine(NULL, arch_get_next_mach); +	if (!mdesc)  		machine_halt(); -	} - -	/* compat = "<manufacturer>,<model>" */ -	compat =  mdesc_best->dt_compat[0]; - -	model = strchr(compat, ','); -	if (model) -		model++; - -	strlcpy(manufacturer, compat, model ? model - compat : strlen(compat)); - -	pr_info("Board \"%s\" from %s (Manufacturer)\n", model, manufacturer); - -	/* Retrieve various information from the /chosen node */ -	of_scan_flat_dt(early_init_dt_scan_chosen, boot_command_line); - -	/* Initialize {size,address}-cells info */ -	of_scan_flat_dt(early_init_dt_scan_root, NULL); - -	/* Setup memory, calling early_init_dt_add_memory_arch */ -	of_scan_flat_dt(early_init_dt_scan_memory, NULL); +	dt_root = of_get_flat_dt_root();  	clk = of_get_flat_dt_prop(dt_root, "clock-frequency", &len);  	if (clk)  		arc_set_core_freq(of_read_ulong(clk, len/4)); -	return mdesc_best; -} - -/* - * Copy the flattened DT out of .init since unflattening doesn't copy strings - * and the normal DT APIs refs them from orig flat DT - */ -void __init copy_devtree(void) -{ -	void *alloc = early_init_dt_alloc_memory_arch( -			be32_to_cpu(initial_boot_params->totalsize), 64); -	if (alloc) { -		memcpy(alloc, initial_boot_params, -				be32_to_cpu(initial_boot_params->totalsize)); -		initial_boot_params = alloc; -	} +	return mdesc;  } diff --git a/arch/arc/kernel/entry.S b/arch/arc/kernel/entry.S index b908dde8a33..83a046a7cd0 100644 --- a/arch/arc/kernel/entry.S +++ b/arch/arc/kernel/entry.S @@ -141,7 +141,7 @@ VECTOR   EV_Extension            ; 0x130, Extn Intruction Excp  (0x26)  VECTOR   reserved                ; Reserved Exceptions  .endr -#include <linux/linkage.h>   /* ARC_{EXTRY,EXIT} */ +#include <linux/linkage.h>   /* {EXTRY,EXIT} */  #include <asm/entry.h>       /* SAVE_ALL_{INT1,INT2,SYS...} */  #include <asm/errno.h>  #include <asm/arcregs.h> @@ -156,7 +156,7 @@ ARCFP_DATA int1_saved_reg  int1_saved_reg:  	.zero 4 -/* Each Interrupt level needs it's own scratch */ +/* Each Interrupt level needs its own scratch */  #ifdef CONFIG_ARC_COMPACT_IRQ_LEVELS  ARCFP_DATA int2_saved_reg @@ -184,7 +184,7 @@ reserved:		; processor restart  ; ---------------------------------------------  ;  Level 2 ISR: Can interrupt a Level 1 ISR  ; --------------------------------------------- -ARC_ENTRY handle_interrupt_level2 +ENTRY(handle_interrupt_level2)  	; TODO-vineetg for SMP this wont work  	; free up r9 as scratchpad @@ -225,14 +225,14 @@ ARC_ENTRY handle_interrupt_level2  	b   ret_from_exception -ARC_EXIT handle_interrupt_level2 +END(handle_interrupt_level2)  #endif  ; ---------------------------------------------  ;  Level 1 ISR  ; --------------------------------------------- -ARC_ENTRY handle_interrupt_level1 +ENTRY(handle_interrupt_level1)  	/* free up r9 as scratchpad */  #ifdef CONFIG_SMP @@ -250,6 +250,14 @@ ARC_ENTRY handle_interrupt_level1  	lr  r0, [icause1]  	and r0, r0, 0x1f +#ifdef CONFIG_TRACE_IRQFLAGS +	; icause1 needs to be read early, before calling tracing, which +	; can clobber scratch regs, hence use of stack to stash it +	push r0 +	TRACE_ASM_IRQ_DISABLE +	pop  r0 +#endif +  	bl.d  @arch_do_IRQ  	mov r1, sp @@ -257,7 +265,7 @@ ARC_ENTRY handle_interrupt_level1  	sr r8, [AUX_IRQ_LV12]       ; clear bit in Sticky Status Reg  	b   ret_from_exception -ARC_EXIT handle_interrupt_level1 +END(handle_interrupt_level1)  ;################### Non TLB Exception Handling ############################# @@ -265,7 +273,7 @@ ARC_EXIT handle_interrupt_level1  ; Instruction Error Exception Handler  ; --------------------------------------------- -ARC_ENTRY instr_service +ENTRY(instr_service)  	EXCEPTION_PROLOGUE @@ -276,13 +284,13 @@ ARC_ENTRY instr_service  	bl  do_insterror_or_kprobe  	b   ret_from_exception -ARC_EXIT instr_service +END(instr_service)  ; ---------------------------------------------  ; Memory Error Exception Handler  ; --------------------------------------------- -ARC_ENTRY mem_service +ENTRY(mem_service)  	EXCEPTION_PROLOGUE @@ -293,13 +301,13 @@ ARC_ENTRY mem_service  	bl  do_memory_error  	b   ret_from_exception -ARC_EXIT mem_service +END(mem_service)  ; ---------------------------------------------  ; Machine Check Exception Handler  ; --------------------------------------------- -ARC_ENTRY EV_MachineCheck +ENTRY(EV_MachineCheck)  	EXCEPTION_PROLOGUE @@ -323,13 +331,13 @@ ARC_ENTRY EV_MachineCheck  	j  do_machine_check_fault -ARC_EXIT EV_MachineCheck +END(EV_MachineCheck)  ; ---------------------------------------------  ; Protection Violation Exception Handler  ; --------------------------------------------- -ARC_ENTRY EV_TLBProtV +ENTRY(EV_TLBProtV)  	EXCEPTION_PROLOGUE @@ -337,9 +345,9 @@ ARC_ENTRY EV_TLBProtV  	;  vineetg: Mar 6th: Random Seg Fault issue #1  	;  ecr and efa were not saved in case an Intr sneaks in  	;  after fake rtie -	; +  	lr  r2, [ecr] -	lr  r1, [efa]	; Faulting Data address +	lr  r0, [efa]	; Faulting Data address  	; --------(4) Return from CPU Exception Mode ---------  	;  Fake a rtie, but rtie to next label @@ -348,6 +356,8 @@ ARC_ENTRY EV_TLBProtV  	FAKE_RET_FROM_EXCPN r9 +	mov   r1, sp +  	;------ (5) Type of Protection Violation? ----------  	;  	; ProtV Hardware Exception is triggered for Access Faults of 2 types @@ -358,16 +368,12 @@ ARC_ENTRY EV_TLBProtV  	bbit1 r2, ECR_C_BIT_PROTV_MISALIG_DATA, 4f  	;========= (6a) Access Violation Processing ======== -	mov r0, sp              ; pt_regs  	bl  do_page_fault  	b   ret_from_exception  	;========== (6b) Non aligned access ============  4: -	mov r0, r1 -	mov r1, sp              ; pt_regs -#ifdef  CONFIG_ARC_MISALIGN_ACCESS  	SAVE_CALLEE_SAVED_USER  	mov r2, sp              ; callee_regs @@ -376,18 +382,15 @@ ARC_ENTRY EV_TLBProtV  	; TBD: optimize - do this only if a callee reg was involved  	; either a dst of emulated LD/ST or src with address-writeback  	RESTORE_CALLEE_SAVED_USER -#else -	bl  do_misaligned_error -#endif  	b   ret_from_exception -ARC_EXIT EV_TLBProtV +END(EV_TLBProtV)  ; ---------------------------------------------  ; Privilege Violation Exception Handler  ; --------------------------------------------- -ARC_ENTRY EV_PrivilegeV +ENTRY(EV_PrivilegeV)  	EXCEPTION_PROLOGUE @@ -398,12 +401,12 @@ ARC_ENTRY EV_PrivilegeV  	bl  do_privilege_fault  	b   ret_from_exception -ARC_EXIT EV_PrivilegeV +END(EV_PrivilegeV)  ; ---------------------------------------------  ; Extension Instruction Exception Handler  ; --------------------------------------------- -ARC_ENTRY EV_Extension +ENTRY(EV_Extension)  	EXCEPTION_PROLOGUE @@ -414,7 +417,7 @@ ARC_ENTRY EV_Extension  	bl  do_extension_fault  	b   ret_from_exception -ARC_EXIT EV_Extension +END(EV_Extension)  ;######################### System Call Tracing ######################### @@ -470,7 +473,7 @@ trap_with_param:  	lr  r0, [efa]  	mov r1, sp -	; Now that we have read EFA, its safe to do "fake" rtie +	; Now that we have read EFA, it is safe to do "fake" rtie  	;   and get out of CPU exception mode  	FAKE_RET_FROM_EXCPN r11 @@ -501,7 +504,7 @@ trap_with_param:  ;   (2) Break Points  ;------------------------------------------------------------------ -ARC_ENTRY EV_Trap +ENTRY(EV_Trap)  	EXCEPTION_PROLOGUE @@ -531,9 +534,9 @@ ARC_ENTRY EV_Trap  	jl      [r9]        ; Entry into Sys Call Handler  	; fall through to ret_from_system_call -ARC_EXIT EV_Trap +END(EV_Trap) -ARC_ENTRY ret_from_system_call +ENTRY(ret_from_system_call)  	st  r0, [sp, PT_r0]     ; sys call return value in pt_regs @@ -543,7 +546,7 @@ ARC_ENTRY ret_from_system_call  ;  ; If ret to user mode do we need to handle signals, schedule() et al. -ARC_ENTRY ret_from_exception +ENTRY(ret_from_exception)  	; Pre-{IRQ,Trap,Exception} K/U mode from pt_regs->status32  	ld  r8, [sp, PT_status32]   ; returning to User/Kernel Mode @@ -575,6 +578,7 @@ resume_user_mode_begin:  	; --- (Slow Path #2) pending signal  ---  	mov r0, sp	; pt_regs for arg to do_signal()/do_notify_resume() +	GET_CURR_THR_INFO_FLAGS   r9  	bbit0  r9, TIF_SIGPENDING, .Lchk_notify_resume  	; Normal Trap/IRQ entry only saves Scratch (caller-saved) regs @@ -610,11 +614,13 @@ resume_user_mode_begin:  resume_kernel_mode: -#ifdef CONFIG_PREEMPT - -	; This is a must for preempt_schedule_irq() +	; Disable Interrupts from this point on +	; CONFIG_PREEMPT: This is a must for preempt_schedule_irq() +	; !CONFIG_PREEMPT: To ensure restore_regs is intr safe  	IRQ_DISABLE	r9 +#ifdef CONFIG_PREEMPT +  	; Can't preempt if preemption disabled  	GET_CURR_THR_INFO_FROM_SP   r10  	ld  r8, [r10, THREAD_INFO_PREEMPT_COUNT] @@ -640,6 +646,8 @@ resume_kernel_mode:  restore_regs : +	TRACE_ASM_IRQ_ENABLE +  	lr	r10, [status32]  	; Restore REG File. In case multiple Events outstanding, @@ -670,9 +678,9 @@ not_exception:  	brne r9, event_IRQ2, 149f  	;------------------------------------------------------------------ -	; 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 +	; if L2 IRQ interrupted an L1 ISR,  we'd disabled preemption earlier +	; so that sched doesn't move to new task, causing L1 to be delayed +	; undeterministically. Now that we've achieved that, let's reset  	; things to what they were, before returning from L2 context  	;---------------------------------------------------------------- @@ -720,15 +728,15 @@ not_level1_interrupt:  debug_marker_syscall:  	rtie -ARC_EXIT ret_from_exception +END(ret_from_exception) -ARC_ENTRY ret_from_fork +ENTRY(ret_from_fork)  	; when the forked child comes here from the __switch_to function  	; r0 has the last task pointer.  	; put last task in scheduler queue  	bl   @schedule_tail -	; If kernel thread, jump to it's entry-point +	; If kernel thread, jump to its entry-point  	ld   r9, [sp, PT_status32]  	brne r9, 0, 1f @@ -739,11 +747,11 @@ ARC_ENTRY ret_from_fork  	; special case of kernel_thread entry point returning back due to  	; kernel_execve() - pretend return from syscall to ret to userland  	b    ret_from_exception -ARC_EXIT ret_from_fork +END(ret_from_fork)  ;################### Special Sys Call Wrappers ########################## -ARC_ENTRY sys_clone_wrapper +ENTRY(sys_clone_wrapper)  	SAVE_CALLEE_SAVED_USER  	bl  @sys_clone  	DISCARD_CALLEE_SAVED_USER @@ -753,7 +761,7 @@ ARC_ENTRY sys_clone_wrapper  	bnz  tracesys_exit  	b ret_from_system_call -ARC_EXIT sys_clone_wrapper +END(sys_clone_wrapper)  #ifdef CONFIG_ARC_DW2_UNWIND  ; Workaround for bug 94179 (STAR ): diff --git a/arch/arc/kernel/head.S b/arch/arc/kernel/head.S index 0f944f02451..4d2481bd8b9 100644 --- a/arch/arc/kernel/head.S +++ b/arch/arc/kernel/head.S @@ -12,10 +12,42 @@   *      to skip certain things during boot on simulator   */ +#include <linux/linkage.h>  #include <asm/asm-offsets.h>  #include <asm/entry.h> -#include <linux/linkage.h>  #include <asm/arcregs.h> +#include <asm/cache.h> + +.macro CPU_EARLY_SETUP + +	; Setting up Vectror Table (in case exception happens in early boot +	sr	@_int_vec_base_lds, [AUX_INTR_VEC_BASE] + +	; Disable I-cache/D-cache if kernel so configured +	lr	r5, [ARC_REG_IC_BCR] +	breq    r5, 0, 1f		; I$ doesn't exist +	lr	r5, [ARC_REG_IC_CTRL] +#ifdef CONFIG_ARC_HAS_ICACHE +	bclr	r5, r5, 0		; 0 - Enable, 1 is Disable +#else +	bset	r5, r5, 0		; I$ exists, but is not used +#endif +	sr	r5, [ARC_REG_IC_CTRL] + +1: +	lr	r5, [ARC_REG_DC_BCR] +	breq    r5, 0, 1f		; D$ doesn't exist +	lr	r5, [ARC_REG_DC_CTRL] +	bclr	r5, r5, 6		; Invalidate (discard w/o wback) +#ifdef CONFIG_ARC_HAS_DCACHE +	bclr	r5, r5, 0		; Enable (+Inv) +#else +	bset	r5, r5, 0		; Disable (+Inv) +#endif +	sr	r5, [ARC_REG_DC_CTRL] + +1: +.endm  	.cpu A7 @@ -24,13 +56,13 @@  	.globl stext  stext:  	;------------------------------------------------------------------- -	; Don't clobber r0-r4 yet. It might have bootloader provided info +	; Don't clobber r0-r2 yet. It might have bootloader provided info  	;------------------------------------------------------------------- -	sr	@_int_vec_base_lds, [AUX_INTR_VEC_BASE] +	CPU_EARLY_SETUP  #ifdef CONFIG_SMP -	; Only Boot (Master) proceeds. Others wait in platform dependent way +	; Ensure 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) @@ -39,35 +71,25 @@ stext:  	; need to make sure only boot cpu takes this path.  	GET_CPU_ID  r5  	cmp	r5, 0 -	jnz	arc_platform_smp_wait_to_boot +	mov.ne	r0, r5 +	jne	arc_platform_smp_wait_to_boot  #endif  	; Clear BSS before updating any globals  	; XXX: use ZOL here  	mov	r5, __bss_start -	mov	r6, __bss_stop -1: -	st.ab   0, [r5,4] -	brlt    r5, r6, 1b - -#ifdef CONFIG_CMDLINE_UBOOT -	; support for bootloader provided cmdline -	;    If cmdline passed by u-boot, then -	;    r0 = 1  (because ATAGS parsing, now retired, used to use 0) -	;    r1 = magic number (board identity) -	;    r2 = addr of cmdline string (somewhere in memory/flash) - -	brne	r0, 1, .Lother_bootup_chores	; u-boot didn't pass cmdline -	breq	r2, 0, .Lother_bootup_chores	; or cmdline is NULL - -	mov	r5, @command_line +	sub	r6, __bss_stop, r5 +	lsr.f	lp_count, r6, 2 +	lpnz	1f +	st.ab   0, [r5, 4]  1: -	ldb.ab  r6, [r2, 1] -	breq    r6, 0, .Lother_bootup_chores -	b.d     1b -	stb.ab  r6, [r5, 1] -#endif -.Lother_bootup_chores: +	; Uboot - kernel ABI +	;    r0 = [0] No uboot interaction, [1] cmdline in r2, [2] DTB in r2 +	;    r1 = magic number (board identity, unused as of now +	;    r2 = pointer to uboot provided cmdline or external DTB in mem +	; These are handled later in setup_arch() +	st	r0, [@uboot_tag] +	st	r2, [@uboot_arg]  	; Identify if running on ISS vs Silicon  	; 	IDENTITY Reg [ 3  2  1  0 ] @@ -95,13 +117,13 @@ stext:  ;----------------------------------------------------------------  ;     First lines of code run by secondary before jumping to 'C'  ;---------------------------------------------------------------- -	.section .init.text, "ax",@progbits +	.section .text, "ax",@progbits  	.type first_lines_of_secondary, @function  	.globl first_lines_of_secondary  first_lines_of_secondary: -	sr	@_int_vec_base_lds, [AUX_INTR_VEC_BASE] +	CPU_EARLY_SETUP  	; setup per-cpu idle task as "current" on this CPU  	ld	r0, [@secondary_idle_tsk] diff --git a/arch/arc/kernel/irq.c b/arch/arc/kernel/irq.c index 5fc92455da3..7d653c0d077 100644 --- a/arch/arc/kernel/irq.c +++ b/arch/arc/kernel/irq.c @@ -39,10 +39,14 @@ void arc_init_IRQ(void)  	level_mask |= IS_ENABLED(CONFIG_ARC_IRQ5_LV2) << 5;  	level_mask |= IS_ENABLED(CONFIG_ARC_IRQ6_LV2) << 6; -	if (level_mask) { +	/* +	 * Write to register, even if no LV2 IRQs configured to reset it +	 * in case bootloader had mucked with it +	 */ +	write_aux_reg(AUX_IRQ_LEV, level_mask); + +	if (level_mask)  		pr_info("Level-2 interrupts bitset %x\n", level_mask); -		write_aux_reg(AUX_IRQ_LEV, level_mask); -	}  }  /* @@ -146,24 +150,6 @@ void arch_do_IRQ(unsigned int irq, struct pt_regs *regs)  	set_irq_regs(old_regs);  } -int __init get_hw_config_num_irq(void) -{ -	uint32_t val = read_aux_reg(ARC_REG_VECBASE_BCR); - -	switch (val & 0x03) { -	case 0: -		return 16; -	case 1: -		return 32; -	case 2: -		return 8; -	default: -		return 0; -	} - -	return 0; -} -  /*   * arch_local_irq_enable - Enable interrupts.   * diff --git a/arch/arc/kernel/kgdb.c b/arch/arc/kernel/kgdb.c index a7698fb1481..a2ff5c5d145 100644 --- a/arch/arc/kernel/kgdb.c +++ b/arch/arc/kernel/kgdb.c @@ -196,6 +196,18 @@ void kgdb_arch_set_pc(struct pt_regs *regs, unsigned long ip)  	instruction_pointer(regs) = ip;  } +static void kgdb_call_nmi_hook(void *ignored) +{ +	kgdb_nmicallback(raw_smp_processor_id(), NULL); +} + +void kgdb_roundup_cpus(unsigned long flags) +{ +	local_irq_enable(); +	smp_call_function(kgdb_call_nmi_hook, NULL, 0); +	local_irq_disable(); +} +  struct kgdb_arch arch_kgdb_ops = {  	/* breakpoint instruction: TRAP_S 0x3 */  #ifdef CONFIG_CPU_BIG_ENDIAN diff --git a/arch/arc/kernel/kprobes.c b/arch/arc/kernel/kprobes.c index 72f97822784..42b05046fad 100644 --- a/arch/arc/kernel/kprobes.c +++ b/arch/arc/kernel/kprobes.c @@ -87,13 +87,13 @@ static void __kprobes save_previous_kprobe(struct kprobe_ctlblk *kcb)  static void __kprobes restore_previous_kprobe(struct kprobe_ctlblk *kcb)  { -	__get_cpu_var(current_kprobe) = kcb->prev_kprobe.kp; +	__this_cpu_write(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; +	__this_cpu_write(current_kprobe, p);  }  static void __kprobes resume_execution(struct kprobe *p, unsigned long addr, @@ -237,7 +237,7 @@ int __kprobes arc_kprobe_handler(unsigned long addr, struct pt_regs *regs)  		return 1;  	} else if (kprobe_running()) { -		p = __get_cpu_var(current_kprobe); +		p = __this_cpu_read(current_kprobe);  		if (p->break_handler && p->break_handler(p, regs)) {  			setup_singlestep(p, regs);  			kcb->kprobe_status = KPROBE_HIT_SS; @@ -327,7 +327,7 @@ int __kprobes kprobe_fault_handler(struct pt_regs *regs, unsigned long trapnr)  		 */  		/* We increment the nmissed count for accounting, -		 * we can also use npre/npostfault count for accouting +		 * we can also use npre/npostfault count for accounting  		 * these specific fault cases.  		 */  		kprobes_inc_nmissed_count(cur); diff --git a/arch/arc/kernel/perf_event.c b/arch/arc/kernel/perf_event.c new file mode 100644 index 00000000000..63177e4cb66 --- /dev/null +++ b/arch/arc/kernel/perf_event.c @@ -0,0 +1,326 @@ +/* + * Linux performance counter support for ARC700 series + * + * Copyright (C) 2013 Synopsys, Inc. (www.synopsys.com) + * + * This code is inspired by the perf support of various other architectures. + * + * 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/errno.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/perf_event.h> +#include <linux/platform_device.h> +#include <asm/arcregs.h> + +struct arc_pmu { +	struct pmu	pmu; +	int		counter_size;	/* in bits */ +	int		n_counters; +	unsigned long	used_mask[BITS_TO_LONGS(ARC_PMU_MAX_HWEVENTS)]; +	int		ev_hw_idx[PERF_COUNT_ARC_HW_MAX]; +}; + +/* read counter #idx; note that counter# != event# on ARC! */ +static uint64_t arc_pmu_read_counter(int idx) +{ +	uint32_t tmp; +	uint64_t result; + +	/* +	 * ARC supports making 'snapshots' of the counters, so we don't +	 * need to care about counters wrapping to 0 underneath our feet +	 */ +	write_aux_reg(ARC_REG_PCT_INDEX, idx); +	tmp = read_aux_reg(ARC_REG_PCT_CONTROL); +	write_aux_reg(ARC_REG_PCT_CONTROL, tmp | ARC_REG_PCT_CONTROL_SN); +	result = (uint64_t) (read_aux_reg(ARC_REG_PCT_SNAPH)) << 32; +	result |= read_aux_reg(ARC_REG_PCT_SNAPL); + +	return result; +} + +static void arc_perf_event_update(struct perf_event *event, +				  struct hw_perf_event *hwc, int idx) +{ +	struct arc_pmu *arc_pmu = container_of(event->pmu, struct arc_pmu, pmu); +	uint64_t prev_raw_count, new_raw_count; +	int64_t delta; + +	do { +		prev_raw_count = local64_read(&hwc->prev_count); +		new_raw_count = arc_pmu_read_counter(idx); +	} while (local64_cmpxchg(&hwc->prev_count, prev_raw_count, +				 new_raw_count) != prev_raw_count); + +	delta = (new_raw_count - prev_raw_count) & +		((1ULL << arc_pmu->counter_size) - 1ULL); + +	local64_add(delta, &event->count); +	local64_sub(delta, &hwc->period_left); +} + +static void arc_pmu_read(struct perf_event *event) +{ +	arc_perf_event_update(event, &event->hw, event->hw.idx); +} + +static int arc_pmu_cache_event(u64 config) +{ +	unsigned int cache_type, cache_op, cache_result; +	int ret; + +	cache_type	= (config >>  0) & 0xff; +	cache_op	= (config >>  8) & 0xff; +	cache_result	= (config >> 16) & 0xff; +	if (cache_type >= PERF_COUNT_HW_CACHE_MAX) +		return -EINVAL; +	if (cache_op >= PERF_COUNT_HW_CACHE_OP_MAX) +		return -EINVAL; +	if (cache_result >= PERF_COUNT_HW_CACHE_RESULT_MAX) +		return -EINVAL; + +	ret = arc_pmu_cache_map[cache_type][cache_op][cache_result]; + +	if (ret == CACHE_OP_UNSUPPORTED) +		return -ENOENT; + +	return ret; +} + +/* initializes hw_perf_event structure if event is supported */ +static int arc_pmu_event_init(struct perf_event *event) +{ +	struct arc_pmu *arc_pmu = container_of(event->pmu, struct arc_pmu, pmu); +	struct hw_perf_event *hwc = &event->hw; +	int ret; + +	/* ARC 700 PMU does not support sampling events */ +	if (is_sampling_event(event)) +		return -ENOENT; + +	switch (event->attr.type) { +	case PERF_TYPE_HARDWARE: +		if (event->attr.config >= PERF_COUNT_HW_MAX) +			return -ENOENT; +		if (arc_pmu->ev_hw_idx[event->attr.config] < 0) +			return -ENOENT; +		hwc->config = arc_pmu->ev_hw_idx[event->attr.config]; +		pr_debug("initializing event %d with cfg %d\n", +			 (int) event->attr.config, (int) hwc->config); +		return 0; +	case PERF_TYPE_HW_CACHE: +		ret = arc_pmu_cache_event(event->attr.config); +		if (ret < 0) +			return ret; +		hwc->config = arc_pmu->ev_hw_idx[ret]; +		return 0; +	default: +		return -ENOENT; +	} +} + +/* starts all counters */ +static void arc_pmu_enable(struct pmu *pmu) +{ +	uint32_t tmp; +	tmp = read_aux_reg(ARC_REG_PCT_CONTROL); +	write_aux_reg(ARC_REG_PCT_CONTROL, (tmp & 0xffff0000) | 0x1); +} + +/* stops all counters */ +static void arc_pmu_disable(struct pmu *pmu) +{ +	uint32_t tmp; +	tmp = read_aux_reg(ARC_REG_PCT_CONTROL); +	write_aux_reg(ARC_REG_PCT_CONTROL, (tmp & 0xffff0000) | 0x0); +} + +/* + * Assigns hardware counter to hardware condition. + * Note that there is no separate start/stop mechanism; + * stopping is achieved by assigning the 'never' condition + */ +static void arc_pmu_start(struct perf_event *event, int flags) +{ +	struct hw_perf_event *hwc = &event->hw; +	int idx = hwc->idx; + +	if (WARN_ON_ONCE(idx == -1)) +		return; + +	if (flags & PERF_EF_RELOAD) +		WARN_ON_ONCE(!(event->hw.state & PERF_HES_UPTODATE)); + +	event->hw.state = 0; + +	/* enable ARC pmu here */ +	write_aux_reg(ARC_REG_PCT_INDEX, idx); +	write_aux_reg(ARC_REG_PCT_CONFIG, hwc->config); +} + +static void arc_pmu_stop(struct perf_event *event, int flags) +{ +	struct hw_perf_event *hwc = &event->hw; +	int idx = hwc->idx; + +	if (!(event->hw.state & PERF_HES_STOPPED)) { +		/* stop ARC pmu here */ +		write_aux_reg(ARC_REG_PCT_INDEX, idx); + +		/* condition code #0 is always "never" */ +		write_aux_reg(ARC_REG_PCT_CONFIG, 0); + +		event->hw.state |= PERF_HES_STOPPED; +	} + +	if ((flags & PERF_EF_UPDATE) && +	    !(event->hw.state & PERF_HES_UPTODATE)) { +		arc_perf_event_update(event, &event->hw, idx); +		event->hw.state |= PERF_HES_UPTODATE; +	} +} + +static void arc_pmu_del(struct perf_event *event, int flags) +{ +	struct arc_pmu *arc_pmu = container_of(event->pmu, struct arc_pmu, pmu); + +	arc_pmu_stop(event, PERF_EF_UPDATE); +	__clear_bit(event->hw.idx, arc_pmu->used_mask); + +	perf_event_update_userpage(event); +} + +/* allocate hardware counter and optionally start counting */ +static int arc_pmu_add(struct perf_event *event, int flags) +{ +	struct arc_pmu *arc_pmu = container_of(event->pmu, struct arc_pmu, pmu); +	struct hw_perf_event *hwc = &event->hw; +	int idx = hwc->idx; + +	if (__test_and_set_bit(idx, arc_pmu->used_mask)) { +		idx = find_first_zero_bit(arc_pmu->used_mask, +					  arc_pmu->n_counters); +		if (idx == arc_pmu->n_counters) +			return -EAGAIN; + +		__set_bit(idx, arc_pmu->used_mask); +		hwc->idx = idx; +	} + +	write_aux_reg(ARC_REG_PCT_INDEX, idx); +	write_aux_reg(ARC_REG_PCT_CONFIG, 0); +	write_aux_reg(ARC_REG_PCT_COUNTL, 0); +	write_aux_reg(ARC_REG_PCT_COUNTH, 0); +	local64_set(&hwc->prev_count, 0); + +	hwc->state = PERF_HES_UPTODATE | PERF_HES_STOPPED; +	if (flags & PERF_EF_START) +		arc_pmu_start(event, PERF_EF_RELOAD); + +	perf_event_update_userpage(event); + +	return 0; +} + +static int arc_pmu_device_probe(struct platform_device *pdev) +{ +	struct arc_pmu *arc_pmu; +	struct arc_reg_pct_build pct_bcr; +	struct arc_reg_cc_build cc_bcr; +	int i, j, ret; + +	union cc_name { +		struct { +			uint32_t word0, word1; +			char sentinel; +		} indiv; +		char str[9]; +	} cc_name; + + +	READ_BCR(ARC_REG_PCT_BUILD, pct_bcr); +	if (!pct_bcr.v) { +		pr_err("This core does not have performance counters!\n"); +		return -ENODEV; +	} + +	arc_pmu = devm_kzalloc(&pdev->dev, sizeof(struct arc_pmu), +			       GFP_KERNEL); +	if (!arc_pmu) +		return -ENOMEM; + +	arc_pmu->n_counters = pct_bcr.c; +	BUG_ON(arc_pmu->n_counters > ARC_PMU_MAX_HWEVENTS); + +	arc_pmu->counter_size = 32 + (pct_bcr.s << 4); +	pr_info("ARC PMU found with %d counters of size %d bits\n", +		arc_pmu->n_counters, arc_pmu->counter_size); + +	READ_BCR(ARC_REG_CC_BUILD, cc_bcr); + +	if (!cc_bcr.v) +		pr_err("Strange! Performance counters exist, but no countable conditions?\n"); + +	pr_info("ARC PMU has %d countable conditions\n", cc_bcr.c); + +	cc_name.str[8] = 0; +	for (i = 0; i < PERF_COUNT_HW_MAX; i++) +		arc_pmu->ev_hw_idx[i] = -1; + +	for (j = 0; j < cc_bcr.c; j++) { +		write_aux_reg(ARC_REG_CC_INDEX, j); +		cc_name.indiv.word0 = read_aux_reg(ARC_REG_CC_NAME0); +		cc_name.indiv.word1 = read_aux_reg(ARC_REG_CC_NAME1); +		for (i = 0; i < ARRAY_SIZE(arc_pmu_ev_hw_map); i++) { +			if (arc_pmu_ev_hw_map[i] && +			    !strcmp(arc_pmu_ev_hw_map[i], cc_name.str) && +			    strlen(arc_pmu_ev_hw_map[i])) { +				pr_debug("mapping %d to idx %d with name %s\n", +					 i, j, cc_name.str); +				arc_pmu->ev_hw_idx[i] = j; +			} +		} +	} + +	arc_pmu->pmu = (struct pmu) { +		.pmu_enable	= arc_pmu_enable, +		.pmu_disable	= arc_pmu_disable, +		.event_init	= arc_pmu_event_init, +		.add		= arc_pmu_add, +		.del		= arc_pmu_del, +		.start		= arc_pmu_start, +		.stop		= arc_pmu_stop, +		.read		= arc_pmu_read, +	}; + +	ret = perf_pmu_register(&arc_pmu->pmu, pdev->name, PERF_TYPE_RAW); + +	return ret; +} + +#ifdef CONFIG_OF +static const struct of_device_id arc_pmu_match[] = { +	{ .compatible = "snps,arc700-pmu" }, +	{}, +}; +MODULE_DEVICE_TABLE(of, arc_pmu_match); +#endif + +static struct platform_driver arc_pmu_driver = { +	.driver	= { +		.name		= "arc700-pmu", +		.of_match_table = of_match_ptr(arc_pmu_match), +	}, +	.probe		= arc_pmu_device_probe, +}; + +module_platform_driver(arc_pmu_driver); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Mischa Jonker <mjonker@synopsys.com>"); +MODULE_DESCRIPTION("ARC PMU driver"); diff --git a/arch/arc/kernel/process.c b/arch/arc/kernel/process.c index 07a3a968fe4..fdd89715d2d 100644 --- a/arch/arc/kernel/process.c +++ b/arch/arc/kernel/process.c @@ -151,6 +151,29 @@ int copy_thread(unsigned long clone_flags,  }  /* + * Do necessary setup to start up a new user task + */ +void start_thread(struct pt_regs * regs, unsigned long pc, unsigned long usp) +{ +	set_fs(USER_DS); /* user space */ + +	regs->sp = usp; +	regs->ret = pc; + +	/* +	 * [U]ser Mode bit set +	 * [L] ZOL loop inhibited to begin with - cleared by a LP insn +	 * Interrupts enabled +	 */ +	regs->status32 = STATUS_U_MASK | STATUS_L_MASK | +			 STATUS_E1_MASK | STATUS_E2_MASK; + +	/* bogus seed values for debugging */ +	regs->lp_start = 0x10; +	regs->lp_end = 0x80; +} + +/*   * Some archs flush debug and FPU info here   */  void flush_thread(void) diff --git a/arch/arc/kernel/ptrace.c b/arch/arc/kernel/ptrace.c index 333238564b6..13b3ffb27a3 100644 --- a/arch/arc/kernel/ptrace.c +++ b/arch/arc/kernel/ptrace.c @@ -102,7 +102,7 @@ static int genregs_set(struct task_struct *target,  	REG_IGNORE_ONE(pad2);  	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 */ +	REG_IGNORE_ONE(stop_pc);			/* PC updated via @ret */  	return ret;  } @@ -146,6 +146,10 @@ long arch_ptrace(struct task_struct *child, long request,  	pr_debug("REQ=%ld: ADDR =0x%lx, DATA=0x%lx)\n", request, addr, data);  	switch (request) { +	case PTRACE_GET_THREAD_AREA: +		ret = put_user(task_thread_info(child)->thr_ptr, +			       (unsigned long __user *)data); +		break;  	default:  		ret = ptrace_request(child, request, addr, data);  		break; diff --git a/arch/arc/kernel/reset.c b/arch/arc/kernel/reset.c index e227a2b1c94..2768fa1e39b 100644 --- a/arch/arc/kernel/reset.c +++ b/arch/arc/kernel/reset.c @@ -31,3 +31,4 @@ void machine_power_off(void)  }  void (*pm_power_off) (void) = NULL; +EXPORT_SYMBOL(pm_power_off); diff --git a/arch/arc/kernel/setup.c b/arch/arc/kernel/setup.c index 2c68bc7e6a7..119dddb752b 100644 --- a/arch/arc/kernel/setup.c +++ b/arch/arc/kernel/setup.c @@ -21,7 +21,6 @@  #include <asm/setup.h>  #include <asm/page.h>  #include <asm/irq.h> -#include <asm/prom.h>  #include <asm/unwind.h>  #include <asm/clk.h>  #include <asm/mach_desc.h> @@ -30,15 +29,17 @@  int running_on_hw = 1;	/* vs. on ISS */ -char __initdata command_line[COMMAND_LINE_SIZE]; -struct machine_desc *machine_desc; +/* Part of U-boot ABI: see head.S */ +int __initdata uboot_tag; +char __initdata *uboot_arg; + +const struct machine_desc *machine_desc;  struct task_struct *_current_task[NR_CPUS];	/* For stack switching */  struct cpuinfo_arc cpuinfo_arc700[NR_CPUS]; - -void read_arc_build_cfg_regs(void) +static void read_arc_build_cfg_regs(void)  {  	struct bcr_perip uncached_space;  	struct cpuinfo_arc *cpu = &cpuinfo_arc700[smp_processor_id()]; @@ -106,7 +107,7 @@ static const struct cpuinfo_data arc_cpu_tbl[] = {  	{ {0x00, NULL		} }  }; -char *arc_cpu_mumbojumbo(int cpu_id, char *buf, int len) +static char *arc_cpu_mumbojumbo(int cpu_id, char *buf, int len)  {  	int n = 0;  	struct cpuinfo_arc *cpu = &cpuinfo_arc700[cpu_id]; @@ -171,7 +172,7 @@ static const struct id_to_str mac_mul_nm[] = {  	{0x6, "Dual 16x16 and 32x16"}  }; -char *arc_extn_mumbojumbo(int cpu_id, char *buf, int len) +static char *arc_extn_mumbojumbo(int cpu_id, char *buf, int len)  {  	int n = 0;  	struct cpuinfo_arc *cpu = &cpuinfo_arc700[cpu_id]; @@ -234,7 +235,7 @@ char *arc_extn_mumbojumbo(int cpu_id, char *buf, int len)  	return buf;  } -void arc_chk_ccms(void) +static void arc_chk_ccms(void)  {  #if defined(CONFIG_ARC_HAS_DCCM) || defined(CONFIG_ARC_HAS_ICCM)  	struct cpuinfo_arc *cpu = &cpuinfo_arc700[smp_processor_id()]; @@ -269,7 +270,7 @@ void arc_chk_ccms(void)   * 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 arc_chk_fpu(void) +static void arc_chk_fpu(void)  {  	struct cpuinfo_arc *cpu = &cpuinfo_arc700[smp_processor_id()]; @@ -313,19 +314,40 @@ void setup_processor(void)  	arc_chk_fpu();  } +static inline int is_kernel(unsigned long addr) +{ +	if (addr >= (unsigned long)_stext && addr <= (unsigned long)_end) +		return 1; +	return 0; +} +  void __init setup_arch(char **cmdline_p)  { -	/* This also populates @boot_command_line from /bootargs */ -	machine_desc = setup_machine_fdt(__dtb_start); -	if (!machine_desc) -		panic("Embedded DT invalid\n"); - -	/* Append any u-boot provided cmdline */ -#ifdef CONFIG_CMDLINE_UBOOT -	/* Add a whitespace seperator between the 2 cmdlines */ -	strlcat(boot_command_line, " ", COMMAND_LINE_SIZE); -	strlcat(boot_command_line, command_line, COMMAND_LINE_SIZE); -#endif +	/* make sure that uboot passed pointer to cmdline/dtb is valid */ +	if (uboot_tag && is_kernel((unsigned long)uboot_arg)) +		panic("Invalid uboot arg\n"); + +	/* See if u-boot passed an external Device Tree blob */ +	machine_desc = setup_machine_fdt(uboot_arg);	/* uboot_tag == 2 */ +	if (!machine_desc) { +		/* No, so try the embedded one */ +		machine_desc = setup_machine_fdt(__dtb_start); +		if (!machine_desc) +			panic("Embedded DT invalid\n"); + +		/* +		 * If we are here, it is established that @uboot_arg didn't +		 * point to DT blob. Instead if u-boot says it is cmdline, +		 * Appent to embedded DT cmdline. +		 * setup_machine_fdt() would have populated @boot_command_line +		 */ +		if (uboot_tag == 1) { +			/* Ensure a whitespace between the 2 cmdlines */ +			strlcat(boot_command_line, " ", COMMAND_LINE_SIZE); +			strlcat(boot_command_line, uboot_arg, +				COMMAND_LINE_SIZE); +		} +	}  	/* Save unparsed command line copy for /proc/cmdline */  	*cmdline_p = boot_command_line; @@ -346,8 +368,7 @@ void __init setup_arch(char **cmdline_p)  	setup_arch_memory();  	/* copy flat DT out of .init and then unflatten it */ -	copy_devtree(); -	unflatten_device_tree(); +	unflatten_and_copy_device_tree();  	/* Can be issue if someone passes cmd line arg "ro"  	 * But that is unlikely so keeping it as it is diff --git a/arch/arc/kernel/signal.c b/arch/arc/kernel/signal.c index ee6ef2f60a2..7e95e1a8651 100644 --- a/arch/arc/kernel/signal.c +++ b/arch/arc/kernel/signal.c @@ -101,7 +101,6 @@ SYSCALL_DEFINE0(rt_sigreturn)  {  	struct rt_sigframe __user *sf;  	unsigned int magic; -	int err;  	struct pt_regs *regs = current_pt_regs();  	/* Always make any pending restarted system calls return -EINTR */ @@ -119,15 +118,16 @@ SYSCALL_DEFINE0(rt_sigreturn)  	if (!access_ok(VERIFY_READ, sf, sizeof(*sf)))  		goto badframe; -	err = restore_usr_regs(regs, sf); -	err |= __get_user(magic, &sf->sigret_magic); -	if (err) +	if (__get_user(magic, &sf->sigret_magic))  		goto badframe;  	if (unlikely(is_do_ss_needed(magic)))  		if (restore_altstack(&sf->uc.uc_stack))  			goto badframe; +	if (restore_usr_regs(regs, sf)) +		goto badframe; +  	/* Don't restart from sigreturn */  	syscall_wont_restart(regs); @@ -191,6 +191,15 @@ setup_rt_frame(int signo, struct k_sigaction *ka, siginfo_t *info,  		return 1;  	/* +	 * w/o SA_SIGINFO, struct ucontext is partially populated (only +	 * uc_mcontext/uc_sigmask) for kernel's normal user state preservation +	 * during signal handler execution. This works for SA_SIGINFO as well +	 * although the semantics are now overloaded (the same reg state can be +	 * inspected by userland: but are they allowed to fiddle with it ? +	 */ +	err |= stash_usr_regs(sf, regs, set); + +	/*  	 * SA_SIGINFO requires 3 args to signal handler:  	 *  #1: sig-no (common to any handler)  	 *  #2: struct siginfo @@ -213,14 +222,6 @@ setup_rt_frame(int signo, struct k_sigaction *ka, siginfo_t *info,  		magic = MAGIC_SIGALTSTK;  	} -	/* -	 * w/o SA_SIGINFO, struct ucontext is partially populated (only -	 * uc_mcontext/uc_sigmask) for kernel's normal user state preservation -	 * during signal handler execution. This works for SA_SIGINFO as well -	 * although the semantics are now overloaded (the same reg state can be -	 * inspected by userland: but are they allowed to fiddle with it ? -	 */ -	err |= stash_usr_regs(sf, regs, set);  	err |= __put_user(magic, &sf->sigret_magic);  	if (err)  		return err; diff --git a/arch/arc/kernel/smp.c b/arch/arc/kernel/smp.c index bca3052c956..c802bb50060 100644 --- a/arch/arc/kernel/smp.c +++ b/arch/arc/kernel/smp.c @@ -95,7 +95,7 @@ void __init smp_cpus_done(unsigned int max_cpus)   *        If it turns out to be elaborate, it's better to code it in assembly   *   */ -void __attribute__((weak)) arc_platform_smp_wait_to_boot(int cpu) +void __weak arc_platform_smp_wait_to_boot(int cpu)  {  	/*  	 * As a hack for debugging - since debugger will single-step over the @@ -128,6 +128,7 @@ void start_kernel_secondary(void)  	atomic_inc(&mm->mm_users);  	atomic_inc(&mm->mm_count);  	current->active_mm = mm; +	cpumask_set_cpu(cpu, mm_cpumask(mm));  	notify_cpu_starting(cpu);  	set_cpu_online(cpu, true); @@ -137,7 +138,7 @@ void start_kernel_secondary(void)  	if (machine_desc->init_smp)  		machine_desc->init_smp(smp_processor_id()); -	arc_local_timer_setup(cpu); +	arc_local_timer_setup();  	local_irq_enable();  	preempt_disable(); @@ -196,52 +197,65 @@ int __init setup_profiling_timer(unsigned int multiplier)  /*              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_EMPTY = 0,  	IPI_RESCHEDULE = 1,  	IPI_CALL_FUNC, -	IPI_CALL_FUNC_SINGLE, -	IPI_CPU_STOP +	IPI_CPU_STOP,  }; -struct ipi_data { -	unsigned long bits; -}; +/* + * In arches with IRQ for each msg type (above), receiver can use IRQ-id  to + * figure out what msg was sent. For those which don't (ARC has dedicated IPI + * IRQ), the msg-type needs to be conveyed via per-cpu data + */ -static DEFINE_PER_CPU(struct ipi_data, ipi_data); +static DEFINE_PER_CPU(unsigned long, ipi_data); -static void ipi_send_msg(const struct cpumask *callmap, enum ipi_msg_type msg) +static void ipi_send_msg_one(int cpu, enum ipi_msg_type msg)  { +	unsigned long __percpu *ipi_data_ptr = per_cpu_ptr(&ipi_data, cpu); +	unsigned long old, new;  	unsigned long flags; -	unsigned int cpu; + +	pr_debug("%d Sending msg [%d] to %d\n", smp_processor_id(), msg, cpu);  	local_irq_save(flags); -	for_each_cpu(cpu, callmap) { -		struct ipi_data *ipi = &per_cpu(ipi_data, cpu); -		set_bit(msg, &ipi->bits); -	} +	/* +	 * Atomically write new msg bit (in case others are writing too), +	 * and read back old value +	 */ +	do { +		new = old = *ipi_data_ptr; +		new |= 1U << msg; +	} while (cmpxchg(ipi_data_ptr, old, new) != old); -	/* Call the platform specific cross-CPU call function  */ -	if (plat_smp_ops.ipi_send) -		plat_smp_ops.ipi_send((void *)callmap); +	/* +	 * Call the platform specific IPI kick function, but avoid if possible: +	 * Only do so if there's no pending msg from other concurrent sender(s). +	 * Otherwise, recevier will see this msg as well when it takes the +	 * IPI corresponding to that msg. This is true, even if it is already in +	 * IPI handler, because !@old means it has not yet dequeued the msg(s) +	 * so @new msg can be a free-loader +	 */ +	if (plat_smp_ops.ipi_send && !old) +		plat_smp_ops.ipi_send(cpu);  	local_irq_restore(flags);  } +static void ipi_send_msg(const struct cpumask *callmap, enum ipi_msg_type msg) +{ +	unsigned int cpu; + +	for_each_cpu(cpu, callmap) +		ipi_send_msg_one(cpu, msg); +} +  void smp_send_reschedule(int cpu)  { -	ipi_send_msg(cpumask_of(cpu), IPI_RESCHEDULE); +	ipi_send_msg_one(cpu, IPI_RESCHEDULE);  }  void smp_send_stop(void) @@ -254,7 +268,7 @@ void smp_send_stop(void)  void arch_send_call_function_single_ipi(int cpu)  { -	ipi_send_msg(cpumask_of(cpu), IPI_CALL_FUNC_SINGLE); +	ipi_send_msg_one(cpu, IPI_CALL_FUNC);  }  void arch_send_call_function_ipi_mask(const struct cpumask *mask) @@ -265,37 +279,29 @@ void arch_send_call_function_ipi_mask(const struct cpumask *mask)  /*   * ipi_cpu_stop - handle IPI from smp_send_stop()   */ -static void ipi_cpu_stop(unsigned int cpu) +static void ipi_cpu_stop(void)  {  	machine_halt();  } -static inline void __do_IPI(unsigned long *ops, struct ipi_data *ipi, int cpu) +static inline void __do_IPI(unsigned long msg)  { -	unsigned long msg = 0; - -	do { -		msg = find_next_bit(ops, BITS_PER_LONG, msg+1); +	switch (msg) { +	case IPI_RESCHEDULE: +		scheduler_ipi(); +		break; -		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_CALL_FUNC: +		generic_smp_call_function_interrupt(); +		break; -		case IPI_CPU_STOP: -			ipi_cpu_stop(cpu); -			break; -		} -	} while (msg < BITS_PER_LONG); +	case IPI_CPU_STOP: +		ipi_cpu_stop(); +		break; +	default: +		pr_warn("IPI with unexpected msg %ld\n", msg); +	}  }  /* @@ -304,19 +310,25 @@ static inline void __do_IPI(unsigned long *ops, struct ipi_data *ipi, int cpu)   */  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; +	unsigned long pending; + +	pr_debug("IPI [%ld] received on cpu %d\n", +		 *this_cpu_ptr(&ipi_data), smp_processor_id());  	if (plat_smp_ops.ipi_clear) -		plat_smp_ops.ipi_clear(cpu, irq); +		plat_smp_ops.ipi_clear(irq);  	/* -	 * XXX: is this loop really needed -	 * And do we need to move ipi_clean inside +	 * "dequeue" the msg corresponding to this IPI (and possibly other +	 * piggybacked msg from elided IPIs: see ipi_send_msg_one() above)  	 */ -	while ((ops = xchg(&ipi->bits, 0)) != 0) -		__do_IPI(&ops, ipi, cpu); +	pending = xchg(this_cpu_ptr(&ipi_data), 0); + +	do { +		unsigned long msg = __ffs(pending); +		__do_IPI(msg); +		pending &= ~(1U << msg); +	} while (pending);  	return IRQ_HANDLED;  } @@ -325,8 +337,19 @@ irqreturn_t do_IPI(int irq, void *dev_id)   * API called by platform code to hookup arch-common ISR to their IPI IRQ   */  static DEFINE_PER_CPU(int, ipi_dev); + +static struct irqaction arc_ipi_irq = { +        .name    = "IPI Interrupt", +        .flags   = IRQF_PERCPU, +        .handler = do_IPI, +}; +  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); +	if (!cpu) +		return setup_irq(irq, &arc_ipi_irq); +	else +		arch_unmask_irq(irq); + +	return 0;  } diff --git a/arch/arc/kernel/stacktrace.c b/arch/arc/kernel/stacktrace.c index f8b7d880304..9ce47cfe230 100644 --- a/arch/arc/kernel/stacktrace.c +++ b/arch/arc/kernel/stacktrace.c @@ -237,11 +237,14 @@ unsigned int get_wchan(struct task_struct *tsk)   */  void save_stack_trace_tsk(struct task_struct *tsk, struct stack_trace *trace)  { +	/* Assumes @tsk is sleeping so unwinds from __switch_to */  	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); +	/* Pass NULL for task so it unwinds the current call frame */ +	arc_unwind_core(NULL, NULL, __collect_all, trace);  } +EXPORT_SYMBOL_GPL(save_stack_trace);  #endif diff --git a/arch/arc/kernel/time.c b/arch/arc/kernel/time.c index 3fde7de3ea6..36c2aa99436 100644 --- a/arch/arc/kernel/time.c +++ b/arch/arc/kernel/time.c @@ -63,9 +63,10 @@  int arc_counter_setup(void)  { -	/* RTSC insn taps into cpu clk, needs no setup */ - -	/* For SMP, only allowed if cross-core-sync, hence usable as cs */ +	/* +	 * For SMP this needs to be 0. However Kconfig glue doesn't +	 * enable this option for SMP configs +	 */  	return 1;  } @@ -154,22 +155,6 @@ static void arc_timer_event_setup(unsigned int limit)  	write_aux_reg(ARC_REG_TIMER0_CTRL, TIMER_CTRL_IE | TIMER_CTRL_NH);  } -/* - * Acknowledge the interrupt (oneshot) and optionally re-arm it (periodic) - * -Any write to CTRL Reg will ack the intr (NH bit: Count when not halted) - * -Rearming is done by setting the IE bit - * - * Small optimisation: Normal code would have been - *   if (irq_reenable) - *     CTRL_REG = (IE | NH); - *   else - *     CTRL_REG = NH; - * However since IE is BIT0 we can fold the branch - */ -static void arc_timer_event_ack(unsigned int irq_reenable) -{ -	write_aux_reg(ARC_REG_TIMER0_CTRL, irq_reenable | TIMER_CTRL_NH); -}  static int arc_clkevent_set_next_event(unsigned long delta,  				       struct clock_event_device *dev) @@ -206,10 +191,22 @@ static DEFINE_PER_CPU(struct clock_event_device, arc_clockevent_device) = {  static irqreturn_t timer_irq_handler(int irq, void *dev_id)  { -	struct clock_event_device *clk = &__get_cpu_var(arc_clockevent_device); +	/* +	 * Note that generic IRQ core could have passed @evt for @dev_id if +	 * irq_set_chip_and_handler() asked for handle_percpu_devid_irq() +	 */ +	struct clock_event_device *evt = this_cpu_ptr(&arc_clockevent_device); +	int irq_reenable = evt->mode == CLOCK_EVT_MODE_PERIODIC; + +	/* +	 * Any write to CTRL reg ACks the interrupt, we rewrite the +	 * Count when [N]ot [H]alted bit. +	 * And re-arm it if perioid by [I]nterrupt [E]nable bit +	 */ +	write_aux_reg(ARC_REG_TIMER0_CTRL, irq_reenable | TIMER_CTRL_NH); + +	evt->event_handler(evt); -	arc_timer_event_ack(clk->mode == CLOCK_EVT_MODE_PERIODIC); -	clk->event_handler(clk);  	return IRQ_HANDLED;  } @@ -221,14 +218,14 @@ static struct irqaction arc_timer_irq = {  /*   * Setup the local event timer for @cpu - * N.B. weak so that some exotic ARC SoCs can completely override it   */ -void __attribute__((weak)) arc_local_timer_setup(unsigned int cpu) +void arc_local_timer_setup()  { -	struct clock_event_device *clk = &per_cpu(arc_clockevent_device, cpu); +	struct clock_event_device *evt = this_cpu_ptr(&arc_clockevent_device); +	int cpu = smp_processor_id(); -	clk->cpumask = cpumask_of(cpu); -	clockevents_config_and_register(clk, arc_get_core_freq(), +	evt->cpumask = cpumask_of(cpu); +	clockevents_config_and_register(evt, arc_get_core_freq(),  					0, ARC_TIMER_MAX);  	/* @@ -265,7 +262,7 @@ void __init time_init(void)  		clocksource_register_hz(&arc_counter, arc_get_core_freq());  	/* sets up the periodic event timer */ -	arc_local_timer_setup(smp_processor_id()); +	arc_local_timer_setup();  	if (machine_desc->init_time)  		machine_desc->init_time(); diff --git a/arch/arc/kernel/traps.c b/arch/arc/kernel/traps.c index e21692d2fda..3eadfdabc32 100644 --- a/arch/arc/kernel/traps.c +++ b/arch/arc/kernel/traps.c @@ -84,19 +84,18 @@ DO_ERROR_INFO(SIGBUS, "Invalid Mem Access", do_memory_error, BUS_ADRERR)  DO_ERROR_INFO(SIGTRAP, "Breakpoint Set", trap_is_brkpt, TRAP_BRKPT)  DO_ERROR_INFO(SIGBUS, "Misaligned Access", do_misaligned_error, BUS_ADRALN) -#ifdef CONFIG_ARC_MISALIGN_ACCESS  /*   * Entry Point for Misaligned Data access Exception, for emulating in software   */  int do_misaligned_access(unsigned long address, struct pt_regs *regs,  			 struct callee_regs *cregs)  { +	/* If emulation not enabled, or failed, kill the task */  	if (misaligned_fixup(address, regs, cregs) != 0)  		return do_misaligned_error(address, regs);  	return 0;  } -#endif  /*   * Entry point for miscll errors such as Nested Exceptions diff --git a/arch/arc/kernel/troubleshoot.c b/arch/arc/kernel/troubleshoot.c index 73a7450ee62..1badf9b84b5 100644 --- a/arch/arc/kernel/troubleshoot.c +++ b/arch/arc/kernel/troubleshoot.c @@ -86,12 +86,13 @@ static void show_faulting_vma(unsigned long address, char *buf)  	unsigned long ino = 0;  	dev_t dev = 0;  	char *nm = buf; +	struct mm_struct *active_mm = current->active_mm;  	/* can't use print_vma_addr() yet as it doesn't check for  	 * non-inclusive vma  	 */ - -	vma = find_vma(current->active_mm, address); +	down_read(&active_mm->mmap_sem); +	vma = find_vma(active_mm, address);  	/* check against the find_vma( ) behaviour which returns the next VMA  	 * if the container VMA is not found @@ -110,9 +111,10 @@ static void show_faulting_vma(unsigned long address, char *buf)  			vma->vm_start < TASK_UNMAPPED_BASE ?  				address : address - vma->vm_start,  			nm, vma->vm_start, vma->vm_end); -	} else { +	} else  		pr_info("    @No matching VMA found\n"); -	} + +	up_read(&active_mm->mmap_sem);  }  static void show_ecr_verbose(struct pt_regs *regs) diff --git a/arch/arc/kernel/vmlinux.lds.S b/arch/arc/kernel/vmlinux.lds.S index 2555f5886af..dd35bde39f6 100644 --- a/arch/arc/kernel/vmlinux.lds.S +++ b/arch/arc/kernel/vmlinux.lds.S @@ -116,7 +116,7 @@ SECTIONS  	_edata = .; -	BSS_SECTION(0, 0, 0) +	BSS_SECTION(4, 4, 4)  #ifdef CONFIG_ARC_DW2_UNWIND  	. = ALIGN(PAGE_SIZE);  | 
