aboutsummaryrefslogtreecommitdiffstats
path: root/arch/arm64/kernel
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2013-02-20 17:30:33 -0500
committerLinus Torvalds <torvalds@linux-foundation.org>2013-02-20 17:30:33 -0500
commit79a69d342d71b2b4eafdf51e2451606cfe380a44 (patch)
tree7246dbcb872f416b3e27a8020106cf5098cb30f9 /arch/arm64/kernel
parent6db167dfc013b0e114c81077ac091ba26a69f4ed (diff)
parentec45d1cfd3cb65121fc52f39efc17d832f4f7b91 (diff)
Merge tag 'arm64-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/cmarinas/linux-aarch64
Pull arm64 patches from Catalin Marinas: - SMP support for the PSCI booting protocol (power state coordination interface). - Simple earlyprintk support. - Platform devices populated by default from the DT (SoC-agnostic). - CONTEXTIDR support (used by external trace tools). * tag 'arm64-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/cmarinas/linux-aarch64: arm64: mm: update CONTEXTIDR register to contain PID of current process arm64: atomics: fix grossly inconsistent asm constraints for exclusives arm64: compat: use compat_uptr_t type for compat_ucontext.uc_link arm64: Select ARCH_WANT_FRAME_POINTERS arm64: Add kvm_para.h and xor.h generic headers arm64: SMP: enable PSCI boot method arm64: psci: add support for PSCI invocations from the kernel arm64: SMP: rework the SMP code to be enabling method agnostic arm64: perf: add guest vs host discrimination arm64: add COMPAT_PSR_*_BIT flags arm64: Add simple earlyprintk support arm64: Populate the platform devices
Diffstat (limited to 'arch/arm64/kernel')
-rw-r--r--arch/arm64/kernel/Makefile5
-rw-r--r--arch/arm64/kernel/early_printk.c118
-rw-r--r--arch/arm64/kernel/head.S12
-rw-r--r--arch/arm64/kernel/perf_event.c37
-rw-r--r--arch/arm64/kernel/process.c4
-rw-r--r--arch/arm64/kernel/psci.c211
-rw-r--r--arch/arm64/kernel/setup.c11
-rw-r--r--arch/arm64/kernel/signal32.c4
-rw-r--r--arch/arm64/kernel/smp.c69
-rw-r--r--arch/arm64/kernel/smp_psci.c52
-rw-r--r--arch/arm64/kernel/smp_spin_table.c66
11 files changed, 560 insertions, 29 deletions
diff --git a/arch/arm64/kernel/Makefile b/arch/arm64/kernel/Makefile
index 74239c31e25a..7b4b564961d4 100644
--- a/arch/arm64/kernel/Makefile
+++ b/arch/arm64/kernel/Makefile
@@ -9,14 +9,15 @@ AFLAGS_head.o := -DTEXT_OFFSET=$(TEXT_OFFSET)
9arm64-obj-y := cputable.o debug-monitors.o entry.o irq.o fpsimd.o \ 9arm64-obj-y := cputable.o debug-monitors.o entry.o irq.o fpsimd.o \
10 entry-fpsimd.o process.o ptrace.o setup.o signal.o \ 10 entry-fpsimd.o process.o ptrace.o setup.o signal.o \
11 sys.o stacktrace.o time.o traps.o io.o vdso.o \ 11 sys.o stacktrace.o time.o traps.o io.o vdso.o \
12 hyp-stub.o 12 hyp-stub.o psci.o
13 13
14arm64-obj-$(CONFIG_COMPAT) += sys32.o kuser32.o signal32.o \ 14arm64-obj-$(CONFIG_COMPAT) += sys32.o kuser32.o signal32.o \
15 sys_compat.o 15 sys_compat.o
16arm64-obj-$(CONFIG_MODULES) += arm64ksyms.o module.o 16arm64-obj-$(CONFIG_MODULES) += arm64ksyms.o module.o
17arm64-obj-$(CONFIG_SMP) += smp.o 17arm64-obj-$(CONFIG_SMP) += smp.o smp_spin_table.o smp_psci.o
18arm64-obj-$(CONFIG_HW_PERF_EVENTS) += perf_event.o 18arm64-obj-$(CONFIG_HW_PERF_EVENTS) += perf_event.o
19arm64-obj-$(CONFIG_HAVE_HW_BREAKPOINT)+= hw_breakpoint.o 19arm64-obj-$(CONFIG_HAVE_HW_BREAKPOINT)+= hw_breakpoint.o
20arm64-obj-$(CONFIG_EARLY_PRINTK) += early_printk.o
20 21
21obj-y += $(arm64-obj-y) vdso/ 22obj-y += $(arm64-obj-y) vdso/
22obj-m += $(arm64-obj-m) 23obj-m += $(arm64-obj-m)
diff --git a/arch/arm64/kernel/early_printk.c b/arch/arm64/kernel/early_printk.c
new file mode 100644
index 000000000000..7e320a2edb9b
--- /dev/null
+++ b/arch/arm64/kernel/early_printk.c
@@ -0,0 +1,118 @@
1/*
2 * Earlyprintk support.
3 *
4 * Copyright (C) 2012 ARM Ltd.
5 * Author: Catalin Marinas <catalin.marinas@arm.com>
6 *
7 * This program is free software: you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License version 2 as
9 * published by the Free Software Foundation.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program. If not, see <http://www.gnu.org/licenses/>.
18 */
19#include <linux/kernel.h>
20#include <linux/console.h>
21#include <linux/init.h>
22#include <linux/string.h>
23#include <linux/mm.h>
24#include <linux/io.h>
25
26#include <linux/amba/serial.h>
27
28static void __iomem *early_base;
29static void (*printch)(char ch);
30
31/*
32 * PL011 single character TX.
33 */
34static void pl011_printch(char ch)
35{
36 while (readl_relaxed(early_base + UART01x_FR) & UART01x_FR_TXFF)
37 ;
38 writeb_relaxed(ch, early_base + UART01x_DR);
39 while (readl_relaxed(early_base + UART01x_FR) & UART01x_FR_BUSY)
40 ;
41}
42
43struct earlycon_match {
44 const char *name;
45 void (*printch)(char ch);
46};
47
48static const struct earlycon_match earlycon_match[] __initconst = {
49 { .name = "pl011", .printch = pl011_printch, },
50 {}
51};
52
53static void early_write(struct console *con, const char *s, unsigned n)
54{
55 while (n-- > 0) {
56 if (*s == '\n')
57 printch('\r');
58 printch(*s);
59 s++;
60 }
61}
62
63static struct console early_console = {
64 .name = "earlycon",
65 .write = early_write,
66 .flags = CON_PRINTBUFFER | CON_BOOT,
67 .index = -1,
68};
69
70/*
71 * Parse earlyprintk=... parameter in the format:
72 *
73 * <name>[,<addr>][,<options>]
74 *
75 * and register the early console. It is assumed that the UART has been
76 * initialised by the bootloader already.
77 */
78static int __init setup_early_printk(char *buf)
79{
80 const struct earlycon_match *match = earlycon_match;
81 phys_addr_t paddr = 0;
82
83 if (!buf) {
84 pr_warning("No earlyprintk arguments passed.\n");
85 return 0;
86 }
87
88 while (match->name) {
89 size_t len = strlen(match->name);
90 if (!strncmp(buf, match->name, len)) {
91 buf += len;
92 break;
93 }
94 match++;
95 }
96 if (!match->name) {
97 pr_warning("Unknown earlyprintk arguments: %s\n", buf);
98 return 0;
99 }
100
101 /* I/O address */
102 if (!strncmp(buf, ",0x", 3)) {
103 char *e;
104 paddr = simple_strtoul(buf + 1, &e, 16);
105 buf = e;
106 }
107 /* no options parsing yet */
108
109 if (paddr)
110 early_base = early_io_map(paddr, EARLYCON_IOBASE);
111
112 printch = match->printch;
113 register_console(&early_console);
114
115 return 0;
116}
117
118early_param("earlyprintk", setup_early_printk);
diff --git a/arch/arm64/kernel/head.S b/arch/arm64/kernel/head.S
index 368ad1f7c36c..0a0a49756826 100644
--- a/arch/arm64/kernel/head.S
+++ b/arch/arm64/kernel/head.S
@@ -82,10 +82,8 @@
82 82
83#ifdef CONFIG_ARM64_64K_PAGES 83#ifdef CONFIG_ARM64_64K_PAGES
84#define MM_MMUFLAGS PTE_ATTRINDX(MT_NORMAL) | PTE_FLAGS 84#define MM_MMUFLAGS PTE_ATTRINDX(MT_NORMAL) | PTE_FLAGS
85#define IO_MMUFLAGS PTE_ATTRINDX(MT_DEVICE_nGnRE) | PTE_XN | PTE_FLAGS
86#else 85#else
87#define MM_MMUFLAGS PMD_ATTRINDX(MT_NORMAL) | PMD_FLAGS 86#define MM_MMUFLAGS PMD_ATTRINDX(MT_NORMAL) | PMD_FLAGS
88#define IO_MMUFLAGS PMD_ATTRINDX(MT_DEVICE_nGnRE) | PMD_SECT_XN | PMD_FLAGS
89#endif 87#endif
90 88
91/* 89/*
@@ -368,6 +366,7 @@ ENDPROC(__calc_phys_offset)
368 * - identity mapping to enable the MMU (low address, TTBR0) 366 * - identity mapping to enable the MMU (low address, TTBR0)
369 * - first few MB of the kernel linear mapping to jump to once the MMU has 367 * - first few MB of the kernel linear mapping to jump to once the MMU has
370 * been enabled, including the FDT blob (TTBR1) 368 * been enabled, including the FDT blob (TTBR1)
369 * - UART mapping if CONFIG_EARLY_PRINTK is enabled (TTBR1)
371 */ 370 */
372__create_page_tables: 371__create_page_tables:
373 pgtbl x25, x26, x24 // idmap_pg_dir and swapper_pg_dir addresses 372 pgtbl x25, x26, x24 // idmap_pg_dir and swapper_pg_dir addresses
@@ -420,6 +419,15 @@ __create_page_tables:
420 sub x6, x6, #1 // inclusive range 419 sub x6, x6, #1 // inclusive range
421 create_block_map x0, x7, x3, x5, x6 420 create_block_map x0, x7, x3, x5, x6
4221: 4211:
422#ifdef CONFIG_EARLY_PRINTK
423 /*
424 * Create the pgd entry for the UART mapping. The full mapping is done
425 * later based earlyprintk kernel parameter.
426 */
427 ldr x5, =EARLYCON_IOBASE // UART virtual address
428 add x0, x26, #2 * PAGE_SIZE // section table address
429 create_pgd_entry x26, x0, x5, x6, x7
430#endif
423 ret 431 ret
424ENDPROC(__create_page_tables) 432ENDPROC(__create_page_tables)
425 .ltorg 433 .ltorg
diff --git a/arch/arm64/kernel/perf_event.c b/arch/arm64/kernel/perf_event.c
index f7073c7b1ca9..1e49e5eb81e9 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,
1331{ 1331{
1332 struct frame_tail __user *tail; 1332 struct frame_tail __user *tail;
1333 1333
1334 if (perf_guest_cbs && perf_guest_cbs->is_in_guest()) {
1335 /* We don't support guest os callchain now */
1336 return;
1337 }
1338
1334 tail = (struct frame_tail __user *)regs->regs[29]; 1339 tail = (struct frame_tail __user *)regs->regs[29];
1335 1340
1336 while (entry->nr < PERF_MAX_STACK_DEPTH && 1341 while (entry->nr < PERF_MAX_STACK_DEPTH &&
@@ -1355,8 +1360,40 @@ void perf_callchain_kernel(struct perf_callchain_entry *entry,
1355{ 1360{
1356 struct stackframe frame; 1361 struct stackframe frame;
1357 1362
1363 if (perf_guest_cbs && perf_guest_cbs->is_in_guest()) {
1364 /* We don't support guest os callchain now */
1365 return;
1366 }
1367
1358 frame.fp = regs->regs[29]; 1368 frame.fp = regs->regs[29];
1359 frame.sp = regs->sp; 1369 frame.sp = regs->sp;
1360 frame.pc = regs->pc; 1370 frame.pc = regs->pc;
1361 walk_stackframe(&frame, callchain_trace, entry); 1371 walk_stackframe(&frame, callchain_trace, entry);
1362} 1372}
1373
1374unsigned long perf_instruction_pointer(struct pt_regs *regs)
1375{
1376 if (perf_guest_cbs && perf_guest_cbs->is_in_guest())
1377 return perf_guest_cbs->get_guest_ip();
1378
1379 return instruction_pointer(regs);
1380}
1381
1382unsigned long perf_misc_flags(struct pt_regs *regs)
1383{
1384 int misc = 0;
1385
1386 if (perf_guest_cbs && perf_guest_cbs->is_in_guest()) {
1387 if (perf_guest_cbs->is_user_mode())
1388 misc |= PERF_RECORD_MISC_GUEST_USER;
1389 else
1390 misc |= PERF_RECORD_MISC_GUEST_KERNEL;
1391 } else {
1392 if (user_mode(regs))
1393 misc |= PERF_RECORD_MISC_USER;
1394 else
1395 misc |= PERF_RECORD_MISC_KERNEL;
1396 }
1397
1398 return misc;
1399}
diff --git a/arch/arm64/kernel/process.c b/arch/arm64/kernel/process.c
index c7002d40a9b0..0337cdb0667b 100644
--- a/arch/arm64/kernel/process.c
+++ b/arch/arm64/kernel/process.c
@@ -45,9 +45,10 @@
45 45
46#include <asm/compat.h> 46#include <asm/compat.h>
47#include <asm/cacheflush.h> 47#include <asm/cacheflush.h>
48#include <asm/fpsimd.h>
49#include <asm/mmu_context.h>
48#include <asm/processor.h> 50#include <asm/processor.h>
49#include <asm/stacktrace.h> 51#include <asm/stacktrace.h>
50#include <asm/fpsimd.h>
51 52
52static void setup_restart(void) 53static void setup_restart(void)
53{ 54{
@@ -314,6 +315,7 @@ struct task_struct *__switch_to(struct task_struct *prev,
314 /* the actual thread switch */ 315 /* the actual thread switch */
315 last = cpu_switch_to(prev, next); 316 last = cpu_switch_to(prev, next);
316 317
318 contextidr_thread_switch(next);
317 return last; 319 return last;
318} 320}
319 321
diff --git a/arch/arm64/kernel/psci.c b/arch/arm64/kernel/psci.c
new file mode 100644
index 000000000000..14f73c445ff5
--- /dev/null
+++ b/arch/arm64/kernel/psci.c
@@ -0,0 +1,211 @@
1/*
2 * This program is free software; you can redistribute it and/or modify
3 * it under the terms of the GNU General Public License version 2 as
4 * published by the Free Software Foundation.
5 *
6 * This program is distributed in the hope that it will be useful,
7 * but WITHOUT ANY WARRANTY; without even the implied warranty of
8 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
9 * GNU General Public License for more details.
10 *
11 * Copyright (C) 2013 ARM Limited
12 *
13 * Author: Will Deacon <will.deacon@arm.com>
14 */
15
16#define pr_fmt(fmt) "psci: " fmt
17
18#include <linux/init.h>
19#include <linux/of.h>
20
21#include <asm/compiler.h>
22#include <asm/errno.h>
23#include <asm/psci.h>
24
25struct psci_operations psci_ops;
26
27static int (*invoke_psci_fn)(u64, u64, u64, u64);
28
29enum psci_function {
30 PSCI_FN_CPU_SUSPEND,
31 PSCI_FN_CPU_ON,
32 PSCI_FN_CPU_OFF,
33 PSCI_FN_MIGRATE,
34 PSCI_FN_MAX,
35};
36
37static u32 psci_function_id[PSCI_FN_MAX];
38
39#define PSCI_RET_SUCCESS 0
40#define PSCI_RET_EOPNOTSUPP -1
41#define PSCI_RET_EINVAL -2
42#define PSCI_RET_EPERM -3
43
44static int psci_to_linux_errno(int errno)
45{
46 switch (errno) {
47 case PSCI_RET_SUCCESS:
48 return 0;
49 case PSCI_RET_EOPNOTSUPP:
50 return -EOPNOTSUPP;
51 case PSCI_RET_EINVAL:
52 return -EINVAL;
53 case PSCI_RET_EPERM:
54 return -EPERM;
55 };
56
57 return -EINVAL;
58}
59
60#define PSCI_POWER_STATE_ID_MASK 0xffff
61#define PSCI_POWER_STATE_ID_SHIFT 0
62#define PSCI_POWER_STATE_TYPE_MASK 0x1
63#define PSCI_POWER_STATE_TYPE_SHIFT 16
64#define PSCI_POWER_STATE_AFFL_MASK 0x3
65#define PSCI_POWER_STATE_AFFL_SHIFT 24
66
67static u32 psci_power_state_pack(struct psci_power_state state)
68{
69 return ((state.id & PSCI_POWER_STATE_ID_MASK)
70 << PSCI_POWER_STATE_ID_SHIFT) |
71 ((state.type & PSCI_POWER_STATE_TYPE_MASK)
72 << PSCI_POWER_STATE_TYPE_SHIFT) |
73 ((state.affinity_level & PSCI_POWER_STATE_AFFL_MASK)
74 << PSCI_POWER_STATE_AFFL_SHIFT);
75}
76
77/*
78 * The following two functions are invoked via the invoke_psci_fn pointer
79 * and will not be inlined, allowing us to piggyback on the AAPCS.
80 */
81static noinline int __invoke_psci_fn_hvc(u64 function_id, u64 arg0, u64 arg1,
82 u64 arg2)
83{
84 asm volatile(
85 __asmeq("%0", "x0")
86 __asmeq("%1", "x1")
87 __asmeq("%2", "x2")
88 __asmeq("%3", "x3")
89 "hvc #0\n"
90 : "+r" (function_id)
91 : "r" (arg0), "r" (arg1), "r" (arg2));
92
93 return function_id;
94}
95
96static noinline int __invoke_psci_fn_smc(u64 function_id, u64 arg0, u64 arg1,
97 u64 arg2)
98{
99 asm volatile(
100 __asmeq("%0", "x0")
101 __asmeq("%1", "x1")
102 __asmeq("%2", "x2")
103 __asmeq("%3", "x3")
104 "smc #0\n"
105 : "+r" (function_id)
106 : "r" (arg0), "r" (arg1), "r" (arg2));
107
108 return function_id;
109}
110
111static int psci_cpu_suspend(struct psci_power_state state,
112 unsigned long entry_point)
113{
114 int err;
115 u32 fn, power_state;
116
117 fn = psci_function_id[PSCI_FN_CPU_SUSPEND];
118 power_state = psci_power_state_pack(state);
119 err = invoke_psci_fn(fn, power_state, entry_point, 0);
120 return psci_to_linux_errno(err);
121}
122
123static int psci_cpu_off(struct psci_power_state state)
124{
125 int err;
126 u32 fn, power_state;
127
128 fn = psci_function_id[PSCI_FN_CPU_OFF];
129 power_state = psci_power_state_pack(state);
130 err = invoke_psci_fn(fn, power_state, 0, 0);
131 return psci_to_linux_errno(err);
132}
133
134static int psci_cpu_on(unsigned long cpuid, unsigned long entry_point)
135{
136 int err;
137 u32 fn;
138
139 fn = psci_function_id[PSCI_FN_CPU_ON];
140 err = invoke_psci_fn(fn, cpuid, entry_point, 0);
141 return psci_to_linux_errno(err);
142}
143
144static int psci_migrate(unsigned long cpuid)
145{
146 int err;
147 u32 fn;
148
149 fn = psci_function_id[PSCI_FN_MIGRATE];
150 err = invoke_psci_fn(fn, cpuid, 0, 0);
151 return psci_to_linux_errno(err);
152}
153
154static const struct of_device_id psci_of_match[] __initconst = {
155 { .compatible = "arm,psci", },
156 {},
157};
158
159int __init psci_init(void)
160{
161 struct device_node *np;
162 const char *method;
163 u32 id;
164 int err = 0;
165
166 np = of_find_matching_node(NULL, psci_of_match);
167 if (!np)
168 return -ENODEV;
169
170 pr_info("probing function IDs from device-tree\n");
171
172 if (of_property_read_string(np, "method", &method)) {
173 pr_warning("missing \"method\" property\n");
174 err = -ENXIO;
175 goto out_put_node;
176 }
177
178 if (!strcmp("hvc", method)) {
179 invoke_psci_fn = __invoke_psci_fn_hvc;
180 } else if (!strcmp("smc", method)) {
181 invoke_psci_fn = __invoke_psci_fn_smc;
182 } else {
183 pr_warning("invalid \"method\" property: %s\n", method);
184 err = -EINVAL;
185 goto out_put_node;
186 }
187
188 if (!of_property_read_u32(np, "cpu_suspend", &id)) {
189 psci_function_id[PSCI_FN_CPU_SUSPEND] = id;
190 psci_ops.cpu_suspend = psci_cpu_suspend;
191 }
192
193 if (!of_property_read_u32(np, "cpu_off", &id)) {
194 psci_function_id[PSCI_FN_CPU_OFF] = id;
195 psci_ops.cpu_off = psci_cpu_off;
196 }
197
198 if (!of_property_read_u32(np, "cpu_on", &id)) {
199 psci_function_id[PSCI_FN_CPU_ON] = id;
200 psci_ops.cpu_on = psci_cpu_on;
201 }
202
203 if (!of_property_read_u32(np, "migrate", &id)) {
204 psci_function_id[PSCI_FN_MIGRATE] = id;
205 psci_ops.migrate = psci_migrate;
206 }
207
208out_put_node:
209 of_node_put(np);
210 return err;
211}
diff --git a/arch/arm64/kernel/setup.c b/arch/arm64/kernel/setup.c
index 7665a9bfdb1e..113db863f832 100644
--- a/arch/arm64/kernel/setup.c
+++ b/arch/arm64/kernel/setup.c
@@ -39,6 +39,7 @@
39#include <linux/proc_fs.h> 39#include <linux/proc_fs.h>
40#include <linux/memblock.h> 40#include <linux/memblock.h>
41#include <linux/of_fdt.h> 41#include <linux/of_fdt.h>
42#include <linux/of_platform.h>
42 43
43#include <asm/cputype.h> 44#include <asm/cputype.h>
44#include <asm/elf.h> 45#include <asm/elf.h>
@@ -49,6 +50,7 @@
49#include <asm/tlbflush.h> 50#include <asm/tlbflush.h>
50#include <asm/traps.h> 51#include <asm/traps.h>
51#include <asm/memblock.h> 52#include <asm/memblock.h>
53#include <asm/psci.h>
52 54
53unsigned int processor_id; 55unsigned int processor_id;
54EXPORT_SYMBOL(processor_id); 56EXPORT_SYMBOL(processor_id);
@@ -260,6 +262,8 @@ void __init setup_arch(char **cmdline_p)
260 262
261 unflatten_device_tree(); 263 unflatten_device_tree();
262 264
265 psci_init();
266
263#ifdef CONFIG_SMP 267#ifdef CONFIG_SMP
264 smp_init_cpus(); 268 smp_init_cpus();
265#endif 269#endif
@@ -289,6 +293,13 @@ static int __init topology_init(void)
289} 293}
290subsys_initcall(topology_init); 294subsys_initcall(topology_init);
291 295
296static int __init arm64_device_probe(void)
297{
298 of_platform_populate(NULL, of_default_bus_match_table, NULL, NULL);
299 return 0;
300}
301device_initcall(arm64_device_probe);
302
292static const char *hwcap_str[] = { 303static const char *hwcap_str[] = {
293 "fp", 304 "fp",
294 "asimd", 305 "asimd",
diff --git a/arch/arm64/kernel/signal32.c b/arch/arm64/kernel/signal32.c
index a4db3d22aac4..41db148a7eb9 100644
--- a/arch/arm64/kernel/signal32.c
+++ b/arch/arm64/kernel/signal32.c
@@ -76,7 +76,7 @@ struct compat_sigcontext {
76 76
77struct compat_ucontext { 77struct compat_ucontext {
78 compat_ulong_t uc_flags; 78 compat_ulong_t uc_flags;
79 struct compat_ucontext *uc_link; 79 compat_uptr_t uc_link;
80 compat_stack_t uc_stack; 80 compat_stack_t uc_stack;
81 struct compat_sigcontext uc_mcontext; 81 struct compat_sigcontext uc_mcontext;
82 compat_sigset_t uc_sigmask; 82 compat_sigset_t uc_sigmask;
@@ -703,7 +703,7 @@ int compat_setup_rt_frame(int usig, struct k_sigaction *ka, siginfo_t *info,
703 err |= copy_siginfo_to_user32(&frame->info, info); 703 err |= copy_siginfo_to_user32(&frame->info, info);
704 704
705 __put_user_error(0, &frame->sig.uc.uc_flags, err); 705 __put_user_error(0, &frame->sig.uc.uc_flags, err);
706 __put_user_error(NULL, &frame->sig.uc.uc_link, err); 706 __put_user_error(0, &frame->sig.uc.uc_link, err);
707 707
708 memset(&stack, 0, sizeof(stack)); 708 memset(&stack, 0, sizeof(stack));
709 stack.ss_sp = (compat_uptr_t)current->sas_ss_sp; 709 stack.ss_sp = (compat_uptr_t)current->sas_ss_sp;
diff --git a/arch/arm64/kernel/smp.c b/arch/arm64/kernel/smp.c
index 538300f2273d..bdd34597254b 100644
--- a/arch/arm64/kernel/smp.c
+++ b/arch/arm64/kernel/smp.c
@@ -233,7 +233,28 @@ void __init smp_prepare_boot_cpu(void)
233} 233}
234 234
235static void (*smp_cross_call)(const struct cpumask *, unsigned int); 235static void (*smp_cross_call)(const struct cpumask *, unsigned int);
236static phys_addr_t cpu_release_addr[NR_CPUS]; 236
237static const struct smp_enable_ops *enable_ops[] __initconst = {
238 &smp_spin_table_ops,
239 &smp_psci_ops,
240 NULL,
241};
242
243static const struct smp_enable_ops *smp_enable_ops[NR_CPUS];
244
245static const struct smp_enable_ops * __init smp_get_enable_ops(const char *name)
246{
247 const struct smp_enable_ops *ops = enable_ops[0];
248
249 while (ops) {
250 if (!strcmp(name, ops->name))
251 return ops;
252
253 ops++;
254 }
255
256 return NULL;
257}
237 258
238/* 259/*
239 * Enumerate the possible CPU set from the device tree. 260 * Enumerate the possible CPU set from the device tree.
@@ -252,22 +273,22 @@ void __init smp_init_cpus(void)
252 * We currently support only the "spin-table" enable-method. 273 * We currently support only the "spin-table" enable-method.
253 */ 274 */
254 enable_method = of_get_property(dn, "enable-method", NULL); 275 enable_method = of_get_property(dn, "enable-method", NULL);
255 if (!enable_method || strcmp(enable_method, "spin-table")) { 276 if (!enable_method) {
256 pr_err("CPU %d: missing or invalid enable-method property: %s\n", 277 pr_err("CPU %d: missing enable-method property\n", cpu);
257 cpu, enable_method);
258 goto next; 278 goto next;
259 } 279 }
260 280
261 /* 281 smp_enable_ops[cpu] = smp_get_enable_ops(enable_method);
262 * Determine the address from which the CPU is polling. 282
263 */ 283 if (!smp_enable_ops[cpu]) {
264 if (of_property_read_u64(dn, "cpu-release-addr", 284 pr_err("CPU %d: invalid enable-method property: %s\n",
265 &cpu_release_addr[cpu])) { 285 cpu, enable_method);
266 pr_err("CPU %d: missing or invalid cpu-release-addr property\n",
267 cpu);
268 goto next; 286 goto next;
269 } 287 }
270 288
289 if (smp_enable_ops[cpu]->init_cpu(dn, cpu))
290 goto next;
291
271 set_cpu_possible(cpu, true); 292 set_cpu_possible(cpu, true);
272next: 293next:
273 cpu++; 294 cpu++;
@@ -281,8 +302,7 @@ next:
281 302
282void __init smp_prepare_cpus(unsigned int max_cpus) 303void __init smp_prepare_cpus(unsigned int max_cpus)
283{ 304{
284 int cpu; 305 int cpu, err;
285 void **release_addr;
286 unsigned int ncores = num_possible_cpus(); 306 unsigned int ncores = num_possible_cpus();
287 307
288 /* 308 /*
@@ -291,30 +311,35 @@ void __init smp_prepare_cpus(unsigned int max_cpus)
291 if (max_cpus > ncores) 311 if (max_cpus > ncores)
292 max_cpus = ncores; 312 max_cpus = ncores;
293 313
314 /* Don't bother if we're effectively UP */
315 if (max_cpus <= 1)
316 return;
317
294 /* 318 /*
295 * Initialise the present map (which describes the set of CPUs 319 * Initialise the present map (which describes the set of CPUs
296 * actually populated at the present time) and release the 320 * actually populated at the present time) and release the
297 * secondaries from the bootloader. 321 * secondaries from the bootloader.
322 *
323 * Make sure we online at most (max_cpus - 1) additional CPUs.
298 */ 324 */
325 max_cpus--;
299 for_each_possible_cpu(cpu) { 326 for_each_possible_cpu(cpu) {
300 if (max_cpus == 0) 327 if (max_cpus == 0)
301 break; 328 break;
302 329
303 if (!cpu_release_addr[cpu]) 330 if (cpu == smp_processor_id())
331 continue;
332
333 if (!smp_enable_ops[cpu])
304 continue; 334 continue;
305 335
306 release_addr = __va(cpu_release_addr[cpu]); 336 err = smp_enable_ops[cpu]->prepare_cpu(cpu);
307 release_addr[0] = (void *)__pa(secondary_holding_pen); 337 if (err)
308 __flush_dcache_area(release_addr, sizeof(release_addr[0])); 338 continue;
309 339
310 set_cpu_present(cpu, true); 340 set_cpu_present(cpu, true);
311 max_cpus--; 341 max_cpus--;
312 } 342 }
313
314 /*
315 * Send an event to wake up the secondaries.
316 */
317 sev();
318} 343}
319 344
320 345
diff --git a/arch/arm64/kernel/smp_psci.c b/arch/arm64/kernel/smp_psci.c
new file mode 100644
index 000000000000..112091684c22
--- /dev/null
+++ b/arch/arm64/kernel/smp_psci.c
@@ -0,0 +1,52 @@
1/*
2 * PSCI SMP initialisation
3 *
4 * Copyright (C) 2013 ARM Ltd.
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License version 2 as
8 * published by the Free Software Foundation.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
17 */
18
19#include <linux/init.h>
20#include <linux/of.h>
21#include <linux/smp.h>
22
23#include <asm/psci.h>
24
25static int __init smp_psci_init_cpu(struct device_node *dn, int cpu)
26{
27 return 0;
28}
29
30static int __init smp_psci_prepare_cpu(int cpu)
31{
32 int err;
33
34 if (!psci_ops.cpu_on) {
35 pr_err("psci: no cpu_on method, not booting CPU%d\n", cpu);
36 return -ENODEV;
37 }
38
39 err = psci_ops.cpu_on(cpu, __pa(secondary_holding_pen));
40 if (err) {
41 pr_err("psci: failed to boot CPU%d (%d)\n", cpu, err);
42 return err;
43 }
44
45 return 0;
46}
47
48const struct smp_enable_ops smp_psci_ops __initconst = {
49 .name = "psci",
50 .init_cpu = smp_psci_init_cpu,
51 .prepare_cpu = smp_psci_prepare_cpu,
52};
diff --git a/arch/arm64/kernel/smp_spin_table.c b/arch/arm64/kernel/smp_spin_table.c
new file mode 100644
index 000000000000..7c35fa682f76
--- /dev/null
+++ b/arch/arm64/kernel/smp_spin_table.c
@@ -0,0 +1,66 @@
1/*
2 * Spin Table SMP initialisation
3 *
4 * Copyright (C) 2013 ARM Ltd.
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License version 2 as
8 * published by the Free Software Foundation.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
17 */
18
19#include <linux/init.h>
20#include <linux/of.h>
21#include <linux/smp.h>
22
23#include <asm/cacheflush.h>
24
25static phys_addr_t cpu_release_addr[NR_CPUS];
26
27static int __init smp_spin_table_init_cpu(struct device_node *dn, int cpu)
28{
29 /*
30 * Determine the address from which the CPU is polling.
31 */
32 if (of_property_read_u64(dn, "cpu-release-addr",
33 &cpu_release_addr[cpu])) {
34 pr_err("CPU %d: missing or invalid cpu-release-addr property\n",
35 cpu);
36
37 return -1;
38 }
39
40 return 0;
41}
42
43static int __init smp_spin_table_prepare_cpu(int cpu)
44{
45 void **release_addr;
46
47 if (!cpu_release_addr[cpu])
48 return -ENODEV;
49
50 release_addr = __va(cpu_release_addr[cpu]);
51 release_addr[0] = (void *)__pa(secondary_holding_pen);
52 __flush_dcache_area(release_addr, sizeof(release_addr[0]));
53
54 /*
55 * Send an event to wake up the secondary CPU.
56 */
57 sev();
58
59 return 0;
60}
61
62const struct smp_enable_ops smp_spin_table_ops __initconst = {
63 .name = "spin-table",
64 .init_cpu = smp_spin_table_init_cpu,
65 .prepare_cpu = smp_spin_table_prepare_cpu,
66};