summaryrefslogtreecommitdiff
path: root/arch/mips/kernel
diff options
context:
space:
mode:
Diffstat (limited to 'arch/mips/kernel')
-rw-r--r--arch/mips/kernel/binfmt_elfn32.c8
-rw-r--r--arch/mips/kernel/binfmt_elfo32.c8
-rw-r--r--arch/mips/kernel/branch.c36
-rw-r--r--arch/mips/kernel/kprobes.c67
-rw-r--r--arch/mips/kernel/linux32.c1
-rw-r--r--arch/mips/kernel/mips-cpc.c17
-rw-r--r--arch/mips/kernel/mips-r2-to-r6-emul.c1
-rw-r--r--arch/mips/kernel/module.c1
-rw-r--r--arch/mips/kernel/pm-cps.c160
-rw-r--r--arch/mips/kernel/probes-common.h83
-rw-r--r--arch/mips/kernel/proc.c7
-rw-r--r--arch/mips/kernel/smp-gic.c66
-rw-r--r--arch/mips/kernel/smp-mt.c23
-rw-r--r--arch/mips/kernel/smp.c65
-rw-r--r--arch/mips/kernel/traps.c53
-rw-r--r--arch/mips/kernel/uprobes.c88
16 files changed, 345 insertions, 339 deletions
diff --git a/arch/mips/kernel/binfmt_elfn32.c b/arch/mips/kernel/binfmt_elfn32.c
index 58ad63d7eb42..9c7f3e136d50 100644
--- a/arch/mips/kernel/binfmt_elfn32.c
+++ b/arch/mips/kernel/binfmt_elfn32.c
@@ -1,5 +1,6 @@
/*
* Support for n32 Linux/MIPS ELF binaries.
+ * Author: Ralf Baechle (ralf@linux-mips.org)
*
* Copyright (C) 1999, 2001 Ralf Baechle
* Copyright (C) 1999, 2001 Silicon Graphics, Inc.
@@ -37,7 +38,6 @@ typedef elf_fpreg_t elf_fpregset_t[ELF_NFPREG];
#define ELF_ET_DYN_BASE (TASK32_SIZE / 3 * 2)
#include <asm/processor.h>
-#include <linux/module.h>
#include <linux/elfcore.h>
#include <linux/compat.h>
#include <linux/math64.h>
@@ -96,12 +96,6 @@ jiffies_to_compat_timeval(unsigned long jiffies, struct compat_timeval *value)
#define ELF_CORE_EFLAGS EF_MIPS_ABI2
-MODULE_DESCRIPTION("Binary format loader for compatibility with n32 Linux/MIPS binaries");
-MODULE_AUTHOR("Ralf Baechle (ralf@linux-mips.org)");
-
-#undef MODULE_DESCRIPTION
-#undef MODULE_AUTHOR
-
#undef TASK_SIZE
#define TASK_SIZE TASK_SIZE32
diff --git a/arch/mips/kernel/binfmt_elfo32.c b/arch/mips/kernel/binfmt_elfo32.c
index 49fb881481f7..1ab34322dd97 100644
--- a/arch/mips/kernel/binfmt_elfo32.c
+++ b/arch/mips/kernel/binfmt_elfo32.c
@@ -1,5 +1,6 @@
/*
* Support for o32 Linux/MIPS ELF binaries.
+ * Author: Ralf Baechle (ralf@linux-mips.org)
*
* Copyright (C) 1999, 2001 Ralf Baechle
* Copyright (C) 1999, 2001 Silicon Graphics, Inc.
@@ -42,7 +43,6 @@ typedef elf_fpreg_t elf_fpregset_t[ELF_NFPREG];
#include <asm/processor.h>
-#include <linux/module.h>
#include <linux/elfcore.h>
#include <linux/compat.h>
#include <linux/math64.h>
@@ -99,12 +99,6 @@ jiffies_to_compat_timeval(unsigned long jiffies, struct compat_timeval *value)
value->tv_usec = rem / NSEC_PER_USEC;
}
-MODULE_DESCRIPTION("Binary format loader for compatibility with o32 Linux/MIPS binaries");
-MODULE_AUTHOR("Ralf Baechle (ralf@linux-mips.org)");
-
-#undef MODULE_DESCRIPTION
-#undef MODULE_AUTHOR
-
#undef TASK_SIZE
#define TASK_SIZE TASK_SIZE32
diff --git a/arch/mips/kernel/branch.c b/arch/mips/kernel/branch.c
index 46c227fc98f5..12c718181e5e 100644
--- a/arch/mips/kernel/branch.c
+++ b/arch/mips/kernel/branch.c
@@ -9,7 +9,7 @@
#include <linux/kernel.h>
#include <linux/sched.h>
#include <linux/signal.h>
-#include <linux/module.h>
+#include <linux/export.h>
#include <asm/branch.h>
#include <asm/cpu.h>
#include <asm/cpu-features.h>
@@ -866,3 +866,37 @@ unaligned:
force_sig(SIGBUS, current);
return -EFAULT;
}
+
+#if (defined CONFIG_KPROBES) || (defined CONFIG_UPROBES)
+
+int __insn_is_compact_branch(union mips_instruction insn)
+{
+ if (!cpu_has_mips_r6)
+ return 0;
+
+ switch (insn.i_format.opcode) {
+ case blezl_op:
+ case bgtzl_op:
+ case blez_op:
+ case bgtz_op:
+ /*
+ * blez[l] and bgtz[l] opcodes with non-zero rt
+ * are MIPS R6 compact branches
+ */
+ if (insn.i_format.rt)
+ return 1;
+ break;
+ case bc6_op:
+ case balc6_op:
+ case pop10_op:
+ case pop30_op:
+ case pop66_op:
+ case pop76_op:
+ return 1;
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(__insn_is_compact_branch);
+
+#endif /* CONFIG_KPROBES || CONFIG_UPROBES */
diff --git a/arch/mips/kernel/kprobes.c b/arch/mips/kernel/kprobes.c
index 212f46f2014e..f5c8bce70db2 100644
--- a/arch/mips/kernel/kprobes.c
+++ b/arch/mips/kernel/kprobes.c
@@ -32,7 +32,8 @@
#include <asm/ptrace.h>
#include <asm/branch.h>
#include <asm/break.h>
-#include <asm/inst.h>
+
+#include "probes-common.h"
static const union mips_instruction breakpoint_insn = {
.b_format = {
@@ -55,63 +56,7 @@ DEFINE_PER_CPU(struct kprobe_ctlblk, kprobe_ctlblk);
static int __kprobes insn_has_delayslot(union mips_instruction insn)
{
- switch (insn.i_format.opcode) {
-
- /*
- * This group contains:
- * jr and jalr are in r_format format.
- */
- case spec_op:
- switch (insn.r_format.func) {
- case jr_op:
- case jalr_op:
- break;
- default:
- goto insn_ok;
- }
-
- /*
- * This group contains:
- * bltz_op, bgez_op, bltzl_op, bgezl_op,
- * bltzal_op, bgezal_op, bltzall_op, bgezall_op.
- */
- case bcond_op:
-
- /*
- * These are unconditional and in j_format.
- */
- case jal_op:
- case j_op:
-
- /*
- * These are conditional and in i_format.
- */
- case beq_op:
- case beql_op:
- case bne_op:
- case bnel_op:
- case blez_op:
- case blezl_op:
- case bgtz_op:
- case bgtzl_op:
-
- /*
- * These are the FPA/cp1 branch instructions.
- */
- case cop1_op:
-
-#ifdef CONFIG_CPU_CAVIUM_OCTEON
- case lwc2_op: /* This is bbit0 on Octeon */
- case ldc2_op: /* This is bbit032 on Octeon */
- case swc2_op: /* This is bbit1 on Octeon */
- case sdc2_op: /* This is bbit132 on Octeon */
-#endif
- return 1;
- default:
- break;
- }
-insn_ok:
- return 0;
+ return __insn_has_delay_slot(insn);
}
/*
@@ -161,6 +106,12 @@ int __kprobes arch_prepare_kprobe(struct kprobe *p)
goto out;
}
+ if (__insn_is_compact_branch(insn)) {
+ pr_notice("Kprobes for compact branches are not supported\n");
+ ret = -EINVAL;
+ goto out;
+ }
+
/* insn: must be on special executable page on mips. */
p->ainsn.insn = get_insn_slot();
if (!p->ainsn.insn) {
diff --git a/arch/mips/kernel/linux32.c b/arch/mips/kernel/linux32.c
index 0b29646bcee7..50fb62544df7 100644
--- a/arch/mips/kernel/linux32.c
+++ b/arch/mips/kernel/linux32.c
@@ -26,7 +26,6 @@
#include <linux/utsname.h>
#include <linux/personality.h>
#include <linux/dnotify.h>
-#include <linux/module.h>
#include <linux/binfmts.h>
#include <linux/security.h>
#include <linux/compat.h>
diff --git a/arch/mips/kernel/mips-cpc.c b/arch/mips/kernel/mips-cpc.c
index 566b8d2c092c..2a45867d3b4f 100644
--- a/arch/mips/kernel/mips-cpc.c
+++ b/arch/mips/kernel/mips-cpc.c
@@ -52,7 +52,7 @@ static phys_addr_t mips_cpc_phys_base(void)
int mips_cpc_probe(void)
{
phys_addr_t addr;
- unsigned cpu;
+ unsigned int cpu;
for_each_possible_cpu(cpu)
spin_lock_init(&per_cpu(cpc_core_lock, cpu));
@@ -70,7 +70,12 @@ int mips_cpc_probe(void)
void mips_cpc_lock_other(unsigned int core)
{
- unsigned curr_core;
+ unsigned int curr_core;
+
+ if (mips_cm_revision() >= CM_REV_CM3)
+ /* Systems with CM >= 3 lock the CPC via mips_cm_lock_other */
+ return;
+
preempt_disable();
curr_core = current_cpu_data.core;
spin_lock_irqsave(&per_cpu(cpc_core_lock, curr_core),
@@ -86,7 +91,13 @@ void mips_cpc_lock_other(unsigned int core)
void mips_cpc_unlock_other(void)
{
- unsigned curr_core = current_cpu_data.core;
+ unsigned int curr_core;
+
+ if (mips_cm_revision() >= CM_REV_CM3)
+ /* Systems with CM >= 3 lock the CPC via mips_cm_lock_other */
+ return;
+
+ curr_core = current_cpu_data.core;
spin_unlock_irqrestore(&per_cpu(cpc_core_lock, curr_core),
per_cpu(cpc_core_lock_flags, curr_core));
preempt_enable();
diff --git a/arch/mips/kernel/mips-r2-to-r6-emul.c b/arch/mips/kernel/mips-r2-to-r6-emul.c
index 0a7e10b5f9e3..22dedd62818a 100644
--- a/arch/mips/kernel/mips-r2-to-r6-emul.c
+++ b/arch/mips/kernel/mips-r2-to-r6-emul.c
@@ -15,7 +15,6 @@
#include <linux/debugfs.h>
#include <linux/init.h>
#include <linux/kernel.h>
-#include <linux/module.h>
#include <linux/ptrace.h>
#include <linux/seq_file.h>
diff --git a/arch/mips/kernel/module.c b/arch/mips/kernel/module.c
index 79850e376ef6..94627a3a6a0d 100644
--- a/arch/mips/kernel/module.c
+++ b/arch/mips/kernel/module.c
@@ -20,6 +20,7 @@
#undef DEBUG
+#include <linux/extable.h>
#include <linux/moduleloader.h>
#include <linux/elf.h>
#include <linux/mm.h>
diff --git a/arch/mips/kernel/pm-cps.c b/arch/mips/kernel/pm-cps.c
index 5b31a9405ebc..7cf653e21423 100644
--- a/arch/mips/kernel/pm-cps.c
+++ b/arch/mips/kernel/pm-cps.c
@@ -8,6 +8,7 @@
* option) any later version.
*/
+#include <linux/cpuhotplug.h>
#include <linux/init.h>
#include <linux/percpu.h>
#include <linux/slab.h>
@@ -70,13 +71,8 @@ static DEFINE_PER_CPU_ALIGNED(atomic_t, pm_barrier);
DEFINE_PER_CPU_ALIGNED(struct mips_static_suspend_state, cps_cpu_state);
/* A somewhat arbitrary number of labels & relocs for uasm */
-static struct uasm_label labels[32] __initdata;
-static struct uasm_reloc relocs[32] __initdata;
-
-/* CPU dependant sync types */
-static unsigned stype_intervention;
-static unsigned stype_memory;
-static unsigned stype_ordering;
+static struct uasm_label labels[32];
+static struct uasm_reloc relocs[32];
enum mips_reg {
zero, at, v0, v1, a0, a1, a2, a3,
@@ -134,7 +130,7 @@ int cps_pm_enter_state(enum cps_pm_state state)
return -EINVAL;
/* Calculate which coupled CPUs (VPEs) are online */
-#ifdef CONFIG_MIPS_MT
+#if defined(CONFIG_MIPS_MT) || defined(CONFIG_CPU_MIPSR6)
if (cpu_online(cpu)) {
cpumask_and(coupled_mask, cpu_online_mask,
&cpu_sibling_map[cpu]);
@@ -198,10 +194,10 @@ int cps_pm_enter_state(enum cps_pm_state state)
return 0;
}
-static void __init cps_gen_cache_routine(u32 **pp, struct uasm_label **pl,
- struct uasm_reloc **pr,
- const struct cache_desc *cache,
- unsigned op, int lbl)
+static void cps_gen_cache_routine(u32 **pp, struct uasm_label **pl,
+ struct uasm_reloc **pr,
+ const struct cache_desc *cache,
+ unsigned op, int lbl)
{
unsigned cache_size = cache->ways << cache->waybit;
unsigned i;
@@ -242,10 +238,10 @@ static void __init cps_gen_cache_routine(u32 **pp, struct uasm_label **pl,
uasm_i_nop(pp);
}
-static int __init cps_gen_flush_fsb(u32 **pp, struct uasm_label **pl,
- struct uasm_reloc **pr,
- const struct cpuinfo_mips *cpu_info,
- int lbl)
+static int cps_gen_flush_fsb(u32 **pp, struct uasm_label **pl,
+ struct uasm_reloc **pr,
+ const struct cpuinfo_mips *cpu_info,
+ int lbl)
{
unsigned i, fsb_size = 8;
unsigned num_loads = (fsb_size * 3) / 2;
@@ -272,14 +268,9 @@ static int __init cps_gen_flush_fsb(u32 **pp, struct uasm_label **pl,
/* On older ones it's unavailable */
return -1;
- /* CPUs which do not require the workaround */
- case CPU_P5600:
- case CPU_I6400:
- return 0;
-
default:
- WARN_ONCE(1, "pm-cps: FSB flush unsupported for this CPU\n");
- return -1;
+ /* Assume that the CPU does not need this workaround */
+ return 0;
}
/*
@@ -320,8 +311,8 @@ static int __init cps_gen_flush_fsb(u32 **pp, struct uasm_label **pl,
i * line_size * line_stride, t0);
}
- /* Completion barrier */
- uasm_i_sync(pp, stype_memory);
+ /* Barrier ensuring previous cache invalidates are complete */
+ uasm_i_sync(pp, STYPE_SYNC);
uasm_i_ehb(pp);
/* Check whether the pipeline stalled due to the FSB being full */
@@ -340,9 +331,9 @@ static int __init cps_gen_flush_fsb(u32 **pp, struct uasm_label **pl,
return 0;
}
-static void __init cps_gen_set_top_bit(u32 **pp, struct uasm_label **pl,
- struct uasm_reloc **pr,
- unsigned r_addr, int lbl)
+static void cps_gen_set_top_bit(u32 **pp, struct uasm_label **pl,
+ struct uasm_reloc **pr,
+ unsigned r_addr, int lbl)
{
uasm_i_lui(pp, t0, uasm_rel_hi(0x80000000));
uasm_build_label(pl, *pp, lbl);
@@ -353,7 +344,7 @@ static void __init cps_gen_set_top_bit(u32 **pp, struct uasm_label **pl,
uasm_i_nop(pp);
}
-static void * __init cps_gen_entry_code(unsigned cpu, enum cps_pm_state state)
+static void *cps_gen_entry_code(unsigned cpu, enum cps_pm_state state)
{
struct uasm_label *l = labels;
struct uasm_reloc *r = relocs;
@@ -411,7 +402,7 @@ static void * __init cps_gen_entry_code(unsigned cpu, enum cps_pm_state state)
if (coupled_coherence) {
/* Increment ready_count */
- uasm_i_sync(&p, stype_ordering);
+ uasm_i_sync(&p, STYPE_SYNC_MB);
uasm_build_label(&l, p, lbl_incready);
uasm_i_ll(&p, t1, 0, r_nc_count);
uasm_i_addiu(&p, t2, t1, 1);
@@ -419,8 +410,8 @@ static void * __init cps_gen_entry_code(unsigned cpu, enum cps_pm_state state)
uasm_il_beqz(&p, &r, t2, lbl_incready);
uasm_i_addiu(&p, t1, t1, 1);
- /* Ordering barrier */
- uasm_i_sync(&p, stype_ordering);
+ /* Barrier ensuring all CPUs see the updated r_nc_count value */
+ uasm_i_sync(&p, STYPE_SYNC_MB);
/*
* If this is the last VPE to become ready for non-coherence
@@ -441,7 +432,8 @@ static void * __init cps_gen_entry_code(unsigned cpu, enum cps_pm_state state)
uasm_i_lw(&p, t0, 0, r_nc_count);
uasm_il_bltz(&p, &r, t0, lbl_secondary_cont);
uasm_i_ehb(&p);
- uasm_i_yield(&p, zero, t1);
+ if (cpu_has_mipsmt)
+ uasm_i_yield(&p, zero, t1);
uasm_il_b(&p, &r, lbl_poll_cont);
uasm_i_nop(&p);
} else {
@@ -449,8 +441,21 @@ static void * __init cps_gen_entry_code(unsigned cpu, enum cps_pm_state state)
* The core will lose power & this VPE will not continue
* so it can simply halt here.
*/
- uasm_i_addiu(&p, t0, zero, TCHALT_H);
- uasm_i_mtc0(&p, t0, 2, 4);
+ if (cpu_has_mipsmt) {
+ /* Halt the VPE via C0 tchalt register */
+ uasm_i_addiu(&p, t0, zero, TCHALT_H);
+ uasm_i_mtc0(&p, t0, 2, 4);
+ } else if (cpu_has_vp) {
+ /* Halt the VP via the CPC VP_STOP register */
+ unsigned int vpe_id;
+
+ vpe_id = cpu_vpe_id(&cpu_data[cpu]);
+ uasm_i_addiu(&p, t0, zero, 1 << vpe_id);
+ UASM_i_LA(&p, t1, (long)addr_cpc_cl_vp_stop());
+ uasm_i_sw(&p, t0, 0, t1);
+ } else {
+ BUG();
+ }
uasm_build_label(&l, p, lbl_secondary_hang);
uasm_il_b(&p, &r, lbl_secondary_hang);
uasm_i_nop(&p);
@@ -472,22 +477,24 @@ static void * __init cps_gen_entry_code(unsigned cpu, enum cps_pm_state state)
cps_gen_cache_routine(&p, &l, &r, &cpu_data[cpu].dcache,
Index_Writeback_Inv_D, lbl_flushdcache);
- /* Completion barrier */
- uasm_i_sync(&p, stype_memory);
+ /* Barrier ensuring previous cache invalidates are complete */
+ uasm_i_sync(&p, STYPE_SYNC);
uasm_i_ehb(&p);
- /*
- * Disable all but self interventions. The load from COHCTL is defined
- * by the interAptiv & proAptiv SUMs as ensuring that the operation
- * resulting from the preceding store is complete.
- */
- uasm_i_addiu(&p, t0, zero, 1 << cpu_data[cpu].core);
- uasm_i_sw(&p, t0, 0, r_pcohctl);
- uasm_i_lw(&p, t0, 0, r_pcohctl);
-
- /* Sync to ensure previous interventions are complete */
- uasm_i_sync(&p, stype_intervention);
- uasm_i_ehb(&p);
+ if (mips_cm_revision() < CM_REV_CM3) {
+ /*
+ * Disable all but self interventions. The load from COHCTL is
+ * defined by the interAptiv & proAptiv SUMs as ensuring that the
+ * operation resulting from the preceding store is complete.
+ */
+ uasm_i_addiu(&p, t0, zero, 1 << cpu_data[cpu].core);
+ uasm_i_sw(&p, t0, 0, r_pcohctl);
+ uasm_i_lw(&p, t0, 0, r_pcohctl);
+
+ /* Barrier to ensure write to coherence control is complete */
+ uasm_i_sync(&p, STYPE_SYNC);
+ uasm_i_ehb(&p);
+ }
/* Disable coherence */
uasm_i_sw(&p, zero, 0, r_pcohctl);
@@ -531,8 +538,8 @@ static void * __init cps_gen_entry_code(unsigned cpu, enum cps_pm_state state)
goto gen_done;
}
- /* Completion barrier */
- uasm_i_sync(&p, stype_memory);
+ /* Barrier to ensure write to CPC command is complete */
+ uasm_i_sync(&p, STYPE_SYNC);
uasm_i_ehb(&p);
}
@@ -562,26 +569,29 @@ static void * __init cps_gen_entry_code(unsigned cpu, enum cps_pm_state state)
* will run this. The first will actually re-enable coherence & the
* rest will just be performing a rather unusual nop.
*/
- uasm_i_addiu(&p, t0, zero, CM_GCR_Cx_COHERENCE_COHDOMAINEN_MSK);
+ uasm_i_addiu(&p, t0, zero, mips_cm_revision() < CM_REV_CM3
+ ? CM_GCR_Cx_COHERENCE_COHDOMAINEN_MSK
+ : CM3_GCR_Cx_COHERENCE_COHEN_MSK);
+
uasm_i_sw(&p, t0, 0, r_pcohctl);
uasm_i_lw(&p, t0, 0, r_pcohctl);
- /* Completion barrier */
- uasm_i_sync(&p, stype_memory);
+ /* Barrier to ensure write to coherence control is complete */
+ uasm_i_sync(&p, STYPE_SYNC);
uasm_i_ehb(&p);
if (coupled_coherence && (state == CPS_PM_NC_WAIT)) {
/* Decrement ready_count */
uasm_build_label(&l, p, lbl_decready);
- uasm_i_sync(&p, stype_ordering);
+ uasm_i_sync(&p, STYPE_SYNC_MB);
uasm_i_ll(&p, t1, 0, r_nc_count);
uasm_i_addiu(&p, t2, t1, -1);
uasm_i_sc(&p, t2, 0, r_nc_count);
uasm_il_beqz(&p, &r, t2, lbl_decready);
uasm_i_andi(&p, v0, t1, (1 << fls(smp_num_siblings)) - 1);
- /* Ordering barrier */
- uasm_i_sync(&p, stype_ordering);
+ /* Barrier ensuring all CPUs see the updated r_nc_count value */
+ uasm_i_sync(&p, STYPE_SYNC_MB);
}
if (coupled_coherence && (state == CPS_PM_CLOCK_GATED)) {
@@ -602,8 +612,8 @@ static void * __init cps_gen_entry_code(unsigned cpu, enum cps_pm_state state)
*/
uasm_build_label(&l, p, lbl_secondary_cont);
- /* Ordering barrier */
- uasm_i_sync(&p, stype_ordering);
+ /* Barrier ensuring all CPUs see the updated r_nc_count value */
+ uasm_i_sync(&p, STYPE_SYNC_MB);
}
/* The core is coherent, time to return to C code */
@@ -628,7 +638,7 @@ out_err:
return NULL;
}
-static int __init cps_gen_core_entries(unsigned cpu)
+static int cps_pm_online_cpu(unsigned int cpu)
{
enum cps_pm_state state;
unsigned core = cpu_data[cpu].core;
@@ -670,29 +680,10 @@ static int __init cps_gen_core_entries(unsigned cpu)
static int __init cps_pm_init(void)
{
- unsigned cpu;
- int err;
-
- /* Detect appropriate sync types for the system */
- switch (current_cpu_data.cputype) {
- case CPU_INTERAPTIV:
- case CPU_PROAPTIV:
- case CPU_M5150:
- case CPU_P5600:
- case CPU_I6400:
- stype_intervention = 0x2;
- stype_memory = 0x3;
- stype_ordering = 0x10;
- break;
-
- default:
- pr_warn("Power management is using heavyweight sync 0\n");
- }
-
/* A CM is required for all non-coherent states */
if (!mips_cm_present()) {
pr_warn("pm-cps: no CM, non-coherent states unavailable\n");
- goto out;
+ return 0;
}
/*
@@ -722,12 +713,7 @@ static int __init cps_pm_init(void)
pr_warn("pm-cps: no CPC, clock & power gating unavailable\n");
}
- for_each_present_cpu(cpu) {
- err = cps_gen_core_entries(cpu);
- if (err)
- return err;
- }
-out:
- return 0;
+ return cpuhp_setup_state(CPUHP_AP_ONLINE_DYN, "AP_PM_CPS_CPU_ONLINE",
+ cps_pm_online_cpu, NULL);
}
arch_initcall(cps_pm_init);
diff --git a/arch/mips/kernel/probes-common.h b/arch/mips/kernel/probes-common.h
new file mode 100644
index 000000000000..dd08e41134b6
--- /dev/null
+++ b/arch/mips/kernel/probes-common.h
@@ -0,0 +1,83 @@
+/*
+ * Copyright (C) 2016 Imagination Technologies
+ * Author: Marcin Nowakowski <marcin.nowakowski@imgtec.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ */
+
+#ifndef __PROBES_COMMON_H
+#define __PROBES_COMMON_H
+
+#include <asm/inst.h>
+
+int __insn_is_compact_branch(union mips_instruction insn);
+
+static inline int __insn_has_delay_slot(const union mips_instruction insn)
+{
+ switch (insn.i_format.opcode) {
+ /*
+ * jr and jalr are in r_format format.
+ */
+ case spec_op:
+ switch (insn.r_format.func) {
+ case jalr_op:
+ case jr_op:
+ return 1;
+ }
+ break;
+
+ /*
+ * This group contains:
+ * bltz_op, bgez_op, bltzl_op, bgezl_op,
+ * bltzal_op, bgezal_op, bltzall_op, bgezall_op.
+ */
+ case bcond_op:
+ switch (insn.i_format.rt) {
+ case bltz_op:
+ case bltzl_op:
+ case bgez_op:
+ case bgezl_op:
+ case bltzal_op:
+ case bltzall_op:
+ case bgezal_op:
+ case bgezall_op:
+ case bposge32_op:
+ return 1;
+ }
+ break;
+
+ /*
+ * These are unconditional and in j_format.
+ */
+ case jal_op:
+ case j_op:
+ case beq_op:
+ case beql_op:
+ case bne_op:
+ case bnel_op:
+ case blez_op: /* not really i_format */
+ case blezl_op:
+ case bgtz_op:
+ case bgtzl_op:
+ return 1;
+
+ /*
+ * And now the FPA/cp1 branch instructions.
+ */
+ case cop1_op:
+#ifdef CONFIG_CPU_CAVIUM_OCTEON
+ case lwc2_op: /* This is bbit0 on Octeon */
+ case ldc2_op: /* This is bbit032 on Octeon */
+ case swc2_op: /* This is bbit1 on Octeon */
+ case sdc2_op: /* This is bbit132 on Octeon */
+#endif
+ return 1;
+ }
+
+ return 0;
+}
+
+#endif /* __PROBES_COMMON_H */
diff --git a/arch/mips/kernel/proc.c b/arch/mips/kernel/proc.c
index 97dc01b03631..4eff2aed7360 100644
--- a/arch/mips/kernel/proc.c
+++ b/arch/mips/kernel/proc.c
@@ -135,6 +135,13 @@ static int show_cpuinfo(struct seq_file *m, void *v)
seq_printf(m, "package\t\t\t: %d\n", cpu_data[n].package);
seq_printf(m, "core\t\t\t: %d\n", cpu_data[n].core);
+#if defined(CONFIG_MIPS_MT_SMP) || defined(CONFIG_CPU_MIPSR6)
+ if (cpu_has_mipsmt)
+ seq_printf(m, "VPE\t\t\t: %d\n", cpu_data[n].vpe_id);
+ else if (cpu_has_vp)
+ seq_printf(m, "VP\t\t\t: %d\n", cpu_data[n].vpe_id);
+#endif
+
sprintf(fmt, "VCE%%c exceptions\t\t: %s\n",
cpu_has_vce ? "%u" : "not available");
seq_printf(m, fmt, 'D', vced_count);
diff --git a/arch/mips/kernel/smp-gic.c b/arch/mips/kernel/smp-gic.c
deleted file mode 100644
index 9b63829cf929..000000000000
--- a/arch/mips/kernel/smp-gic.c
+++ /dev/null
@@ -1,66 +0,0 @@
-/*
- * Copyright (C) 2013 Imagination Technologies
- * Author: Paul Burton <paul.burton@imgtec.com>
- *
- * Based on smp-cmp.c:
- * Copyright (C) 2007 MIPS Technologies, Inc.
- * Author: Chris Dearman (chris@mips.com)
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the
- * Free Software Foundation; either version 2 of the License, or (at your
- * option) any later version.
- */
-
-#include <linux/irqchip/mips-gic.h>
-#include <linux/printk.h>
-
-#include <asm/mips-cpc.h>
-#include <asm/smp-ops.h>
-
-void gic_send_ipi_single(int cpu, unsigned int action)
-{
- unsigned long flags;
- unsigned int intr;
- unsigned int core = cpu_data[cpu].core;
-
- pr_debug("CPU%d: %s cpu %d action %u status %08x\n",
- smp_processor_id(), __func__, cpu, action, read_c0_status());
-
- local_irq_save(flags);
-
- switch (action) {
- case SMP_CALL_FUNCTION:
- intr = plat_ipi_call_int_xlate(cpu);
- break;
-
- case SMP_RESCHEDULE_YOURSELF:
- intr = plat_ipi_resched_int_xlate(cpu);
- break;
-
- default:
- BUG();
- }
-
- gic_send_ipi(intr);
-
- if (mips_cpc_present() && (core != current_cpu_data.core)) {
- while (!cpumask_test_cpu(cpu, &cpu_coherent_mask)) {
- mips_cm_lock_other(core, 0);
- mips_cpc_lock_other(core);
- write_cpc_co_cmd(CPC_Cx_CMD_PWRUP);
- mips_cpc_unlock_other();
- mips_cm_unlock_other();
- }
- }
-
- local_irq_restore(flags);
-}
-
-void gic_send_ipi_mask(const struct cpumask *mask, unsigned int action)
-{
- unsigned int i;
-
- for_each_cpu(i, mask)
- gic_send_ipi_single(i, action);
-}
diff --git a/arch/mips/kernel/smp-mt.c b/arch/mips/kernel/smp-mt.c
index 4f9570a57e8d..e077ea3e11fb 100644
--- a/arch/mips/kernel/smp-mt.c
+++ b/arch/mips/kernel/smp-mt.c
@@ -289,26 +289,3 @@ struct plat_smp_ops vsmp_smp_ops = {
.prepare_cpus = vsmp_prepare_cpus,
};
-#ifdef CONFIG_PROC_FS
-static int proc_cpuinfo_chain_call(struct notifier_block *nfb,
- unsigned long action_unused, void *data)
-{
- struct proc_cpuinfo_notifier_args *pcn = data;
- struct seq_file *m = pcn->m;
- unsigned long n = pcn->n;
-
- if (!cpu_has_mipsmt)
- return NOTIFY_OK;
-
- seq_printf(m, "VPE\t\t\t: %d\n", cpu_data[n].vpe_id);
-
- return NOTIFY_OK;
-}
-
-static int __init proc_cpuinfo_notifier_init(void)
-{
- return proc_cpuinfo_notifier(proc_cpuinfo_chain_call, 0);
-}
-
-subsys_initcall(proc_cpuinfo_notifier_init);
-#endif
diff --git a/arch/mips/kernel/smp.c b/arch/mips/kernel/smp.c
index b0baf48951fa..7ebb1918e2ac 100644
--- a/arch/mips/kernel/smp.c
+++ b/arch/mips/kernel/smp.c
@@ -25,7 +25,7 @@
#include <linux/smp.h>
#include <linux/spinlock.h>
#include <linux/threads.h>
-#include <linux/module.h>
+#include <linux/export.h>
#include <linux/time.h>
#include <linux/timex.h>
#include <linux/sched.h>
@@ -192,9 +192,11 @@ void mips_smp_send_ipi_mask(const struct cpumask *mask, unsigned int action)
continue;
while (!cpumask_test_cpu(cpu, &cpu_coherent_mask)) {
+ mips_cm_lock_other(core, 0);
mips_cpc_lock_other(core);
write_cpc_co_cmd(CPC_Cx_CMD_PWRUP);
mips_cpc_unlock_other();
+ mips_cm_unlock_other();
}
}
}
@@ -229,7 +231,7 @@ static struct irqaction irq_call = {
.name = "IPI call"
};
-static __init void smp_ipi_init_one(unsigned int virq,
+static void smp_ipi_init_one(unsigned int virq,
struct irqaction *action)
{
int ret;
@@ -239,9 +241,11 @@ static __init void smp_ipi_init_one(unsigned int virq,
BUG_ON(ret);
}
-static int __init mips_smp_ipi_init(void)
+static unsigned int call_virq, sched_virq;
+
+int mips_smp_ipi_allocate(const struct cpumask *mask)
{
- unsigned int call_virq, sched_virq;
+ int virq;
struct irq_domain *ipidomain;
struct device_node *node;
@@ -268,16 +272,20 @@ static int __init mips_smp_ipi_init(void)
if (!ipidomain)
return 0;
- call_virq = irq_reserve_ipi(ipidomain, cpu_possible_mask);
- BUG_ON(!call_virq);
+ virq = irq_reserve_ipi(ipidomain, mask);
+ BUG_ON(!virq);
+ if (!call_virq)
+ call_virq = virq;
- sched_virq = irq_reserve_ipi(ipidomain, cpu_possible_mask);
- BUG_ON(!sched_virq);
+ virq = irq_reserve_ipi(ipidomain, mask);
+ BUG_ON(!virq);
+ if (!sched_virq)
+ sched_virq = virq;
if (irq_domain_is_ipi_per_cpu(ipidomain)) {
int cpu;
- for_each_cpu(cpu, cpu_possible_mask) {
+ for_each_cpu(cpu, mask) {
smp_ipi_init_one(call_virq + cpu, &irq_call);
smp_ipi_init_one(sched_virq + cpu, &irq_resched);
}
@@ -286,6 +294,45 @@ static int __init mips_smp_ipi_init(void)
smp_ipi_init_one(sched_virq, &irq_resched);
}
+ return 0;
+}
+
+int mips_smp_ipi_free(const struct cpumask *mask)
+{
+ struct irq_domain *ipidomain;
+ struct device_node *node;
+
+ node = of_irq_find_parent(of_root);
+ ipidomain = irq_find_matching_host(node, DOMAIN_BUS_IPI);
+
+ /*
+ * Some platforms have half DT setup. So if we found irq node but
+ * didn't find an ipidomain, try to search for one that is not in the
+ * DT.
+ */
+ if (node && !ipidomain)
+ ipidomain = irq_find_matching_host(NULL, DOMAIN_BUS_IPI);
+
+ BUG_ON(!ipidomain);
+
+ if (irq_domain_is_ipi_per_cpu(ipidomain)) {
+ int cpu;
+
+ for_each_cpu(cpu, mask) {
+ remove_irq(call_virq + cpu, &irq_call);
+ remove_irq(sched_virq + cpu, &irq_resched);
+ }
+ }
+ irq_destroy_ipi(call_virq, mask);
+ irq_destroy_ipi(sched_virq, mask);
+ return 0;
+}
+
+
+static int __init mips_smp_ipi_init(void)
+{
+ mips_smp_ipi_allocate(cpu_possible_mask);
+
call_desc = irq_to_desc(call_virq);
sched_desc = irq_to_desc(sched_virq);
diff --git a/arch/mips/kernel/traps.c b/arch/mips/kernel/traps.c
index 3de85be2486a..1f5fdee1dfc3 100644
--- a/arch/mips/kernel/traps.c
+++ b/arch/mips/kernel/traps.c
@@ -21,6 +21,7 @@
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/module.h>
+#include <linux/extable.h>
#include <linux/mm.h>
#include <linux/sched.h>
#include <linux/smp.h>
@@ -48,6 +49,7 @@
#include <asm/fpu.h>
#include <asm/fpu_emulator.h>
#include <asm/idle.h>
+#include <asm/mips-cm.h>
#include <asm/mips-r2-to-r6-emul.h>
#include <asm/mipsregs.h>
#include <asm/mipsmtregs.h>
@@ -444,6 +446,8 @@ asmlinkage void do_be(struct pt_regs *regs)
if (board_be_handler)
action = board_be_handler(regs, fixup != NULL);
+ else
+ mips_cm_error_report();
switch (action) {
case MIPS_BE_DISCARD:
@@ -2091,6 +2095,14 @@ static void configure_exception_vector(void)
{
if (cpu_has_veic || cpu_has_vint) {
unsigned long sr = set_c0_status(ST0_BEV);
+ /* If available, use WG to set top bits of EBASE */
+ if (cpu_has_ebase_wg) {
+#ifdef CONFIG_64BIT
+ write_c0_ebase_64(ebase | MIPS_EBASE_WG);
+#else
+ write_c0_ebase(ebase | MIPS_EBASE_WG);
+#endif
+ }
write_c0_ebase(ebase);
write_c0_status(sr);
/* Setting vector spacing enables EI/VI mode */
@@ -2127,8 +2139,17 @@ void per_cpu_trap_init(bool is_boot_cpu)
* We shouldn't trust a secondary core has a sane EBASE register
* so use the one calculated by the boot CPU.
*/
- if (!is_boot_cpu)
+ if (!is_boot_cpu) {
+ /* If available, use WG to set top bits of EBASE */
+ if (cpu_has_ebase_wg) {
+#ifdef CONFIG_64BIT
+ write_c0_ebase_64(ebase | MIPS_EBASE_WG);
+#else
+ write_c0_ebase(ebase | MIPS_EBASE_WG);
+#endif
+ }
write_c0_ebase(ebase);
+ }
cp0_compare_irq_shift = CAUSEB_TI - CAUSEB_IP;
cp0_compare_irq = (read_c0_intctl() >> INTCTLB_IPTI) & 7;
@@ -2209,13 +2230,39 @@ void __init trap_init(void)
if (cpu_has_veic || cpu_has_vint) {
unsigned long size = 0x200 + VECTORSPACING*64;
+ phys_addr_t ebase_pa;
+
ebase = (unsigned long)
__alloc_bootmem(size, 1 << fls(size), 0);
+
+ /*
+ * Try to ensure ebase resides in KSeg0 if possible.
+ *
+ * It shouldn't generally be in XKPhys on MIPS64 to avoid
+ * hitting a poorly defined exception base for Cache Errors.
+ * The allocation is likely to be in the low 512MB of physical,
+ * in which case we should be able to convert to KSeg0.
+ *
+ * EVA is special though as it allows segments to be rearranged
+ * and to become uncached during cache error handling.
+ */
+ ebase_pa = __pa(ebase);
+ if (!IS_ENABLED(CONFIG_EVA) && !WARN_ON(ebase_pa >= 0x20000000))
+ ebase = CKSEG0ADDR(ebase_pa);
} else {
ebase = CAC_BASE;
- if (cpu_has_mips_r2_r6)
- ebase += (read_c0_ebase() & 0x3ffff000);
+ if (cpu_has_mips_r2_r6) {
+ if (cpu_has_ebase_wg) {
+#ifdef CONFIG_64BIT
+ ebase = (read_c0_ebase_64() & ~0xfff);
+#else
+ ebase = (read_c0_ebase() & ~0xfff);
+#endif
+ } else {
+ ebase += (read_c0_ebase() & 0x3ffff000);
+ }
+ }
}
if (cpu_has_mmips) {
diff --git a/arch/mips/kernel/uprobes.c b/arch/mips/kernel/uprobes.c
index 4c7c1558944a..dbb917403131 100644
--- a/arch/mips/kernel/uprobes.c
+++ b/arch/mips/kernel/uprobes.c
@@ -8,71 +8,12 @@
#include <asm/branch.h>
#include <asm/cpu-features.h>
#include <asm/ptrace.h>
-#include <asm/inst.h>
+
+#include "probes-common.h"
static inline int insn_has_delay_slot(const union mips_instruction insn)
{
- switch (insn.i_format.opcode) {
- /*
- * jr and jalr are in r_format format.
- */
- case spec_op:
- switch (insn.r_format.func) {
- case jalr_op:
- case jr_op:
- return 1;
- }
- break;
-
- /*
- * This group contains:
- * bltz_op, bgez_op, bltzl_op, bgezl_op,
- * bltzal_op, bgezal_op, bltzall_op, bgezall_op.
- */
- case bcond_op:
- switch (insn.i_format.rt) {
- case bltz_op:
- case bltzl_op:
- case bgez_op:
- case bgezl_op:
- case bltzal_op:
- case bltzall_op:
- case bgezal_op:
- case bgezall_op:
- case bposge32_op:
- return 1;
- }
- break;
-
- /*
- * These are unconditional and in j_format.
- */
- case jal_op:
- case j_op:
- case beq_op:
- case beql_op:
- case bne_op:
- case bnel_op:
- case blez_op: /* not really i_format */
- case blezl_op:
- case bgtz_op:
- case bgtzl_op:
- return 1;
-
- /*
- * And now the FPA/cp1 branch instructions.
- */
- case cop1_op:
-#ifdef CONFIG_CPU_CAVIUM_OCTEON
- case lwc2_op: /* This is bbit0 on Octeon */
- case ldc2_op: /* This is bbit032 on Octeon */
- case swc2_op: /* This is bbit1 on Octeon */
- case sdc2_op: /* This is bbit132 on Octeon */
-#endif
- return 1;
- }
-
- return 0;
+ return __insn_has_delay_slot(insn);
}
/**
@@ -95,6 +36,12 @@ int arch_uprobe_analyze_insn(struct arch_uprobe *aup,
return -EINVAL;
inst.word = aup->insn[0];
+
+ if (__insn_is_compact_branch(inst)) {
+ pr_notice("Uprobes for compact branches are not supported\n");
+ return -EINVAL;
+ }
+
aup->ixol[0] = aup->insn[insn_has_delay_slot(inst)];
aup->ixol[1] = UPROBE_BRK_UPROBE_XOL; /* NOP */
@@ -282,19 +229,14 @@ int __weak set_swbp(struct arch_uprobe *auprobe, struct mm_struct *mm,
void __weak arch_uprobe_copy_ixol(struct page *page, unsigned long vaddr,
void *src, unsigned long len)
{
- void *kaddr;
+ unsigned long kaddr, kstart;
/* Initialize the slot */
- kaddr = kmap_atomic(page);
- memcpy(kaddr + (vaddr & ~PAGE_MASK), src, len);
- kunmap_atomic(kaddr);
-
- /*
- * The MIPS version of flush_icache_range will operate safely on
- * user space addresses and more importantly, it doesn't require a
- * VMA argument.
- */
- flush_icache_range(vaddr, vaddr + len);
+ kaddr = (unsigned long)kmap_atomic(page);
+ kstart = kaddr + (vaddr & ~PAGE_MASK);
+ memcpy((void *)kstart, src, len);
+ flush_icache_range(kstart, kstart + len);
+ kunmap_atomic((void *)kaddr);
}
/**