summaryrefslogtreecommitdiffstats
path: root/arch/arm64/kernel
diff options
context:
space:
mode:
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/entry.S5
-rw-r--r--arch/arm64/kernel/head.S12
-rw-r--r--arch/arm64/kernel/perf_event.c37
-rw-r--r--arch/arm64/kernel/process.c17
-rw-r--r--arch/arm64/kernel/psci.c211
-rw-r--r--arch/arm64/kernel/setup.c11
-rw-r--r--arch/arm64/kernel/signal.c17
-rw-r--r--arch/arm64/kernel/signal32.c224
-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
-rw-r--r--arch/arm64/kernel/sys.c1
-rw-r--r--arch/arm64/kernel/sys32.S5
-rw-r--r--arch/arm64/kernel/time.c29
-rw-r--r--arch/arm64/kernel/traps.c2
17 files changed, 596 insertions, 285 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/early_printk.c b/arch/arm64/kernel/early_printk.c
new file mode 100644
index 00000000000..7e320a2edb9
--- /dev/null
+++ b/arch/arm64/kernel/early_printk.c
@@ -0,0 +1,118 @@
+/*
+ * 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>
+
+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)
+ ;
+}
+
+struct earlycon_match {
+ const char *name;
+ void (*printch)(char ch);
+};
+
+static const struct earlycon_match earlycon_match[] __initconst = {
+ { .name = "pl011", .printch = pl011_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..514d6098dbe 100644
--- a/arch/arm64/kernel/entry.S
+++ b/arch/arm64/kernel/entry.S
@@ -677,10 +677,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..0a0a4975682 100644
--- a/arch/arm64/kernel/head.S
+++ b/arch/arm64/kernel/head.S
@@ -82,10 +82,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
/*
@@ -368,6 +366,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 +419,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/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..0337cdb0667 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)
{
@@ -97,14 +98,9 @@ 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.
+ * The idle thread.
+ * We always respect 'hlt_counter' to prevent low power idle.
*/
void cpu_idle(void)
{
@@ -122,10 +118,10 @@ void cpu_idle(void)
local_irq_disable();
if (!need_resched()) {
stop_critical_timings();
- pm_idle();
+ default_idle();
start_critical_timings();
/*
- * pm_idle functions should always return
+ * default_idle functions should always return
* with IRQs enabled.
*/
WARN_ON(irqs_disabled());
@@ -319,6 +315,7 @@ struct task_struct *__switch_to(struct task_struct *prev,
/* the actual thread switch */
last = cpu_switch_to(prev, next);
+ contextidr_thread_switch(next);
return last;
}
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..113db863f83 100644
--- a/arch/arm64/kernel/setup.c
+++ b/arch/arm64/kernel/setup.c
@@ -39,6 +39,7 @@
#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>
@@ -49,6 +50,7 @@
#include <asm/tlbflush.h>
#include <asm/traps.h>
#include <asm/memblock.h>
+#include <asm/psci.h>
unsigned int processor_id;
EXPORT_SYMBOL(processor_id);
@@ -260,6 +262,8 @@ void __init setup_arch(char **cmdline_p)
unflatten_device_tree();
+ psci_init();
+
#ifdef CONFIG_SMP
smp_init_cpus();
#endif
@@ -289,6 +293,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..7f4f3673f2b 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(&current->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];
@@ -703,13 +560,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 +595,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..bdd34597254 100644
--- a/arch/arm64/kernel/smp.c
+++ b/arch/arm64/kernel/smp.c
@@ -233,7 +233,28 @@ 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[0];
+
+ while (ops) {
+ if (!strcmp(name, ops->name))
+ return ops;
+
+ ops++;
+ }
+
+ return NULL;
+}
/*
* Enumerate the possible CPU set from the device tree.
@@ -252,22 +273,22 @@ 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("CPU %d: missing enable-method property\n", cpu);
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("CPU %d: invalid enable-method property: %s\n",
+ cpu, enable_method);
goto next;
}
+ if (smp_enable_ops[cpu]->init_cpu(dn, cpu))
+ goto next;
+
set_cpu_possible(cpu, true);
next:
cpu++;
@@ -281,8 +302,7 @@ next:
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 +311,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..112091684c2
--- /dev/null
+++ b/arch/arm64/kernel/smp_psci.c
@@ -0,0 +1,52 @@
+/*
+ * 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>
+
+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, __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..6abb0572161 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
diff --git a/arch/arm64/kernel/time.c b/arch/arm64/kernel/time.c
index 3b4b7258f49..b0ef18d14c3 100644
--- a/arch/arm64/kernel/time.c
+++ b/arch/arm64/kernel/time.c
@@ -31,8 +31,9 @@
#include <linux/syscore_ops.h>
#include <linux/timer.h>
#include <linux/irq.h>
+#include <linux/delay.h>
-#include <clocksource/arm_generic.h>
+#include <clocksource/arm_arch_timer.h>
#include <asm/thread_info.h>
#include <asm/stacktrace.h>
@@ -59,7 +60,31 @@ 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;
+
+ if (arch_timer_init())
+ panic("Unable to initialise architected timer.\n");
+
+ arch_timer_rate = arch_timer_get_rate();
+
+ /* 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..b3c5f628bdb 100644
--- a/arch/arm64/kernel/traps.c
+++ b/arch/arm64/kernel/traps.c
@@ -242,7 +242,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();