diff options
Diffstat (limited to 'arch/arm64/kernel')
| -rw-r--r-- | arch/arm64/kernel/Makefile | 5 | ||||
| -rw-r--r-- | arch/arm64/kernel/arm64ksyms.c | 19 | ||||
| -rw-r--r-- | arch/arm64/kernel/early_printk.c | 153 | ||||
| -rw-r--r-- | arch/arm64/kernel/entry.S | 58 | ||||
| -rw-r--r-- | arch/arm64/kernel/head.S | 16 | ||||
| -rw-r--r-- | arch/arm64/kernel/irq.c | 19 | ||||
| -rw-r--r-- | arch/arm64/kernel/perf_event.c | 37 | ||||
| -rw-r--r-- | arch/arm64/kernel/process.c | 73 | ||||
| -rw-r--r-- | arch/arm64/kernel/psci.c | 211 | ||||
| -rw-r--r-- | arch/arm64/kernel/setup.c | 23 | ||||
| -rw-r--r-- | arch/arm64/kernel/signal.c | 17 | ||||
| -rw-r--r-- | arch/arm64/kernel/signal32.c | 225 | ||||
| -rw-r--r-- | arch/arm64/kernel/smp.c | 170 | ||||
| -rw-r--r-- | arch/arm64/kernel/smp_psci.c | 53 | ||||
| -rw-r--r-- | arch/arm64/kernel/smp_spin_table.c | 66 | ||||
| -rw-r--r-- | arch/arm64/kernel/sys.c | 1 | ||||
| -rw-r--r-- | arch/arm64/kernel/sys32.S | 24 | ||||
| -rw-r--r-- | arch/arm64/kernel/time.c | 31 | ||||
| -rw-r--r-- | arch/arm64/kernel/traps.c | 9 | 
19 files changed, 804 insertions, 406 deletions
diff --git a/arch/arm64/kernel/Makefile b/arch/arm64/kernel/Makefile index 74239c31e25..7b4b564961d 100644 --- a/arch/arm64/kernel/Makefile +++ b/arch/arm64/kernel/Makefile @@ -9,14 +9,15 @@ AFLAGS_head.o		:= -DTEXT_OFFSET=$(TEXT_OFFSET)  arm64-obj-y		:= cputable.o debug-monitors.o entry.o irq.o fpsimd.o	\  			   entry-fpsimd.o process.o ptrace.o setup.o signal.o	\  			   sys.o stacktrace.o time.o traps.o io.o vdso.o	\ -			   hyp-stub.o +			   hyp-stub.o psci.o  arm64-obj-$(CONFIG_COMPAT)		+= sys32.o kuser32.o signal32.o 	\  					   sys_compat.o  arm64-obj-$(CONFIG_MODULES)		+= arm64ksyms.o module.o -arm64-obj-$(CONFIG_SMP)			+= smp.o +arm64-obj-$(CONFIG_SMP)			+= smp.o smp_spin_table.o smp_psci.o  arm64-obj-$(CONFIG_HW_PERF_EVENTS)	+= perf_event.o  arm64-obj-$(CONFIG_HAVE_HW_BREAKPOINT)+= hw_breakpoint.o +arm64-obj-$(CONFIG_EARLY_PRINTK)	+= early_printk.o  obj-y					+= $(arm64-obj-y) vdso/  obj-m					+= $(arm64-obj-m) diff --git a/arch/arm64/kernel/arm64ksyms.c b/arch/arm64/kernel/arm64ksyms.c index cef3925eaf6..7df1aad29b6 100644 --- a/arch/arm64/kernel/arm64ksyms.c +++ b/arch/arm64/kernel/arm64ksyms.c @@ -39,8 +39,21 @@ EXPORT_SYMBOL(__copy_from_user);  EXPORT_SYMBOL(__copy_to_user);  EXPORT_SYMBOL(__clear_user); -	/* bitops */ -EXPORT_SYMBOL(__atomic_hash); -  	/* physical memory */  EXPORT_SYMBOL(memstart_addr); + +	/* string / mem functions */ +EXPORT_SYMBOL(strchr); +EXPORT_SYMBOL(strrchr); +EXPORT_SYMBOL(memset); +EXPORT_SYMBOL(memcpy); +EXPORT_SYMBOL(memmove); +EXPORT_SYMBOL(memchr); + +	/* atomic bitops */ +EXPORT_SYMBOL(set_bit); +EXPORT_SYMBOL(test_and_set_bit); +EXPORT_SYMBOL(clear_bit); +EXPORT_SYMBOL(test_and_clear_bit); +EXPORT_SYMBOL(change_bit); +EXPORT_SYMBOL(test_and_change_bit); diff --git a/arch/arm64/kernel/early_printk.c b/arch/arm64/kernel/early_printk.c new file mode 100644 index 00000000000..ac974f48a7a --- /dev/null +++ b/arch/arm64/kernel/early_printk.c @@ -0,0 +1,153 @@ +/* + * Earlyprintk support. + * + * Copyright (C) 2012 ARM Ltd. + * Author: Catalin Marinas <catalin.marinas@arm.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. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program.  If not, see <http://www.gnu.org/licenses/>. + */ +#include <linux/kernel.h> +#include <linux/console.h> +#include <linux/init.h> +#include <linux/string.h> +#include <linux/mm.h> +#include <linux/io.h> + +#include <linux/amba/serial.h> +#include <linux/serial_reg.h> + +static void __iomem *early_base; +static void (*printch)(char ch); + +/* + * PL011 single character TX. + */ +static void pl011_printch(char ch) +{ +	while (readl_relaxed(early_base + UART01x_FR) & UART01x_FR_TXFF) +		; +	writeb_relaxed(ch, early_base + UART01x_DR); +	while (readl_relaxed(early_base + UART01x_FR) & UART01x_FR_BUSY) +		; +} + +/* + * Semihosting-based debug console + */ +static void smh_printch(char ch) +{ +	asm volatile("mov  x1, %0\n" +		     "mov  x0, #3\n" +		     "hlt  0xf000\n" +		     : : "r" (&ch) : "x0", "x1", "memory"); +} + +/* + * 8250/16550 (8-bit aligned registers) single character TX. + */ +static void uart8250_8bit_printch(char ch) +{ +	while (!(readb_relaxed(early_base + UART_LSR) & UART_LSR_THRE)) +		; +	writeb_relaxed(ch, early_base + UART_TX); +} + +/* + * 8250/16550 (32-bit aligned registers) single character TX. + */ +static void uart8250_32bit_printch(char ch) +{ +	while (!(readl_relaxed(early_base + (UART_LSR << 2)) & UART_LSR_THRE)) +		; +	writel_relaxed(ch, early_base + (UART_TX << 2)); +} + +struct earlycon_match { +	const char *name; +	void (*printch)(char ch); +}; + +static const struct earlycon_match earlycon_match[] __initconst = { +	{ .name = "pl011", .printch = pl011_printch, }, +	{ .name = "smh", .printch = smh_printch, }, +	{ .name = "uart8250-8bit", .printch = uart8250_8bit_printch, }, +	{ .name = "uart8250-32bit", .printch = uart8250_32bit_printch, }, +	{} +}; + +static void early_write(struct console *con, const char *s, unsigned n) +{ +	while (n-- > 0) { +		if (*s == '\n') +			printch('\r'); +		printch(*s); +		s++; +	} +} + +static struct console early_console = { +	.name =		"earlycon", +	.write =	early_write, +	.flags =	CON_PRINTBUFFER | CON_BOOT, +	.index =	-1, +}; + +/* + * Parse earlyprintk=... parameter in the format: + * + *   <name>[,<addr>][,<options>] + * + * and register the early console. It is assumed that the UART has been + * initialised by the bootloader already. + */ +static int __init setup_early_printk(char *buf) +{ +	const struct earlycon_match *match = earlycon_match; +	phys_addr_t paddr = 0; + +	if (!buf) { +		pr_warning("No earlyprintk arguments passed.\n"); +		return 0; +	} + +	while (match->name) { +		size_t len = strlen(match->name); +		if (!strncmp(buf, match->name, len)) { +			buf += len; +			break; +		} +		match++; +	} +	if (!match->name) { +		pr_warning("Unknown earlyprintk arguments: %s\n", buf); +		return 0; +	} + +	/* I/O address */ +	if (!strncmp(buf, ",0x", 3)) { +		char *e; +		paddr = simple_strtoul(buf + 1, &e, 16); +		buf = e; +	} +	/* no options parsing yet */ + +	if (paddr) +		early_base = early_io_map(paddr, EARLYCON_IOBASE); + +	printch = match->printch; +	register_console(&early_console); + +	return 0; +} + +early_param("earlyprintk", setup_early_printk); diff --git a/arch/arm64/kernel/entry.S b/arch/arm64/kernel/entry.S index 9c94f404ded..c7e047049f2 100644 --- a/arch/arm64/kernel/entry.S +++ b/arch/arm64/kernel/entry.S @@ -24,6 +24,7 @@  #include <asm/assembler.h>  #include <asm/asm-offsets.h>  #include <asm/errno.h> +#include <asm/esr.h>  #include <asm/thread_info.h>  #include <asm/unistd.h>  #include <asm/unistd32.h> @@ -239,18 +240,18 @@ ENDPROC(el1_error_invalid)  el1_sync:  	kernel_entry 1  	mrs	x1, esr_el1			// read the syndrome register -	lsr	x24, x1, #26			// exception class -	cmp	x24, #0x25			// data abort in EL1 +	lsr	x24, x1, #ESR_EL1_EC_SHIFT	// exception class +	cmp	x24, #ESR_EL1_EC_DABT_EL1	// data abort in EL1  	b.eq	el1_da -	cmp	x24, #0x18			// configurable trap +	cmp	x24, #ESR_EL1_EC_SYS64		// configurable trap  	b.eq	el1_undef -	cmp	x24, #0x26			// stack alignment exception +	cmp	x24, #ESR_EL1_EC_SP_ALIGN	// stack alignment exception  	b.eq	el1_sp_pc -	cmp	x24, #0x22			// pc alignment exception +	cmp	x24, #ESR_EL1_EC_PC_ALIGN	// pc alignment exception  	b.eq	el1_sp_pc -	cmp	x24, #0x00			// unknown exception in EL1 +	cmp	x24, #ESR_EL1_EC_UNKNOWN	// unknown exception in EL1  	b.eq	el1_undef -	cmp	x24, #0x30			// debug exception in EL1 +	cmp	x24, #ESR_EL1_EC_BREAKPT_EL1	// debug exception in EL1  	b.ge	el1_dbg  	b	el1_inv  el1_da: @@ -346,27 +347,27 @@ el1_preempt:  el0_sync:  	kernel_entry 0  	mrs	x25, esr_el1			// read the syndrome register -	lsr	x24, x25, #26			// exception class -	cmp	x24, #0x15			// SVC in 64-bit state +	lsr	x24, x25, #ESR_EL1_EC_SHIFT	// exception class +	cmp	x24, #ESR_EL1_EC_SVC64		// SVC in 64-bit state  	b.eq	el0_svc  	adr	lr, ret_from_exception -	cmp	x24, #0x24			// data abort in EL0 +	cmp	x24, #ESR_EL1_EC_DABT_EL0	// data abort in EL0  	b.eq	el0_da -	cmp	x24, #0x20			// instruction abort in EL0 +	cmp	x24, #ESR_EL1_EC_IABT_EL0	// instruction abort in EL0  	b.eq	el0_ia -	cmp	x24, #0x07			// FP/ASIMD access +	cmp	x24, #ESR_EL1_EC_FP_ASIMD	// FP/ASIMD access  	b.eq	el0_fpsimd_acc -	cmp	x24, #0x2c			// FP/ASIMD exception +	cmp	x24, #ESR_EL1_EC_FP_EXC64	// FP/ASIMD exception  	b.eq	el0_fpsimd_exc -	cmp	x24, #0x18			// configurable trap +	cmp	x24, #ESR_EL1_EC_SYS64		// configurable trap  	b.eq	el0_undef -	cmp	x24, #0x26			// stack alignment exception +	cmp	x24, #ESR_EL1_EC_SP_ALIGN	// stack alignment exception  	b.eq	el0_sp_pc -	cmp	x24, #0x22			// pc alignment exception +	cmp	x24, #ESR_EL1_EC_PC_ALIGN	// pc alignment exception  	b.eq	el0_sp_pc -	cmp	x24, #0x00			// unknown exception in EL0 +	cmp	x24, #ESR_EL1_EC_UNKNOWN	// unknown exception in EL0  	b.eq	el0_undef -	cmp	x24, #0x30			// debug exception in EL0 +	cmp	x24, #ESR_EL1_EC_BREAKPT_EL0	// debug exception in EL0  	b.ge	el0_dbg  	b	el0_inv @@ -375,21 +376,21 @@ el0_sync:  el0_sync_compat:  	kernel_entry 0, 32  	mrs	x25, esr_el1			// read the syndrome register -	lsr	x24, x25, #26			// exception class -	cmp	x24, #0x11			// SVC in 32-bit state +	lsr	x24, x25, #ESR_EL1_EC_SHIFT	// exception class +	cmp	x24, #ESR_EL1_EC_SVC32		// SVC in 32-bit state  	b.eq	el0_svc_compat  	adr	lr, ret_from_exception -	cmp	x24, #0x24			// data abort in EL0 +	cmp	x24, #ESR_EL1_EC_DABT_EL0	// data abort in EL0  	b.eq	el0_da -	cmp	x24, #0x20			// instruction abort in EL0 +	cmp	x24, #ESR_EL1_EC_IABT_EL0	// instruction abort in EL0  	b.eq	el0_ia -	cmp	x24, #0x07			// FP/ASIMD access +	cmp	x24, #ESR_EL1_EC_FP_ASIMD	// FP/ASIMD access  	b.eq	el0_fpsimd_acc -	cmp	x24, #0x28			// FP/ASIMD exception +	cmp	x24, #ESR_EL1_EC_FP_EXC32	// FP/ASIMD exception  	b.eq	el0_fpsimd_exc -	cmp	x24, #0x00			// unknown exception in EL0 +	cmp	x24, #ESR_EL1_EC_UNKNOWN	// unknown exception in EL0  	b.eq	el0_undef -	cmp	x24, #0x30			// debug exception in EL0 +	cmp	x24, #ESR_EL1_EC_BREAKPT_EL0	// debug exception in EL0  	b.ge	el0_dbg  	b	el0_inv  el0_svc_compat: @@ -677,10 +678,5 @@ ENTRY(sys_rt_sigreturn_wrapper)  	b	sys_rt_sigreturn  ENDPROC(sys_rt_sigreturn_wrapper) -ENTRY(sys_sigaltstack_wrapper) -	ldr	x2, [sp, #S_SP] -	b	sys_sigaltstack -ENDPROC(sys_sigaltstack_wrapper) -  ENTRY(handle_arch_irq)  	.quad	0 diff --git a/arch/arm64/kernel/head.S b/arch/arm64/kernel/head.S index 368ad1f7c36..53dcae49e72 100644 --- a/arch/arm64/kernel/head.S +++ b/arch/arm64/kernel/head.S @@ -26,6 +26,7 @@  #include <asm/assembler.h>  #include <asm/ptrace.h>  #include <asm/asm-offsets.h> +#include <asm/cputype.h>  #include <asm/memory.h>  #include <asm/thread_info.h>  #include <asm/pgtable-hwdef.h> @@ -82,10 +83,8 @@  #ifdef CONFIG_ARM64_64K_PAGES  #define MM_MMUFLAGS	PTE_ATTRINDX(MT_NORMAL) | PTE_FLAGS -#define IO_MMUFLAGS	PTE_ATTRINDX(MT_DEVICE_nGnRE) | PTE_XN | PTE_FLAGS  #else  #define MM_MMUFLAGS	PMD_ATTRINDX(MT_NORMAL) | PMD_FLAGS -#define IO_MMUFLAGS	PMD_ATTRINDX(MT_DEVICE_nGnRE) | PMD_SECT_XN | PMD_FLAGS  #endif  /* @@ -231,7 +230,8 @@ ENTRY(secondary_holding_pen)  	bl	__calc_phys_offset		// x24=phys offset  	bl	el2_setup			// Drop to EL1  	mrs	x0, mpidr_el1 -	and	x0, x0, #15			// CPU number +	ldr     x1, =MPIDR_HWID_BITMASK +	and	x0, x0, x1  	adr	x1, 1b  	ldp	x2, x3, [x1]  	sub	x1, x1, x2 @@ -368,6 +368,7 @@ ENDPROC(__calc_phys_offset)   *   - identity mapping to enable the MMU (low address, TTBR0)   *   - first few MB of the kernel linear mapping to jump to once the MMU has   *     been enabled, including the FDT blob (TTBR1) + *   - UART mapping if CONFIG_EARLY_PRINTK is enabled (TTBR1)   */  __create_page_tables:  	pgtbl	x25, x26, x24			// idmap_pg_dir and swapper_pg_dir addresses @@ -420,6 +421,15 @@ __create_page_tables:  	sub	x6, x6, #1			// inclusive range  	create_block_map x0, x7, x3, x5, x6  1: +#ifdef CONFIG_EARLY_PRINTK +	/* +	 * Create the pgd entry for the UART mapping. The full mapping is done +	 * later based earlyprintk kernel parameter. +	 */ +	ldr	x5, =EARLYCON_IOBASE		// UART virtual address +	add	x0, x26, #2 * PAGE_SIZE		// section table address +	create_pgd_entry x26, x0, x5, x6, x7 +#endif  	ret  ENDPROC(__create_page_tables)  	.ltorg diff --git a/arch/arm64/kernel/irq.c b/arch/arm64/kernel/irq.c index 0373c6609ea..ecb3354292e 100644 --- a/arch/arm64/kernel/irq.c +++ b/arch/arm64/kernel/irq.c @@ -25,7 +25,7 @@  #include <linux/irq.h>  #include <linux/smp.h>  #include <linux/init.h> -#include <linux/of_irq.h> +#include <linux/irqchip.h>  #include <linux/seq_file.h>  #include <linux/ratelimit.h> @@ -67,18 +67,17 @@ void handle_IRQ(unsigned int irq, struct pt_regs *regs)  	set_irq_regs(old_regs);  } -/* - * Interrupt controllers supported by the kernel. - */ -static const struct of_device_id intctrl_of_match[] __initconst = { -	/* IRQ controllers { .compatible, .data } info to go here */ -	{} -}; +void __init set_handle_irq(void (*handle_irq)(struct pt_regs *)) +{ +	if (handle_arch_irq) +		return; + +	handle_arch_irq = handle_irq; +}  void __init init_IRQ(void)  { -	of_irq_init(intctrl_of_match); - +	irqchip_init();  	if (!handle_arch_irq)  		panic("No interrupt controller found.");  } diff --git a/arch/arm64/kernel/perf_event.c b/arch/arm64/kernel/perf_event.c index f7073c7b1ca..1e49e5eb81e 100644 --- a/arch/arm64/kernel/perf_event.c +++ b/arch/arm64/kernel/perf_event.c @@ -1331,6 +1331,11 @@ void perf_callchain_user(struct perf_callchain_entry *entry,  {  	struct frame_tail __user *tail; +	if (perf_guest_cbs && perf_guest_cbs->is_in_guest()) { +		/* We don't support guest os callchain now */ +		return; +	} +  	tail = (struct frame_tail __user *)regs->regs[29];  	while (entry->nr < PERF_MAX_STACK_DEPTH && @@ -1355,8 +1360,40 @@ void perf_callchain_kernel(struct perf_callchain_entry *entry,  {  	struct stackframe frame; +	if (perf_guest_cbs && perf_guest_cbs->is_in_guest()) { +		/* We don't support guest os callchain now */ +		return; +	} +  	frame.fp = regs->regs[29];  	frame.sp = regs->sp;  	frame.pc = regs->pc;  	walk_stackframe(&frame, callchain_trace, entry);  } + +unsigned long perf_instruction_pointer(struct pt_regs *regs) +{ +	if (perf_guest_cbs && perf_guest_cbs->is_in_guest()) +		return perf_guest_cbs->get_guest_ip(); + +	return instruction_pointer(regs); +} + +unsigned long perf_misc_flags(struct pt_regs *regs) +{ +	int misc = 0; + +	if (perf_guest_cbs && perf_guest_cbs->is_in_guest()) { +		if (perf_guest_cbs->is_user_mode()) +			misc |= PERF_RECORD_MISC_GUEST_USER; +		else +			misc |= PERF_RECORD_MISC_GUEST_KERNEL; +	} else { +		if (user_mode(regs)) +			misc |= PERF_RECORD_MISC_USER; +		else +			misc |= PERF_RECORD_MISC_KERNEL; +	} + +	return misc; +} diff --git a/arch/arm64/kernel/process.c b/arch/arm64/kernel/process.c index cb0956bc96e..46f02c3b501 100644 --- a/arch/arm64/kernel/process.c +++ b/arch/arm64/kernel/process.c @@ -45,9 +45,10 @@  #include <asm/compat.h>  #include <asm/cacheflush.h> +#include <asm/fpsimd.h> +#include <asm/mmu_context.h>  #include <asm/processor.h>  #include <asm/stacktrace.h> -#include <asm/fpsimd.h>  static void setup_restart(void)  { @@ -80,14 +81,18 @@ void soft_restart(unsigned long addr)  void (*pm_power_off)(void);  EXPORT_SYMBOL_GPL(pm_power_off); -void (*pm_restart)(const char *cmd); -EXPORT_SYMBOL_GPL(pm_restart); +void (*arm_pm_restart)(char str, const char *cmd); +EXPORT_SYMBOL_GPL(arm_pm_restart); +void arch_cpu_idle_prepare(void) +{ +	local_fiq_enable(); +}  /*   * This is our default idle handler.   */ -static void default_idle(void) +void arch_cpu_idle(void)  {  	/*  	 * This should do all the clock switching and wait for interrupt @@ -97,48 +102,6 @@ static void default_idle(void)  	local_irq_enable();  } -void (*pm_idle)(void) = default_idle; -EXPORT_SYMBOL_GPL(pm_idle); - -/* - * The idle thread, has rather strange semantics for calling pm_idle, - * but this is what x86 does and we need to do the same, so that - * things like cpuidle get called in the same way.  The only difference - * is that we always respect 'hlt_counter' to prevent low power idle. - */ -void cpu_idle(void) -{ -	local_fiq_enable(); - -	/* endless idle loop with no priority at all */ -	while (1) { -		tick_nohz_idle_enter(); -		rcu_idle_enter(); -		while (!need_resched()) { -			/* -			 * We need to disable interrupts here to ensure -			 * we don't miss a wakeup call. -			 */ -			local_irq_disable(); -			if (!need_resched()) { -				stop_critical_timings(); -				pm_idle(); -				start_critical_timings(); -				/* -				 * pm_idle functions should always return -				 * with IRQs enabled. -				 */ -				WARN_ON(irqs_disabled()); -			} else { -				local_irq_enable(); -			} -		} -		rcu_idle_exit(); -		tick_nohz_idle_exit(); -		schedule_preempt_disabled(); -	} -} -  void machine_shutdown(void)  {  #ifdef CONFIG_SMP @@ -168,8 +131,8 @@ void machine_restart(char *cmd)  	local_fiq_disable();  	/* Now call the architecture specific reboot code. */ -	if (pm_restart) -		pm_restart(cmd); +	if (arm_pm_restart) +		arm_pm_restart('h', cmd);  	/*  	 * Whoops - the architecture was unable to reboot. @@ -182,11 +145,7 @@ void __show_regs(struct pt_regs *regs)  {  	int i; -	printk("CPU: %d    %s  (%s %.*s)\n", -		raw_smp_processor_id(), print_tainted(), -		init_utsname()->release, -		(int)strcspn(init_utsname()->version, " "), -		init_utsname()->version); +	show_regs_print_info(KERN_DEFAULT);  	print_symbol("PC is at %s\n", instruction_pointer(regs));  	print_symbol("LR is at %s\n", regs->regs[30]);  	printk("pc : [<%016llx>] lr : [<%016llx>] pstate: %08llx\n", @@ -203,7 +162,6 @@ void __show_regs(struct pt_regs *regs)  void show_regs(struct pt_regs * regs)  {  	printk("\n"); -	printk("Pid: %d, comm: %20s\n", task_pid_nr(current), current->comm);  	__show_regs(regs);  } @@ -315,6 +273,13 @@ struct task_struct *__switch_to(struct task_struct *prev,  	fpsimd_thread_switch(next);  	tls_thread_switch(next);  	hw_breakpoint_thread_switch(next); +	contextidr_thread_switch(next); + +	/* +	 * Complete any pending TLB or cache maintenance on this CPU in case +	 * the thread migrates to a different CPU. +	 */ +	dsb();  	/* the actual thread switch */  	last = cpu_switch_to(prev, next); diff --git a/arch/arm64/kernel/psci.c b/arch/arm64/kernel/psci.c new file mode 100644 index 00000000000..14f73c445ff --- /dev/null +++ b/arch/arm64/kernel/psci.c @@ -0,0 +1,211 @@ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + * + * Copyright (C) 2013 ARM Limited + * + * Author: Will Deacon <will.deacon@arm.com> + */ + +#define pr_fmt(fmt) "psci: " fmt + +#include <linux/init.h> +#include <linux/of.h> + +#include <asm/compiler.h> +#include <asm/errno.h> +#include <asm/psci.h> + +struct psci_operations psci_ops; + +static int (*invoke_psci_fn)(u64, u64, u64, u64); + +enum psci_function { +	PSCI_FN_CPU_SUSPEND, +	PSCI_FN_CPU_ON, +	PSCI_FN_CPU_OFF, +	PSCI_FN_MIGRATE, +	PSCI_FN_MAX, +}; + +static u32 psci_function_id[PSCI_FN_MAX]; + +#define PSCI_RET_SUCCESS		0 +#define PSCI_RET_EOPNOTSUPP		-1 +#define PSCI_RET_EINVAL			-2 +#define PSCI_RET_EPERM			-3 + +static int psci_to_linux_errno(int errno) +{ +	switch (errno) { +	case PSCI_RET_SUCCESS: +		return 0; +	case PSCI_RET_EOPNOTSUPP: +		return -EOPNOTSUPP; +	case PSCI_RET_EINVAL: +		return -EINVAL; +	case PSCI_RET_EPERM: +		return -EPERM; +	}; + +	return -EINVAL; +} + +#define PSCI_POWER_STATE_ID_MASK	0xffff +#define PSCI_POWER_STATE_ID_SHIFT	0 +#define PSCI_POWER_STATE_TYPE_MASK	0x1 +#define PSCI_POWER_STATE_TYPE_SHIFT	16 +#define PSCI_POWER_STATE_AFFL_MASK	0x3 +#define PSCI_POWER_STATE_AFFL_SHIFT	24 + +static u32 psci_power_state_pack(struct psci_power_state state) +{ +	return	((state.id & PSCI_POWER_STATE_ID_MASK) +			<< PSCI_POWER_STATE_ID_SHIFT)	| +		((state.type & PSCI_POWER_STATE_TYPE_MASK) +			<< PSCI_POWER_STATE_TYPE_SHIFT)	| +		((state.affinity_level & PSCI_POWER_STATE_AFFL_MASK) +			<< PSCI_POWER_STATE_AFFL_SHIFT); +} + +/* + * The following two functions are invoked via the invoke_psci_fn pointer + * and will not be inlined, allowing us to piggyback on the AAPCS. + */ +static noinline int __invoke_psci_fn_hvc(u64 function_id, u64 arg0, u64 arg1, +					 u64 arg2) +{ +	asm volatile( +			__asmeq("%0", "x0") +			__asmeq("%1", "x1") +			__asmeq("%2", "x2") +			__asmeq("%3", "x3") +			"hvc	#0\n" +		: "+r" (function_id) +		: "r" (arg0), "r" (arg1), "r" (arg2)); + +	return function_id; +} + +static noinline int __invoke_psci_fn_smc(u64 function_id, u64 arg0, u64 arg1, +					 u64 arg2) +{ +	asm volatile( +			__asmeq("%0", "x0") +			__asmeq("%1", "x1") +			__asmeq("%2", "x2") +			__asmeq("%3", "x3") +			"smc	#0\n" +		: "+r" (function_id) +		: "r" (arg0), "r" (arg1), "r" (arg2)); + +	return function_id; +} + +static int psci_cpu_suspend(struct psci_power_state state, +			    unsigned long entry_point) +{ +	int err; +	u32 fn, power_state; + +	fn = psci_function_id[PSCI_FN_CPU_SUSPEND]; +	power_state = psci_power_state_pack(state); +	err = invoke_psci_fn(fn, power_state, entry_point, 0); +	return psci_to_linux_errno(err); +} + +static int psci_cpu_off(struct psci_power_state state) +{ +	int err; +	u32 fn, power_state; + +	fn = psci_function_id[PSCI_FN_CPU_OFF]; +	power_state = psci_power_state_pack(state); +	err = invoke_psci_fn(fn, power_state, 0, 0); +	return psci_to_linux_errno(err); +} + +static int psci_cpu_on(unsigned long cpuid, unsigned long entry_point) +{ +	int err; +	u32 fn; + +	fn = psci_function_id[PSCI_FN_CPU_ON]; +	err = invoke_psci_fn(fn, cpuid, entry_point, 0); +	return psci_to_linux_errno(err); +} + +static int psci_migrate(unsigned long cpuid) +{ +	int err; +	u32 fn; + +	fn = psci_function_id[PSCI_FN_MIGRATE]; +	err = invoke_psci_fn(fn, cpuid, 0, 0); +	return psci_to_linux_errno(err); +} + +static const struct of_device_id psci_of_match[] __initconst = { +	{ .compatible = "arm,psci",	}, +	{}, +}; + +int __init psci_init(void) +{ +	struct device_node *np; +	const char *method; +	u32 id; +	int err = 0; + +	np = of_find_matching_node(NULL, psci_of_match); +	if (!np) +		return -ENODEV; + +	pr_info("probing function IDs from device-tree\n"); + +	if (of_property_read_string(np, "method", &method)) { +		pr_warning("missing \"method\" property\n"); +		err = -ENXIO; +		goto out_put_node; +	} + +	if (!strcmp("hvc", method)) { +		invoke_psci_fn = __invoke_psci_fn_hvc; +	} else if (!strcmp("smc", method)) { +		invoke_psci_fn = __invoke_psci_fn_smc; +	} else { +		pr_warning("invalid \"method\" property: %s\n", method); +		err = -EINVAL; +		goto out_put_node; +	} + +	if (!of_property_read_u32(np, "cpu_suspend", &id)) { +		psci_function_id[PSCI_FN_CPU_SUSPEND] = id; +		psci_ops.cpu_suspend = psci_cpu_suspend; +	} + +	if (!of_property_read_u32(np, "cpu_off", &id)) { +		psci_function_id[PSCI_FN_CPU_OFF] = id; +		psci_ops.cpu_off = psci_cpu_off; +	} + +	if (!of_property_read_u32(np, "cpu_on", &id)) { +		psci_function_id[PSCI_FN_CPU_ON] = id; +		psci_ops.cpu_on = psci_cpu_on; +	} + +	if (!of_property_read_u32(np, "migrate", &id)) { +		psci_function_id[PSCI_FN_MIGRATE] = id; +		psci_ops.migrate = psci_migrate; +	} + +out_put_node: +	of_node_put(np); +	return err; +} diff --git a/arch/arm64/kernel/setup.c b/arch/arm64/kernel/setup.c index 7665a9bfdb1..6a9a5329259 100644 --- a/arch/arm64/kernel/setup.c +++ b/arch/arm64/kernel/setup.c @@ -32,6 +32,7 @@  #include <linux/kexec.h>  #include <linux/crash_dump.h>  #include <linux/root_dev.h> +#include <linux/clk-provider.h>  #include <linux/cpu.h>  #include <linux/interrupt.h>  #include <linux/smp.h> @@ -39,16 +40,19 @@  #include <linux/proc_fs.h>  #include <linux/memblock.h>  #include <linux/of_fdt.h> +#include <linux/of_platform.h>  #include <asm/cputype.h>  #include <asm/elf.h>  #include <asm/cputable.h>  #include <asm/sections.h>  #include <asm/setup.h> +#include <asm/smp_plat.h>  #include <asm/cacheflush.h>  #include <asm/tlbflush.h>  #include <asm/traps.h>  #include <asm/memblock.h> +#include <asm/psci.h>  unsigned int processor_id;  EXPORT_SYMBOL(processor_id); @@ -238,6 +242,8 @@ static void __init request_standard_resources(void)  	}  } +u64 __cpu_logical_map[NR_CPUS] = { [0 ... NR_CPUS-1] = INVALID_HWID }; +  void __init setup_arch(char **cmdline_p)  {  	setup_processor(); @@ -260,6 +266,9 @@ void __init setup_arch(char **cmdline_p)  	unflatten_device_tree(); +	psci_init(); + +	cpu_logical_map(0) = read_cpuid_mpidr() & MPIDR_HWID_BITMASK;  #ifdef CONFIG_SMP  	smp_init_cpus();  #endif @@ -273,6 +282,13 @@ void __init setup_arch(char **cmdline_p)  #endif  } +static int __init arm64_of_clk_init(void) +{ +	of_clk_init(NULL); +	return 0; +} +arch_initcall(arm64_of_clk_init); +  static DEFINE_PER_CPU(struct cpu, cpu_data);  static int __init topology_init(void) @@ -289,6 +305,13 @@ static int __init topology_init(void)  }  subsys_initcall(topology_init); +static int __init arm64_device_probe(void) +{ +	of_platform_populate(NULL, of_default_bus_match_table, NULL, NULL); +	return 0; +} +device_initcall(arm64_device_probe); +  static const char *hwcap_str[] = {  	"fp",  	"asimd", diff --git a/arch/arm64/kernel/signal.c b/arch/arm64/kernel/signal.c index abd756315cb..890a591f75d 100644 --- a/arch/arm64/kernel/signal.c +++ b/arch/arm64/kernel/signal.c @@ -149,8 +149,7 @@ asmlinkage long sys_rt_sigreturn(struct pt_regs *regs)  	if (restore_sigframe(regs, frame))  		goto badframe; -	if (do_sigaltstack(&frame->uc.uc_stack, -			   NULL, regs->sp) == -EFAULT) +	if (restore_altstack(&frame->uc.uc_stack))  		goto badframe;  	return regs->regs[0]; @@ -164,12 +163,6 @@ badframe:  	return 0;  } -asmlinkage long sys_sigaltstack(const stack_t __user *uss, stack_t __user *uoss, -				unsigned long sp) -{ -	return do_sigaltstack(uss, uoss, sp); -} -  static int setup_sigframe(struct rt_sigframe __user *sf,  			  struct pt_regs *regs, sigset_t *set)  { @@ -250,7 +243,6 @@ static int setup_rt_frame(int usig, struct k_sigaction *ka, siginfo_t *info,  			  sigset_t *set, struct pt_regs *regs)  {  	struct rt_sigframe __user *frame; -	stack_t stack;  	int err = 0;  	frame = get_sigframe(ka, regs); @@ -260,12 +252,7 @@ static int setup_rt_frame(int usig, struct k_sigaction *ka, siginfo_t *info,  	__put_user_error(0, &frame->uc.uc_flags, err);  	__put_user_error(NULL, &frame->uc.uc_link, err); -	memset(&stack, 0, sizeof(stack)); -	stack.ss_sp = (void __user *)current->sas_ss_sp; -	stack.ss_flags = sas_ss_flags(regs->sp); -	stack.ss_size = current->sas_ss_size; -	err |= __copy_to_user(&frame->uc.uc_stack, &stack, sizeof(stack)); - +	err |= __save_altstack(&frame->uc.uc_stack, regs->sp);  	err |= setup_sigframe(frame, regs, set);  	if (err == 0) {  		setup_return(regs, ka, frame, usig); diff --git a/arch/arm64/kernel/signal32.c b/arch/arm64/kernel/signal32.c index a4db3d22aac..e393174fe85 100644 --- a/arch/arm64/kernel/signal32.c +++ b/arch/arm64/kernel/signal32.c @@ -28,26 +28,6 @@  #include <asm/uaccess.h>  #include <asm/unistd32.h> -struct compat_sigaction { -	compat_uptr_t			sa_handler; -	compat_ulong_t			sa_flags; -	compat_uptr_t			sa_restorer; -	compat_sigset_t			sa_mask; -}; - -struct compat_old_sigaction { -	compat_uptr_t			sa_handler; -	compat_old_sigset_t		sa_mask; -	compat_ulong_t			sa_flags; -	compat_uptr_t			sa_restorer; -}; - -typedef struct compat_sigaltstack { -	compat_uptr_t			ss_sp; -	int				ss_flags; -	compat_size_t			ss_size; -} compat_stack_t; -  struct compat_sigcontext {  	/* We always set these two fields to 0 */  	compat_ulong_t			trap_no; @@ -76,7 +56,7 @@ struct compat_sigcontext {  struct compat_ucontext {  	compat_ulong_t			uc_flags; -	struct compat_ucontext		*uc_link; +	compat_uptr_t			uc_link;  	compat_stack_t			uc_stack;  	struct compat_sigcontext	uc_mcontext;  	compat_sigset_t			uc_sigmask; @@ -339,127 +319,6 @@ static int compat_restore_vfp_context(struct compat_vfp_sigframe __user *frame)  	return err ? -EFAULT : 0;  } -/* - * atomically swap in the new signal mask, and wait for a signal. - */ -asmlinkage int compat_sys_sigsuspend(int restart, compat_ulong_t oldmask, -				     compat_old_sigset_t mask) -{ -	sigset_t blocked; - -	siginitset(¤t->blocked, mask); -	return sigsuspend(&blocked); -} - -asmlinkage int compat_sys_sigaction(int sig, -				    const struct compat_old_sigaction __user *act, -				    struct compat_old_sigaction __user *oact) -{ -	struct k_sigaction new_ka, old_ka; -	int ret; -	compat_old_sigset_t mask; -	compat_uptr_t handler, restorer; - -	if (act) { -		if (!access_ok(VERIFY_READ, act, sizeof(*act)) || -		    __get_user(handler, &act->sa_handler) || -		    __get_user(restorer, &act->sa_restorer) || -		    __get_user(new_ka.sa.sa_flags, &act->sa_flags) || -		    __get_user(mask, &act->sa_mask)) -			return -EFAULT; - -		new_ka.sa.sa_handler = compat_ptr(handler); -		new_ka.sa.sa_restorer = compat_ptr(restorer); -		siginitset(&new_ka.sa.sa_mask, mask); -	} - -	ret = do_sigaction(sig, act ? &new_ka : NULL, oact ? &old_ka : NULL); - -	if (!ret && oact) { -		if (!access_ok(VERIFY_WRITE, oact, sizeof(*oact)) || -		    __put_user(ptr_to_compat(old_ka.sa.sa_handler), -			       &oact->sa_handler) || -		    __put_user(ptr_to_compat(old_ka.sa.sa_restorer), -			       &oact->sa_restorer) || -		    __put_user(old_ka.sa.sa_flags, &oact->sa_flags) || -		    __put_user(old_ka.sa.sa_mask.sig[0], &oact->sa_mask)) -			return -EFAULT; -	} - -	return ret; -} - -asmlinkage int compat_sys_rt_sigaction(int sig, -				       const struct compat_sigaction __user *act, -				       struct compat_sigaction __user *oact, -				       compat_size_t sigsetsize) -{ -	struct k_sigaction new_ka, old_ka; -	int ret; - -	/* XXX: Don't preclude handling different sized sigset_t's.  */ -	if (sigsetsize != sizeof(compat_sigset_t)) -		return -EINVAL; - -	if (act) { -		compat_uptr_t handler, restorer; - -		ret = get_user(handler, &act->sa_handler); -		new_ka.sa.sa_handler = compat_ptr(handler); -		ret |= get_user(restorer, &act->sa_restorer); -		new_ka.sa.sa_restorer = compat_ptr(restorer); -		ret |= get_sigset_t(&new_ka.sa.sa_mask, &act->sa_mask); -		ret |= __get_user(new_ka.sa.sa_flags, &act->sa_flags); -		if (ret) -			return -EFAULT; -	} - -	ret = do_sigaction(sig, act ? &new_ka : NULL, oact ? &old_ka : NULL); -	if (!ret && oact) { -		ret = put_user(ptr_to_compat(old_ka.sa.sa_handler), &oact->sa_handler); -		ret |= put_sigset_t(&oact->sa_mask, &old_ka.sa.sa_mask); -		ret |= __put_user(old_ka.sa.sa_flags, &oact->sa_flags); -	} -	return ret; -} - -int compat_do_sigaltstack(compat_uptr_t compat_uss, compat_uptr_t compat_uoss, -			  compat_ulong_t sp) -{ -	compat_stack_t __user *newstack = compat_ptr(compat_uss); -	compat_stack_t __user *oldstack = compat_ptr(compat_uoss); -	compat_uptr_t ss_sp; -	int ret; -	mm_segment_t old_fs; -	stack_t uss, uoss; - -	/* Marshall the compat new stack into a stack_t */ -	if (newstack) { -		if (get_user(ss_sp, &newstack->ss_sp) || -		    __get_user(uss.ss_flags, &newstack->ss_flags) || -		    __get_user(uss.ss_size, &newstack->ss_size)) -			return -EFAULT; -		uss.ss_sp = compat_ptr(ss_sp); -	} - -	old_fs = get_fs(); -	set_fs(KERNEL_DS); -	/* The __user pointer casts are valid because of the set_fs() */ -	ret = do_sigaltstack( -		newstack ? (stack_t __user *) &uss : NULL, -		oldstack ? (stack_t __user *) &uoss : NULL, -		(unsigned long)sp); -	set_fs(old_fs); - -	/* Convert the old stack_t into a compat stack. */ -	if (!ret && oldstack && -		(put_user(ptr_to_compat(uoss.ss_sp), &oldstack->ss_sp) || -		 __put_user(uoss.ss_flags, &oldstack->ss_flags) || -		 __put_user(uoss.ss_size, &oldstack->ss_size))) -		return -EFAULT; -	return ret; -} -  static int compat_restore_sigframe(struct pt_regs *regs,  				   struct compat_sigframe __user *sf)  { @@ -562,9 +421,7 @@ asmlinkage int compat_sys_rt_sigreturn(struct pt_regs *regs)  	if (compat_restore_sigframe(regs, &frame->sig))  		goto badframe; -	if (compat_do_sigaltstack(ptr_to_compat(&frame->sig.uc.uc_stack), -				 ptr_to_compat((void __user *)NULL), -				 regs->compat_sp) == -EFAULT) +	if (compat_restore_altstack(&frame->sig.uc.uc_stack))  		goto badframe;  	return regs->regs[0]; @@ -692,7 +549,6 @@ int compat_setup_rt_frame(int usig, struct k_sigaction *ka, siginfo_t *info,  			  sigset_t *set, struct pt_regs *regs)  {  	struct compat_rt_sigframe __user *frame; -	compat_stack_t stack;  	int err = 0;  	frame = compat_get_sigframe(ka, regs, sizeof(*frame)); @@ -703,13 +559,9 @@ int compat_setup_rt_frame(int usig, struct k_sigaction *ka, siginfo_t *info,  	err |= copy_siginfo_to_user32(&frame->info, info);  	__put_user_error(0, &frame->sig.uc.uc_flags, err); -	__put_user_error(NULL, &frame->sig.uc.uc_link, err); +	__put_user_error(0, &frame->sig.uc.uc_link, err); -	memset(&stack, 0, sizeof(stack)); -	stack.ss_sp = (compat_uptr_t)current->sas_ss_sp; -	stack.ss_flags = sas_ss_flags(regs->compat_sp); -	stack.ss_size = current->sas_ss_size; -	err |= __copy_to_user(&frame->sig.uc.uc_stack, &stack, sizeof(stack)); +	err |= __compat_save_altstack(&frame->sig.uc.uc_stack, regs->compat_sp);  	err |= compat_setup_sigframe(&frame->sig, regs, set); @@ -742,75 +594,6 @@ int compat_setup_frame(int usig, struct k_sigaction *ka, sigset_t *set,  	return err;  } -/* - * RT signals don't have generic compat wrappers. - * See arch/powerpc/kernel/signal_32.c - */ -asmlinkage int compat_sys_rt_sigprocmask(int how, compat_sigset_t __user *set, -					 compat_sigset_t __user *oset, -					 compat_size_t sigsetsize) -{ -	sigset_t s; -	sigset_t __user *up; -	int ret; -	mm_segment_t old_fs = get_fs(); - -	if (set) { -		if (get_sigset_t(&s, set)) -			return -EFAULT; -	} - -	set_fs(KERNEL_DS); -	/* This is valid because of the set_fs() */ -	up = (sigset_t __user *) &s; -	ret = sys_rt_sigprocmask(how, set ? up : NULL, oset ? up : NULL, -				 sigsetsize); -	set_fs(old_fs); -	if (ret) -		return ret; -	if (oset) { -		if (put_sigset_t(oset, &s)) -			return -EFAULT; -	} -	return 0; -} - -asmlinkage int compat_sys_rt_sigpending(compat_sigset_t __user *set, -					compat_size_t sigsetsize) -{ -	sigset_t s; -	int ret; -	mm_segment_t old_fs = get_fs(); - -	set_fs(KERNEL_DS); -	/* The __user pointer cast is valid because of the set_fs() */ -	ret = sys_rt_sigpending((sigset_t __user *) &s, sigsetsize); -	set_fs(old_fs); -	if (!ret) { -		if (put_sigset_t(set, &s)) -			return -EFAULT; -	} -	return ret; -} - -asmlinkage int compat_sys_rt_sigqueueinfo(int pid, int sig, -					  compat_siginfo_t __user *uinfo) -{ -	siginfo_t info; -	int ret; -	mm_segment_t old_fs = get_fs(); - -	ret = copy_siginfo_from_user32(&info, uinfo); -	if (unlikely(ret)) -		return ret; - -	set_fs (KERNEL_DS); -	/* The __user pointer cast is valid because of the set_fs() */ -	ret = sys_rt_sigqueueinfo(pid, sig, (siginfo_t __user *) &info); -	set_fs (old_fs); -	return ret; -} -  void compat_setup_restart_syscall(struct pt_regs *regs)  {         regs->regs[7] = __NR_compat_restart_syscall; diff --git a/arch/arm64/kernel/smp.c b/arch/arm64/kernel/smp.c index 538300f2273..5d54e3717bf 100644 --- a/arch/arm64/kernel/smp.c +++ b/arch/arm64/kernel/smp.c @@ -43,6 +43,7 @@  #include <asm/pgtable.h>  #include <asm/pgalloc.h>  #include <asm/processor.h> +#include <asm/smp_plat.h>  #include <asm/sections.h>  #include <asm/tlbflush.h>  #include <asm/ptrace.h> @@ -53,7 +54,7 @@   * where to place its SVC stack   */  struct secondary_data secondary_data; -volatile unsigned long secondary_holding_pen_release = -1; +volatile unsigned long secondary_holding_pen_release = INVALID_HWID;  enum ipi_msg_type {  	IPI_RESCHEDULE, @@ -70,7 +71,7 @@ static DEFINE_RAW_SPINLOCK(boot_lock);   * in coherency or not.  This is necessary for the hotplug code to work   * reliably.   */ -static void __cpuinit write_pen_release(int val) +static void __cpuinit write_pen_release(u64 val)  {  	void *start = (void *)&secondary_holding_pen_release;  	unsigned long size = sizeof(secondary_holding_pen_release); @@ -96,7 +97,7 @@ static int __cpuinit boot_secondary(unsigned int cpu, struct task_struct *idle)  	/*  	 * Update the pen release flag.  	 */ -	write_pen_release(cpu); +	write_pen_release(cpu_logical_map(cpu));  	/*  	 * Send an event, causing the secondaries to read pen_release. @@ -105,7 +106,7 @@ static int __cpuinit boot_secondary(unsigned int cpu, struct task_struct *idle)  	timeout = jiffies + (1 * HZ);  	while (time_before(jiffies, timeout)) { -		if (secondary_holding_pen_release == -1UL) +		if (secondary_holding_pen_release == INVALID_HWID)  			break;  		udelay(10);  	} @@ -116,7 +117,7 @@ static int __cpuinit boot_secondary(unsigned int cpu, struct task_struct *idle)  	 */  	raw_spin_unlock(&boot_lock); -	return secondary_holding_pen_release != -1 ? -ENOSYS : 0; +	return secondary_holding_pen_release != INVALID_HWID ? -ENOSYS : 0;  }  static DECLARE_COMPLETION(cpu_running); @@ -190,7 +191,7 @@ asmlinkage void __cpuinit secondary_start_kernel(void)  	 * Let the primary processor know we're out of the  	 * pen, then head off into the C entry point  	 */ -	write_pen_release(-1); +	write_pen_release(INVALID_HWID);  	/*  	 * Synchronise with the boot thread. @@ -216,7 +217,7 @@ asmlinkage void __cpuinit secondary_start_kernel(void)  	/*  	 * OK, it's off to the idle thread for us  	 */ -	cpu_idle(); +	cpu_startup_entry(CPUHP_ONLINE);  }  void __init smp_cpus_done(unsigned int max_cpus) @@ -233,18 +234,104 @@ void __init smp_prepare_boot_cpu(void)  }  static void (*smp_cross_call)(const struct cpumask *, unsigned int); -static phys_addr_t cpu_release_addr[NR_CPUS]; + +static const struct smp_enable_ops *enable_ops[] __initconst = { +	&smp_spin_table_ops, +	&smp_psci_ops, +	NULL, +}; + +static const struct smp_enable_ops *smp_enable_ops[NR_CPUS]; + +static const struct smp_enable_ops * __init smp_get_enable_ops(const char *name) +{ +	const struct smp_enable_ops **ops = enable_ops; + +	while (*ops) { +		if (!strcmp(name, (*ops)->name)) +			return *ops; + +		ops++; +	} + +	return NULL; +}  /* - * Enumerate the possible CPU set from the device tree. + * Enumerate the possible CPU set from the device tree and build the + * cpu logical map array containing MPIDR values related to logical + * cpus. Assumes that cpu_logical_map(0) has already been initialized.   */  void __init smp_init_cpus(void)  {  	const char *enable_method;  	struct device_node *dn = NULL; -	int cpu = 0; +	int i, cpu = 1; +	bool bootcpu_valid = false;  	while ((dn = of_find_node_by_type(dn, "cpu"))) { +		const u32 *cell; +		u64 hwid; + +		/* +		 * A cpu node with missing "reg" property is +		 * considered invalid to build a cpu_logical_map +		 * entry. +		 */ +		cell = of_get_property(dn, "reg", NULL); +		if (!cell) { +			pr_err("%s: missing reg property\n", dn->full_name); +			goto next; +		} +		hwid = of_read_number(cell, of_n_addr_cells(dn)); + +		/* +		 * Non affinity bits must be set to 0 in the DT +		 */ +		if (hwid & ~MPIDR_HWID_BITMASK) { +			pr_err("%s: invalid reg property\n", dn->full_name); +			goto next; +		} + +		/* +		 * Duplicate MPIDRs are a recipe for disaster. Scan +		 * all initialized entries and check for +		 * duplicates. If any is found just ignore the cpu. +		 * cpu_logical_map was initialized to INVALID_HWID to +		 * avoid matching valid MPIDR values. +		 */ +		for (i = 1; (i < cpu) && (i < NR_CPUS); i++) { +			if (cpu_logical_map(i) == hwid) { +				pr_err("%s: duplicate cpu reg properties in the DT\n", +					dn->full_name); +				goto next; +			} +		} + +		/* +		 * The numbering scheme requires that the boot CPU +		 * must be assigned logical id 0. Record it so that +		 * the logical map built from DT is validated and can +		 * be used. +		 */ +		if (hwid == cpu_logical_map(0)) { +			if (bootcpu_valid) { +				pr_err("%s: duplicate boot cpu reg property in DT\n", +					dn->full_name); +				goto next; +			} + +			bootcpu_valid = true; + +			/* +			 * cpu_logical_map has already been +			 * initialized and the boot cpu doesn't need +			 * the enable-method so continue without +			 * incrementing cpu. +			 */ +			continue; +		} +  		if (cpu >= NR_CPUS)  			goto next; @@ -252,23 +339,25 @@ void __init smp_init_cpus(void)  		 * We currently support only the "spin-table" enable-method.  		 */  		enable_method = of_get_property(dn, "enable-method", NULL); -		if (!enable_method || strcmp(enable_method, "spin-table")) { -			pr_err("CPU %d: missing or invalid enable-method property: %s\n", -			       cpu, enable_method); +		if (!enable_method) { +			pr_err("%s: missing enable-method property\n", +				dn->full_name);  			goto next;  		} -		/* -		 * Determine the address from which the CPU is polling. -		 */ -		if (of_property_read_u64(dn, "cpu-release-addr", -					 &cpu_release_addr[cpu])) { -			pr_err("CPU %d: missing or invalid cpu-release-addr property\n", -			       cpu); +		smp_enable_ops[cpu] = smp_get_enable_ops(enable_method); + +		if (!smp_enable_ops[cpu]) { +			pr_err("%s: invalid enable-method property: %s\n", +			       dn->full_name, enable_method);  			goto next;  		} -		set_cpu_possible(cpu, true); +		if (smp_enable_ops[cpu]->init_cpu(dn, cpu)) +			goto next; + +		pr_debug("cpu logical map 0x%llx\n", hwid); +		cpu_logical_map(cpu) = hwid;  next:  		cpu++;  	} @@ -277,12 +366,24 @@ next:  	if (cpu > NR_CPUS)  		pr_warning("no. of cores (%d) greater than configured maximum of %d - clipping\n",  			   cpu, NR_CPUS); + +	if (!bootcpu_valid) { +		pr_err("DT missing boot CPU MPIDR, not enabling secondaries\n"); +		return; +	} + +	/* +	 * All the cpus that made it to the cpu_logical_map have been +	 * validated so set them as possible cpus. +	 */ +	for (i = 0; i < NR_CPUS; i++) +		if (cpu_logical_map(i) != INVALID_HWID) +			set_cpu_possible(i, true);  }  void __init smp_prepare_cpus(unsigned int max_cpus)  { -	int cpu; -	void **release_addr; +	int cpu, err;  	unsigned int ncores = num_possible_cpus();  	/* @@ -291,30 +392,35 @@ void __init smp_prepare_cpus(unsigned int max_cpus)  	if (max_cpus > ncores)  		max_cpus = ncores; +	/* Don't bother if we're effectively UP */ +	if (max_cpus <= 1) +		return; +  	/*  	 * Initialise the present map (which describes the set of CPUs  	 * actually populated at the present time) and release the  	 * secondaries from the bootloader. +	 * +	 * Make sure we online at most (max_cpus - 1) additional CPUs.  	 */ +	max_cpus--;  	for_each_possible_cpu(cpu) {  		if (max_cpus == 0)  			break; -		if (!cpu_release_addr[cpu]) +		if (cpu == smp_processor_id()) +			continue; + +		if (!smp_enable_ops[cpu])  			continue; -		release_addr = __va(cpu_release_addr[cpu]); -		release_addr[0] = (void *)__pa(secondary_holding_pen); -		__flush_dcache_area(release_addr, sizeof(release_addr[0])); +		err = smp_enable_ops[cpu]->prepare_cpu(cpu); +		if (err) +			continue;  		set_cpu_present(cpu, true);  		max_cpus--;  	} - -	/* -	 * Send an event to wake up the secondaries. -	 */ -	sev();  } diff --git a/arch/arm64/kernel/smp_psci.c b/arch/arm64/kernel/smp_psci.c new file mode 100644 index 00000000000..0c533301be7 --- /dev/null +++ b/arch/arm64/kernel/smp_psci.c @@ -0,0 +1,53 @@ +/* + * PSCI SMP initialisation + * + * Copyright (C) 2013 ARM Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program.  If not, see <http://www.gnu.org/licenses/>. + */ + +#include <linux/init.h> +#include <linux/of.h> +#include <linux/smp.h> + +#include <asm/psci.h> +#include <asm/smp_plat.h> + +static int __init smp_psci_init_cpu(struct device_node *dn, int cpu) +{ +	return 0; +} + +static int __init smp_psci_prepare_cpu(int cpu) +{ +	int err; + +	if (!psci_ops.cpu_on) { +		pr_err("psci: no cpu_on method, not booting CPU%d\n", cpu); +		return -ENODEV; +	} + +	err = psci_ops.cpu_on(cpu_logical_map(cpu), __pa(secondary_holding_pen)); +	if (err) { +		pr_err("psci: failed to boot CPU%d (%d)\n", cpu, err); +		return err; +	} + +	return 0; +} + +const struct smp_enable_ops smp_psci_ops __initconst = { +	.name		= "psci", +	.init_cpu	= smp_psci_init_cpu, +	.prepare_cpu	= smp_psci_prepare_cpu, +}; diff --git a/arch/arm64/kernel/smp_spin_table.c b/arch/arm64/kernel/smp_spin_table.c new file mode 100644 index 00000000000..7c35fa682f7 --- /dev/null +++ b/arch/arm64/kernel/smp_spin_table.c @@ -0,0 +1,66 @@ +/* + * Spin Table SMP initialisation + * + * Copyright (C) 2013 ARM Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program.  If not, see <http://www.gnu.org/licenses/>. + */ + +#include <linux/init.h> +#include <linux/of.h> +#include <linux/smp.h> + +#include <asm/cacheflush.h> + +static phys_addr_t cpu_release_addr[NR_CPUS]; + +static int __init smp_spin_table_init_cpu(struct device_node *dn, int cpu) +{ +	/* +	 * Determine the address from which the CPU is polling. +	 */ +	if (of_property_read_u64(dn, "cpu-release-addr", +				 &cpu_release_addr[cpu])) { +		pr_err("CPU %d: missing or invalid cpu-release-addr property\n", +		       cpu); + +		return -1; +	} + +	return 0; +} + +static int __init smp_spin_table_prepare_cpu(int cpu) +{ +	void **release_addr; + +	if (!cpu_release_addr[cpu]) +		return -ENODEV; + +	release_addr = __va(cpu_release_addr[cpu]); +	release_addr[0] = (void *)__pa(secondary_holding_pen); +	__flush_dcache_area(release_addr, sizeof(release_addr[0])); + +	/* +	 * Send an event to wake up the secondary CPU. +	 */ +	sev(); + +	return 0; +} + +const struct smp_enable_ops smp_spin_table_ops __initconst = { +	.name		= "spin-table", +	.init_cpu 	= smp_spin_table_init_cpu, +	.prepare_cpu	= smp_spin_table_prepare_cpu, +}; diff --git a/arch/arm64/kernel/sys.c b/arch/arm64/kernel/sys.c index 8292a9b090f..3fa98ff14f0 100644 --- a/arch/arm64/kernel/sys.c +++ b/arch/arm64/kernel/sys.c @@ -40,7 +40,6 @@ asmlinkage long sys_mmap(unsigned long addr, unsigned long len,   * Wrappers to pass the pt_regs argument.   */  #define sys_rt_sigreturn	sys_rt_sigreturn_wrapper -#define sys_sigaltstack		sys_sigaltstack_wrapper  #include <asm/syscalls.h> diff --git a/arch/arm64/kernel/sys32.S b/arch/arm64/kernel/sys32.S index 7ef59e9245e..a1b19ed7467 100644 --- a/arch/arm64/kernel/sys32.S +++ b/arch/arm64/kernel/sys32.S @@ -39,11 +39,6 @@ compat_sys_rt_sigreturn_wrapper:  	b	compat_sys_rt_sigreturn  ENDPROC(compat_sys_rt_sigreturn_wrapper) -compat_sys_sigaltstack_wrapper: -	ldr	x2, [sp, #S_COMPAT_SP] -	b	compat_do_sigaltstack -ENDPROC(compat_sys_sigaltstack_wrapper) -  compat_sys_statfs64_wrapper:  	mov	w3, #84  	cmp	w1, #88 @@ -63,11 +58,6 @@ ENDPROC(compat_sys_fstatfs64_wrapper)   * in registers or that take 32-bit parameters which require sign   * extension.   */ -compat_sys_lseek_wrapper: -	sxtw	x1, w1 -	b	sys_lseek -ENDPROC(compat_sys_lseek_wrapper) -  compat_sys_pread64_wrapper:  	orr	x3, x4, x5, lsl #32  	b	sys_pread64 @@ -94,13 +84,6 @@ compat_sys_readahead_wrapper:  	b	sys_readahead  ENDPROC(compat_sys_readahead_wrapper) -compat_sys_lookup_dcookie: -	orr	x0, x0, x1, lsl #32 -	mov	w1, w2 -	mov	w2, w3 -	b	sys_lookup_dcookie -ENDPROC(compat_sys_lookup_dcookie) -  compat_sys_fadvise64_64_wrapper:  	mov	w6, w1  	orr	x1, x2, x3, lsl #32 @@ -121,13 +104,6 @@ compat_sys_fallocate_wrapper:  	b	sys_fallocate  ENDPROC(compat_sys_fallocate_wrapper) -compat_sys_fanotify_mark_wrapper: -	orr	x2, x2, x3, lsl #32 -	mov	w3, w4 -	mov	w4, w5 -	b	sys_fanotify_mark -ENDPROC(compat_sys_fanotify_mark_wrapper) -  #undef __SYSCALL  #define __SYSCALL(x, y)		.quad	y	// x diff --git a/arch/arm64/kernel/time.c b/arch/arm64/kernel/time.c index 3b4b7258f49..a551f88ae2c 100644 --- a/arch/arm64/kernel/time.c +++ b/arch/arm64/kernel/time.c @@ -31,8 +31,10 @@  #include <linux/syscore_ops.h>  #include <linux/timer.h>  #include <linux/irq.h> +#include <linux/delay.h> +#include <linux/clocksource.h> -#include <clocksource/arm_generic.h> +#include <clocksource/arm_arch_timer.h>  #include <asm/thread_info.h>  #include <asm/stacktrace.h> @@ -59,7 +61,32 @@ unsigned long profile_pc(struct pt_regs *regs)  EXPORT_SYMBOL(profile_pc);  #endif +static u64 sched_clock_mult __read_mostly; + +unsigned long long notrace sched_clock(void) +{ +	return arch_timer_read_counter() * sched_clock_mult; +} + +int read_current_timer(unsigned long *timer_value) +{ +	*timer_value = arch_timer_read_counter(); +	return 0; +} +  void __init time_init(void)  { -	arm_generic_timer_init(); +	u32 arch_timer_rate; + +	clocksource_of_init(); + +	arch_timer_rate = arch_timer_get_rate(); +	if (!arch_timer_rate) +		panic("Unable to initialise architected timer.\n"); + +	/* Cache the sched_clock multiplier to save a divide in the hot path. */ +	sched_clock_mult = NSEC_PER_SEC / arch_timer_rate; + +	/* Calibrate the delay loop directly */ +	lpj_fine = arch_timer_rate / HZ;  } diff --git a/arch/arm64/kernel/traps.c b/arch/arm64/kernel/traps.c index 3883f842434..61d7dd29f75 100644 --- a/arch/arm64/kernel/traps.c +++ b/arch/arm64/kernel/traps.c @@ -167,13 +167,6 @@ static void dump_backtrace(struct pt_regs *regs, struct task_struct *tsk)  	}  } -void dump_stack(void) -{ -	dump_backtrace(NULL, NULL); -} - -EXPORT_SYMBOL(dump_stack); -  void show_stack(struct task_struct *tsk, unsigned long *sp)  {  	dump_backtrace(NULL, tsk); @@ -242,7 +235,7 @@ void die(const char *str, struct pt_regs *regs, int err)  		crash_kexec(regs);  	bust_spinlocks(0); -	add_taint(TAINT_DIE); +	add_taint(TAINT_DIE, LOCKDEP_NOW_UNRELIABLE);  	raw_spin_unlock_irq(&die_lock);  	oops_exit();  |