Merge branch 'mipsr6-for-3.20' of git://git.linux-mips.org/pub/scm/mchandras/linux into mips-for-linux-next
This commit is contained in:
@@ -52,7 +52,7 @@ obj-$(CONFIG_MIPS_MT_SMP) += smp-mt.o
|
||||
obj-$(CONFIG_MIPS_CMP) += smp-cmp.o
|
||||
obj-$(CONFIG_MIPS_CPS) += smp-cps.o cps-vec.o
|
||||
obj-$(CONFIG_MIPS_GIC_IPI) += smp-gic.o
|
||||
obj-$(CONFIG_CPU_MIPSR2) += spram.o
|
||||
obj-$(CONFIG_MIPS_SPRAM) += spram.o
|
||||
|
||||
obj-$(CONFIG_MIPS_VPE_LOADER) += vpe.o
|
||||
obj-$(CONFIG_MIPS_VPE_LOADER_CMP) += vpe-cmp.o
|
||||
@@ -90,6 +90,7 @@ obj-$(CONFIG_EARLY_PRINTK) += early_printk.o
|
||||
obj-$(CONFIG_EARLY_PRINTK_8250) += early_printk_8250.o
|
||||
obj-$(CONFIG_SPINLOCK_TEST) += spinlock_test.o
|
||||
obj-$(CONFIG_MIPS_MACHINE) += mips_machine.o
|
||||
obj-$(CONFIG_MIPSR2_TO_R6_EMULATOR) += mips-r2-to-r6-emul.o
|
||||
|
||||
CFLAGS_cpu-bugs64.o = $(shell if $(CC) $(KBUILD_CFLAGS) -Wa,-mdaddi -c -o /dev/null -x c /dev/null >/dev/null 2>&1; then echo "-DHAVE_AS_SET_DADDI"; fi)
|
||||
|
||||
|
@@ -97,6 +97,7 @@ void output_thread_info_defines(void)
|
||||
OFFSET(TI_TP_VALUE, thread_info, tp_value);
|
||||
OFFSET(TI_CPU, thread_info, cpu);
|
||||
OFFSET(TI_PRE_COUNT, thread_info, preempt_count);
|
||||
OFFSET(TI_R2_EMUL_RET, thread_info, r2_emul_return);
|
||||
OFFSET(TI_ADDR_LIMIT, thread_info, addr_limit);
|
||||
OFFSET(TI_RESTART_BLOCK, thread_info, restart_block);
|
||||
OFFSET(TI_REGS, thread_info, regs);
|
||||
|
@@ -16,6 +16,7 @@
|
||||
#include <asm/fpu.h>
|
||||
#include <asm/fpu_emulator.h>
|
||||
#include <asm/inst.h>
|
||||
#include <asm/mips-r2-to-r6-emul.h>
|
||||
#include <asm/ptrace.h>
|
||||
#include <asm/uaccess.h>
|
||||
|
||||
@@ -399,11 +400,21 @@ int __MIPS16e_compute_return_epc(struct pt_regs *regs)
|
||||
* @returns: -EFAULT on error and forces SIGBUS, and on success
|
||||
* returns 0 or BRANCH_LIKELY_TAKEN as appropriate after
|
||||
* evaluating the branch.
|
||||
*
|
||||
* MIPS R6 Compact branches and forbidden slots:
|
||||
* Compact branches do not throw exceptions because they do
|
||||
* not have delay slots. The forbidden slot instruction ($PC+4)
|
||||
* is only executed if the branch was not taken. Otherwise the
|
||||
* forbidden slot is skipped entirely. This means that the
|
||||
* only possible reason to be here because of a MIPS R6 compact
|
||||
* branch instruction is that the forbidden slot has thrown one.
|
||||
* In that case the branch was not taken, so the EPC can be safely
|
||||
* set to EPC + 8.
|
||||
*/
|
||||
int __compute_return_epc_for_insn(struct pt_regs *regs,
|
||||
union mips_instruction insn)
|
||||
{
|
||||
unsigned int bit, fcr31, dspcontrol;
|
||||
unsigned int bit, fcr31, dspcontrol, reg;
|
||||
long epc = regs->cp0_epc;
|
||||
int ret = 0;
|
||||
|
||||
@@ -417,6 +428,8 @@ int __compute_return_epc_for_insn(struct pt_regs *regs,
|
||||
regs->regs[insn.r_format.rd] = epc + 8;
|
||||
/* Fall through */
|
||||
case jr_op:
|
||||
if (NO_R6EMU && insn.r_format.func == jr_op)
|
||||
goto sigill_r6;
|
||||
regs->cp0_epc = regs->regs[insn.r_format.rs];
|
||||
break;
|
||||
}
|
||||
@@ -429,8 +442,10 @@ int __compute_return_epc_for_insn(struct pt_regs *regs,
|
||||
*/
|
||||
case bcond_op:
|
||||
switch (insn.i_format.rt) {
|
||||
case bltz_op:
|
||||
case bltzl_op:
|
||||
if (NO_R6EMU)
|
||||
goto sigill_r6;
|
||||
case bltz_op:
|
||||
if ((long)regs->regs[insn.i_format.rs] < 0) {
|
||||
epc = epc + 4 + (insn.i_format.simmediate << 2);
|
||||
if (insn.i_format.rt == bltzl_op)
|
||||
@@ -440,8 +455,10 @@ int __compute_return_epc_for_insn(struct pt_regs *regs,
|
||||
regs->cp0_epc = epc;
|
||||
break;
|
||||
|
||||
case bgez_op:
|
||||
case bgezl_op:
|
||||
if (NO_R6EMU)
|
||||
goto sigill_r6;
|
||||
case bgez_op:
|
||||
if ((long)regs->regs[insn.i_format.rs] >= 0) {
|
||||
epc = epc + 4 + (insn.i_format.simmediate << 2);
|
||||
if (insn.i_format.rt == bgezl_op)
|
||||
@@ -453,7 +470,29 @@ int __compute_return_epc_for_insn(struct pt_regs *regs,
|
||||
|
||||
case bltzal_op:
|
||||
case bltzall_op:
|
||||
if (NO_R6EMU && (insn.i_format.rs ||
|
||||
insn.i_format.rt == bltzall_op)) {
|
||||
ret = -SIGILL;
|
||||
break;
|
||||
}
|
||||
regs->regs[31] = epc + 8;
|
||||
/*
|
||||
* OK we are here either because we hit a NAL
|
||||
* instruction or because we are emulating an
|
||||
* old bltzal{,l} one. Lets figure out what the
|
||||
* case really is.
|
||||
*/
|
||||
if (!insn.i_format.rs) {
|
||||
/*
|
||||
* NAL or BLTZAL with rs == 0
|
||||
* Doesn't matter if we are R6 or not. The
|
||||
* result is the same
|
||||
*/
|
||||
regs->cp0_epc += 4 +
|
||||
(insn.i_format.simmediate << 2);
|
||||
break;
|
||||
}
|
||||
/* Now do the real thing for non-R6 BLTZAL{,L} */
|
||||
if ((long)regs->regs[insn.i_format.rs] < 0) {
|
||||
epc = epc + 4 + (insn.i_format.simmediate << 2);
|
||||
if (insn.i_format.rt == bltzall_op)
|
||||
@@ -465,7 +504,29 @@ int __compute_return_epc_for_insn(struct pt_regs *regs,
|
||||
|
||||
case bgezal_op:
|
||||
case bgezall_op:
|
||||
if (NO_R6EMU && (insn.i_format.rs ||
|
||||
insn.i_format.rt == bgezall_op)) {
|
||||
ret = -SIGILL;
|
||||
break;
|
||||
}
|
||||
regs->regs[31] = epc + 8;
|
||||
/*
|
||||
* OK we are here either because we hit a BAL
|
||||
* instruction or because we are emulating an
|
||||
* old bgezal{,l} one. Lets figure out what the
|
||||
* case really is.
|
||||
*/
|
||||
if (!insn.i_format.rs) {
|
||||
/*
|
||||
* BAL or BGEZAL with rs == 0
|
||||
* Doesn't matter if we are R6 or not. The
|
||||
* result is the same
|
||||
*/
|
||||
regs->cp0_epc += 4 +
|
||||
(insn.i_format.simmediate << 2);
|
||||
break;
|
||||
}
|
||||
/* Now do the real thing for non-R6 BGEZAL{,L} */
|
||||
if ((long)regs->regs[insn.i_format.rs] >= 0) {
|
||||
epc = epc + 4 + (insn.i_format.simmediate << 2);
|
||||
if (insn.i_format.rt == bgezall_op)
|
||||
@@ -477,7 +538,7 @@ int __compute_return_epc_for_insn(struct pt_regs *regs,
|
||||
|
||||
case bposge32_op:
|
||||
if (!cpu_has_dsp)
|
||||
goto sigill;
|
||||
goto sigill_dsp;
|
||||
|
||||
dspcontrol = rddsp(0x01);
|
||||
|
||||
@@ -508,8 +569,10 @@ int __compute_return_epc_for_insn(struct pt_regs *regs,
|
||||
/*
|
||||
* These are conditional and in i_format.
|
||||
*/
|
||||
case beq_op:
|
||||
case beql_op:
|
||||
if (NO_R6EMU)
|
||||
goto sigill_r6;
|
||||
case beq_op:
|
||||
if (regs->regs[insn.i_format.rs] ==
|
||||
regs->regs[insn.i_format.rt]) {
|
||||
epc = epc + 4 + (insn.i_format.simmediate << 2);
|
||||
@@ -520,8 +583,10 @@ int __compute_return_epc_for_insn(struct pt_regs *regs,
|
||||
regs->cp0_epc = epc;
|
||||
break;
|
||||
|
||||
case bne_op:
|
||||
case bnel_op:
|
||||
if (NO_R6EMU)
|
||||
goto sigill_r6;
|
||||
case bne_op:
|
||||
if (regs->regs[insn.i_format.rs] !=
|
||||
regs->regs[insn.i_format.rt]) {
|
||||
epc = epc + 4 + (insn.i_format.simmediate << 2);
|
||||
@@ -532,8 +597,31 @@ int __compute_return_epc_for_insn(struct pt_regs *regs,
|
||||
regs->cp0_epc = epc;
|
||||
break;
|
||||
|
||||
case blez_op: /* not really i_format */
|
||||
case blezl_op:
|
||||
case blezl_op: /* not really i_format */
|
||||
if (NO_R6EMU)
|
||||
goto sigill_r6;
|
||||
case blez_op:
|
||||
/*
|
||||
* Compact branches for R6 for the
|
||||
* blez and blezl opcodes.
|
||||
* BLEZ | rs = 0 | rt != 0 == BLEZALC
|
||||
* BLEZ | rs = rt != 0 == BGEZALC
|
||||
* BLEZ | rs != 0 | rt != 0 == BGEUC
|
||||
* BLEZL | rs = 0 | rt != 0 == BLEZC
|
||||
* BLEZL | rs = rt != 0 == BGEZC
|
||||
* BLEZL | rs != 0 | rt != 0 == BGEC
|
||||
*
|
||||
* For real BLEZ{,L}, rt is always 0.
|
||||
*/
|
||||
|
||||
if (cpu_has_mips_r6 && insn.i_format.rt) {
|
||||
if ((insn.i_format.opcode == blez_op) &&
|
||||
((!insn.i_format.rs && insn.i_format.rt) ||
|
||||
(insn.i_format.rs == insn.i_format.rt)))
|
||||
regs->regs[31] = epc + 4;
|
||||
regs->cp0_epc += 8;
|
||||
break;
|
||||
}
|
||||
/* rt field assumed to be zero */
|
||||
if ((long)regs->regs[insn.i_format.rs] <= 0) {
|
||||
epc = epc + 4 + (insn.i_format.simmediate << 2);
|
||||
@@ -544,8 +632,32 @@ int __compute_return_epc_for_insn(struct pt_regs *regs,
|
||||
regs->cp0_epc = epc;
|
||||
break;
|
||||
|
||||
case bgtz_op:
|
||||
case bgtzl_op:
|
||||
if (NO_R6EMU)
|
||||
goto sigill_r6;
|
||||
case bgtz_op:
|
||||
/*
|
||||
* Compact branches for R6 for the
|
||||
* bgtz and bgtzl opcodes.
|
||||
* BGTZ | rs = 0 | rt != 0 == BGTZALC
|
||||
* BGTZ | rs = rt != 0 == BLTZALC
|
||||
* BGTZ | rs != 0 | rt != 0 == BLTUC
|
||||
* BGTZL | rs = 0 | rt != 0 == BGTZC
|
||||
* BGTZL | rs = rt != 0 == BLTZC
|
||||
* BGTZL | rs != 0 | rt != 0 == BLTC
|
||||
*
|
||||
* *ZALC varint for BGTZ &&& rt != 0
|
||||
* For real GTZ{,L}, rt is always 0.
|
||||
*/
|
||||
if (cpu_has_mips_r6 && insn.i_format.rt) {
|
||||
if ((insn.i_format.opcode == blez_op) &&
|
||||
((!insn.i_format.rs && insn.i_format.rt) ||
|
||||
(insn.i_format.rs == insn.i_format.rt)))
|
||||
regs->regs[31] = epc + 4;
|
||||
regs->cp0_epc += 8;
|
||||
break;
|
||||
}
|
||||
|
||||
/* rt field assumed to be zero */
|
||||
if ((long)regs->regs[insn.i_format.rs] > 0) {
|
||||
epc = epc + 4 + (insn.i_format.simmediate << 2);
|
||||
@@ -560,40 +672,83 @@ int __compute_return_epc_for_insn(struct pt_regs *regs,
|
||||
* And now the FPA/cp1 branch instructions.
|
||||
*/
|
||||
case cop1_op:
|
||||
preempt_disable();
|
||||
if (is_fpu_owner())
|
||||
fcr31 = read_32bit_cp1_register(CP1_STATUS);
|
||||
else
|
||||
fcr31 = current->thread.fpu.fcr31;
|
||||
preempt_enable();
|
||||
|
||||
bit = (insn.i_format.rt >> 2);
|
||||
bit += (bit != 0);
|
||||
bit += 23;
|
||||
switch (insn.i_format.rt & 3) {
|
||||
case 0: /* bc1f */
|
||||
case 2: /* bc1fl */
|
||||
if (~fcr31 & (1 << bit)) {
|
||||
epc = epc + 4 + (insn.i_format.simmediate << 2);
|
||||
if (insn.i_format.rt == 2)
|
||||
ret = BRANCH_LIKELY_TAKEN;
|
||||
} else
|
||||
if (cpu_has_mips_r6 &&
|
||||
((insn.i_format.rs == bc1eqz_op) ||
|
||||
(insn.i_format.rs == bc1nez_op))) {
|
||||
if (!used_math()) { /* First time FPU user */
|
||||
ret = init_fpu();
|
||||
if (ret && NO_R6EMU) {
|
||||
ret = -ret;
|
||||
break;
|
||||
}
|
||||
ret = 0;
|
||||
set_used_math();
|
||||
}
|
||||
lose_fpu(1); /* Save FPU state for the emulator. */
|
||||
reg = insn.i_format.rt;
|
||||
bit = 0;
|
||||
switch (insn.i_format.rs) {
|
||||
case bc1eqz_op:
|
||||
/* Test bit 0 */
|
||||
if (get_fpr32(¤t->thread.fpu.fpr[reg], 0)
|
||||
& 0x1)
|
||||
bit = 1;
|
||||
break;
|
||||
case bc1nez_op:
|
||||
/* Test bit 0 */
|
||||
if (!(get_fpr32(¤t->thread.fpu.fpr[reg], 0)
|
||||
& 0x1))
|
||||
bit = 1;
|
||||
break;
|
||||
}
|
||||
own_fpu(1);
|
||||
if (bit)
|
||||
epc = epc + 4 +
|
||||
(insn.i_format.simmediate << 2);
|
||||
else
|
||||
epc += 8;
|
||||
regs->cp0_epc = epc;
|
||||
|
||||
break;
|
||||
} else {
|
||||
|
||||
case 1: /* bc1t */
|
||||
case 3: /* bc1tl */
|
||||
if (fcr31 & (1 << bit)) {
|
||||
epc = epc + 4 + (insn.i_format.simmediate << 2);
|
||||
if (insn.i_format.rt == 3)
|
||||
ret = BRANCH_LIKELY_TAKEN;
|
||||
} else
|
||||
epc += 8;
|
||||
regs->cp0_epc = epc;
|
||||
preempt_disable();
|
||||
if (is_fpu_owner())
|
||||
fcr31 = read_32bit_cp1_register(CP1_STATUS);
|
||||
else
|
||||
fcr31 = current->thread.fpu.fcr31;
|
||||
preempt_enable();
|
||||
|
||||
bit = (insn.i_format.rt >> 2);
|
||||
bit += (bit != 0);
|
||||
bit += 23;
|
||||
switch (insn.i_format.rt & 3) {
|
||||
case 0: /* bc1f */
|
||||
case 2: /* bc1fl */
|
||||
if (~fcr31 & (1 << bit)) {
|
||||
epc = epc + 4 +
|
||||
(insn.i_format.simmediate << 2);
|
||||
if (insn.i_format.rt == 2)
|
||||
ret = BRANCH_LIKELY_TAKEN;
|
||||
} else
|
||||
epc += 8;
|
||||
regs->cp0_epc = epc;
|
||||
break;
|
||||
|
||||
case 1: /* bc1t */
|
||||
case 3: /* bc1tl */
|
||||
if (fcr31 & (1 << bit)) {
|
||||
epc = epc + 4 +
|
||||
(insn.i_format.simmediate << 2);
|
||||
if (insn.i_format.rt == 3)
|
||||
ret = BRANCH_LIKELY_TAKEN;
|
||||
} else
|
||||
epc += 8;
|
||||
regs->cp0_epc = epc;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
break;
|
||||
#ifdef CONFIG_CPU_CAVIUM_OCTEON
|
||||
case lwc2_op: /* This is bbit0 on Octeon */
|
||||
if ((regs->regs[insn.i_format.rs] & (1ull<<insn.i_format.rt))
|
||||
@@ -626,15 +781,72 @@ int __compute_return_epc_for_insn(struct pt_regs *regs,
|
||||
epc += 8;
|
||||
regs->cp0_epc = epc;
|
||||
break;
|
||||
#else
|
||||
case bc6_op:
|
||||
/* Only valid for MIPS R6 */
|
||||
if (!cpu_has_mips_r6) {
|
||||
ret = -SIGILL;
|
||||
break;
|
||||
}
|
||||
regs->cp0_epc += 8;
|
||||
break;
|
||||
case balc6_op:
|
||||
if (!cpu_has_mips_r6) {
|
||||
ret = -SIGILL;
|
||||
break;
|
||||
}
|
||||
/* Compact branch: BALC */
|
||||
regs->regs[31] = epc + 4;
|
||||
epc += 4 + (insn.i_format.simmediate << 2);
|
||||
regs->cp0_epc = epc;
|
||||
break;
|
||||
case beqzcjic_op:
|
||||
if (!cpu_has_mips_r6) {
|
||||
ret = -SIGILL;
|
||||
break;
|
||||
}
|
||||
/* Compact branch: BEQZC || JIC */
|
||||
regs->cp0_epc += 8;
|
||||
break;
|
||||
case bnezcjialc_op:
|
||||
if (!cpu_has_mips_r6) {
|
||||
ret = -SIGILL;
|
||||
break;
|
||||
}
|
||||
/* Compact branch: BNEZC || JIALC */
|
||||
if (insn.i_format.rs)
|
||||
regs->regs[31] = epc + 4;
|
||||
regs->cp0_epc += 8;
|
||||
break;
|
||||
#endif
|
||||
case cbcond0_op:
|
||||
case cbcond1_op:
|
||||
/* Only valid for MIPS R6 */
|
||||
if (!cpu_has_mips_r6) {
|
||||
ret = -SIGILL;
|
||||
break;
|
||||
}
|
||||
/*
|
||||
* Compact branches:
|
||||
* bovc, beqc, beqzalc, bnvc, bnec, bnezlac
|
||||
*/
|
||||
if (insn.i_format.rt && !insn.i_format.rs)
|
||||
regs->regs[31] = epc + 4;
|
||||
regs->cp0_epc += 8;
|
||||
break;
|
||||
}
|
||||
|
||||
return ret;
|
||||
|
||||
sigill:
|
||||
sigill_dsp:
|
||||
printk("%s: DSP branch but not DSP ASE - sending SIGBUS.\n", current->comm);
|
||||
force_sig(SIGBUS, current);
|
||||
return -EFAULT;
|
||||
sigill_r6:
|
||||
pr_info("%s: R2 branch but r2-to-r6 emulator is not preset - sending SIGILL.\n",
|
||||
current->comm);
|
||||
force_sig(SIGILL, current);
|
||||
return -EFAULT;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(__compute_return_epc_for_insn);
|
||||
|
||||
|
@@ -39,7 +39,7 @@ int cp0_timer_irq_installed;
|
||||
|
||||
irqreturn_t c0_compare_interrupt(int irq, void *dev_id)
|
||||
{
|
||||
const int r2 = cpu_has_mips_r2;
|
||||
const int r2 = cpu_has_mips_r2_r6;
|
||||
struct clock_event_device *cd;
|
||||
int cpu = smp_processor_id();
|
||||
|
||||
|
@@ -99,11 +99,11 @@ not_nmi:
|
||||
xori t2, t1, 0x7
|
||||
beqz t2, 1f
|
||||
li t3, 32
|
||||
addi t1, t1, 1
|
||||
addiu t1, t1, 1
|
||||
sllv t1, t3, t1
|
||||
1: /* At this point t1 == I-cache sets per way */
|
||||
_EXT t2, v0, MIPS_CONF1_IA_SHF, MIPS_CONF1_IA_SZ
|
||||
addi t2, t2, 1
|
||||
addiu t2, t2, 1
|
||||
mul t1, t1, t0
|
||||
mul t1, t1, t2
|
||||
|
||||
@@ -126,11 +126,11 @@ icache_done:
|
||||
xori t2, t1, 0x7
|
||||
beqz t2, 1f
|
||||
li t3, 32
|
||||
addi t1, t1, 1
|
||||
addiu t1, t1, 1
|
||||
sllv t1, t3, t1
|
||||
1: /* At this point t1 == D-cache sets per way */
|
||||
_EXT t2, v0, MIPS_CONF1_DA_SHF, MIPS_CONF1_DA_SZ
|
||||
addi t2, t2, 1
|
||||
addiu t2, t2, 1
|
||||
mul t1, t1, t0
|
||||
mul t1, t1, t2
|
||||
|
||||
@@ -250,7 +250,7 @@ LEAF(mips_cps_core_init)
|
||||
mfc0 t0, CP0_MVPCONF0
|
||||
srl t0, t0, MVPCONF0_PVPE_SHIFT
|
||||
andi t0, t0, (MVPCONF0_PVPE >> MVPCONF0_PVPE_SHIFT)
|
||||
addi t7, t0, 1
|
||||
addiu t7, t0, 1
|
||||
|
||||
/* If there's only 1, we're done */
|
||||
beqz t0, 2f
|
||||
@@ -280,7 +280,7 @@ LEAF(mips_cps_core_init)
|
||||
mttc0 t0, CP0_TCHALT
|
||||
|
||||
/* Next VPE */
|
||||
addi t5, t5, 1
|
||||
addiu t5, t5, 1
|
||||
slt t0, t5, t7
|
||||
bnez t0, 1b
|
||||
nop
|
||||
@@ -317,7 +317,7 @@ LEAF(mips_cps_boot_vpes)
|
||||
mfc0 t1, CP0_MVPCONF0
|
||||
srl t1, t1, MVPCONF0_PVPE_SHIFT
|
||||
andi t1, t1, MVPCONF0_PVPE >> MVPCONF0_PVPE_SHIFT
|
||||
addi t1, t1, 1
|
||||
addiu t1, t1, 1
|
||||
|
||||
/* Calculate a mask for the VPE ID from EBase.CPUNum */
|
||||
clz t1, t1
|
||||
@@ -424,7 +424,7 @@ LEAF(mips_cps_boot_vpes)
|
||||
|
||||
/* Next VPE */
|
||||
2: srl t6, t6, 1
|
||||
addi t5, t5, 1
|
||||
addiu t5, t5, 1
|
||||
bnez t6, 1b
|
||||
nop
|
||||
|
||||
|
@@ -244,7 +244,7 @@ static inline void check_daddi(void)
|
||||
panic(bug64hit, !DADDI_WAR ? daddiwar : nowar);
|
||||
}
|
||||
|
||||
int daddiu_bug = -1;
|
||||
int daddiu_bug = config_enabled(CONFIG_CPU_MIPSR6) ? 0 : -1;
|
||||
|
||||
static inline void check_daddiu(void)
|
||||
{
|
||||
@@ -314,11 +314,14 @@ static inline void check_daddiu(void)
|
||||
|
||||
void __init check_bugs64_early(void)
|
||||
{
|
||||
check_mult_sh();
|
||||
check_daddiu();
|
||||
if (!config_enabled(CONFIG_CPU_MIPSR6)) {
|
||||
check_mult_sh();
|
||||
check_daddiu();
|
||||
}
|
||||
}
|
||||
|
||||
void __init check_bugs64(void)
|
||||
{
|
||||
check_daddi();
|
||||
if (!config_enabled(CONFIG_CPU_MIPSR6))
|
||||
check_daddi();
|
||||
}
|
||||
|
@@ -237,6 +237,13 @@ static void set_isa(struct cpuinfo_mips *c, unsigned int isa)
|
||||
c->isa_level |= MIPS_CPU_ISA_II | MIPS_CPU_ISA_III;
|
||||
break;
|
||||
|
||||
/* R6 incompatible with everything else */
|
||||
case MIPS_CPU_ISA_M64R6:
|
||||
c->isa_level |= MIPS_CPU_ISA_M32R6 | MIPS_CPU_ISA_M64R6;
|
||||
case MIPS_CPU_ISA_M32R6:
|
||||
c->isa_level |= MIPS_CPU_ISA_M32R6;
|
||||
/* Break here so we don't add incompatible ISAs */
|
||||
break;
|
||||
case MIPS_CPU_ISA_M32R2:
|
||||
c->isa_level |= MIPS_CPU_ISA_M32R2;
|
||||
case MIPS_CPU_ISA_M32R1:
|
||||
@@ -326,6 +333,9 @@ static inline unsigned int decode_config0(struct cpuinfo_mips *c)
|
||||
case 1:
|
||||
set_isa(c, MIPS_CPU_ISA_M32R2);
|
||||
break;
|
||||
case 2:
|
||||
set_isa(c, MIPS_CPU_ISA_M32R6);
|
||||
break;
|
||||
default:
|
||||
goto unknown;
|
||||
}
|
||||
@@ -338,6 +348,9 @@ static inline unsigned int decode_config0(struct cpuinfo_mips *c)
|
||||
case 1:
|
||||
set_isa(c, MIPS_CPU_ISA_M64R2);
|
||||
break;
|
||||
case 2:
|
||||
set_isa(c, MIPS_CPU_ISA_M64R6);
|
||||
break;
|
||||
default:
|
||||
goto unknown;
|
||||
}
|
||||
@@ -501,6 +514,8 @@ static inline unsigned int decode_config5(struct cpuinfo_mips *c)
|
||||
c->options |= MIPS_CPU_EVA;
|
||||
if (config5 & MIPS_CONF5_MRP)
|
||||
c->options |= MIPS_CPU_MAAR;
|
||||
if (config5 & MIPS_CONF5_LLB)
|
||||
c->options |= MIPS_CPU_RW_LLB;
|
||||
|
||||
return config5 & MIPS_CONF_M;
|
||||
}
|
||||
@@ -543,7 +558,7 @@ static void decode_configs(struct cpuinfo_mips *c)
|
||||
}
|
||||
|
||||
#ifndef CONFIG_MIPS_CPS
|
||||
if (cpu_has_mips_r2) {
|
||||
if (cpu_has_mips_r2_r6) {
|
||||
c->core = get_ebase_cpunum();
|
||||
if (cpu_has_mipsmt)
|
||||
c->core >>= fls(core_nvpes()) - 1;
|
||||
@@ -898,6 +913,11 @@ static inline void cpu_probe_mips(struct cpuinfo_mips *c, unsigned int cpu)
|
||||
{
|
||||
c->writecombine = _CACHE_UNCACHED_ACCELERATED;
|
||||
switch (c->processor_id & PRID_IMP_MASK) {
|
||||
case PRID_IMP_QEMU_GENERIC:
|
||||
c->writecombine = _CACHE_UNCACHED;
|
||||
c->cputype = CPU_QEMU_GENERIC;
|
||||
__cpu_name[cpu] = "MIPS GENERIC QEMU";
|
||||
break;
|
||||
case PRID_IMP_4KC:
|
||||
c->cputype = CPU_4KC;
|
||||
c->writecombine = _CACHE_UNCACHED;
|
||||
@@ -1347,8 +1367,7 @@ void cpu_probe(void)
|
||||
if (c->options & MIPS_CPU_FPU) {
|
||||
c->fpu_id = cpu_get_fpu_id();
|
||||
|
||||
if (c->isa_level & (MIPS_CPU_ISA_M32R1 | MIPS_CPU_ISA_M32R2 |
|
||||
MIPS_CPU_ISA_M64R1 | MIPS_CPU_ISA_M64R2)) {
|
||||
if (c->isa_level & cpu_has_mips_r) {
|
||||
if (c->fpu_id & MIPS_FPIR_3D)
|
||||
c->ases |= MIPS_ASE_MIPS3D;
|
||||
if (c->fpu_id & MIPS_FPIR_FREP)
|
||||
@@ -1356,7 +1375,7 @@ void cpu_probe(void)
|
||||
}
|
||||
}
|
||||
|
||||
if (cpu_has_mips_r2) {
|
||||
if (cpu_has_mips_r2_r6) {
|
||||
c->srsets = ((read_c0_srsctl() >> 26) & 0x0f) + 1;
|
||||
/* R2 has Performance Counter Interrupt indicator */
|
||||
c->options |= MIPS_CPU_PCI;
|
||||
|
@@ -11,29 +11,112 @@
|
||||
#include <linux/elf.h>
|
||||
#include <linux/sched.h>
|
||||
|
||||
/* FPU modes */
|
||||
enum {
|
||||
FP_ERROR = -1,
|
||||
FP_DOUBLE_64A = -2,
|
||||
FP_FRE,
|
||||
FP_FR0,
|
||||
FP_FR1,
|
||||
};
|
||||
|
||||
/**
|
||||
* struct mode_req - ABI FPU mode requirements
|
||||
* @single: The program being loaded needs an FPU but it will only issue
|
||||
* single precision instructions meaning that it can execute in
|
||||
* either FR0 or FR1.
|
||||
* @soft: The soft(-float) requirement means that the program being
|
||||
* loaded needs has no FPU dependency at all (i.e. it has no
|
||||
* FPU instructions).
|
||||
* @fr1: The program being loaded depends on FPU being in FR=1 mode.
|
||||
* @frdefault: The program being loaded depends on the default FPU mode.
|
||||
* That is FR0 for O32 and FR1 for N32/N64.
|
||||
* @fre: The program being loaded depends on FPU with FRE=1. This mode is
|
||||
* a bridge which uses FR=1 whilst still being able to maintain
|
||||
* full compatibility with pre-existing code using the O32 FP32
|
||||
* ABI.
|
||||
*
|
||||
* More information about the FP ABIs can be found here:
|
||||
*
|
||||
* https://dmz-portal.mips.com/wiki/MIPS_O32_ABI_-_FR0_and_FR1_Interlinking#10.4.1._Basic_mode_set-up
|
||||
*
|
||||
*/
|
||||
|
||||
struct mode_req {
|
||||
bool single;
|
||||
bool soft;
|
||||
bool fr1;
|
||||
bool frdefault;
|
||||
bool fre;
|
||||
};
|
||||
|
||||
static const struct mode_req fpu_reqs[] = {
|
||||
[MIPS_ABI_FP_ANY] = { true, true, true, true, true },
|
||||
[MIPS_ABI_FP_DOUBLE] = { false, false, false, true, true },
|
||||
[MIPS_ABI_FP_SINGLE] = { true, false, false, false, false },
|
||||
[MIPS_ABI_FP_SOFT] = { false, true, false, false, false },
|
||||
[MIPS_ABI_FP_OLD_64] = { false, false, false, false, false },
|
||||
[MIPS_ABI_FP_XX] = { false, false, true, true, true },
|
||||
[MIPS_ABI_FP_64] = { false, false, true, false, false },
|
||||
[MIPS_ABI_FP_64A] = { false, false, true, false, true }
|
||||
};
|
||||
|
||||
/*
|
||||
* Mode requirements when .MIPS.abiflags is not present in the ELF.
|
||||
* Not present means that everything is acceptable except FR1.
|
||||
*/
|
||||
static struct mode_req none_req = { true, true, false, true, true };
|
||||
|
||||
int arch_elf_pt_proc(void *_ehdr, void *_phdr, struct file *elf,
|
||||
bool is_interp, struct arch_elf_state *state)
|
||||
{
|
||||
struct elfhdr *ehdr = _ehdr;
|
||||
struct elf_phdr *phdr = _phdr;
|
||||
struct elf32_hdr *ehdr32 = _ehdr;
|
||||
struct elf32_phdr *phdr32 = _phdr;
|
||||
struct elf64_phdr *phdr64 = _phdr;
|
||||
struct mips_elf_abiflags_v0 abiflags;
|
||||
int ret;
|
||||
|
||||
if (config_enabled(CONFIG_64BIT) &&
|
||||
(ehdr->e_ident[EI_CLASS] != ELFCLASS32))
|
||||
return 0;
|
||||
if (phdr->p_type != PT_MIPS_ABIFLAGS)
|
||||
return 0;
|
||||
if (phdr->p_filesz < sizeof(abiflags))
|
||||
return -EINVAL;
|
||||
/* Lets see if this is an O32 ELF */
|
||||
if (ehdr32->e_ident[EI_CLASS] == ELFCLASS32) {
|
||||
/* FR = 1 for N32 */
|
||||
if (ehdr32->e_flags & EF_MIPS_ABI2)
|
||||
state->overall_fp_mode = FP_FR1;
|
||||
else
|
||||
/* Set a good default FPU mode for O32 */
|
||||
state->overall_fp_mode = cpu_has_mips_r6 ?
|
||||
FP_FRE : FP_FR0;
|
||||
|
||||
if (ehdr32->e_flags & EF_MIPS_FP64) {
|
||||
/*
|
||||
* Set MIPS_ABI_FP_OLD_64 for EF_MIPS_FP64. We will override it
|
||||
* later if needed
|
||||
*/
|
||||
if (is_interp)
|
||||
state->interp_fp_abi = MIPS_ABI_FP_OLD_64;
|
||||
else
|
||||
state->fp_abi = MIPS_ABI_FP_OLD_64;
|
||||
}
|
||||
if (phdr32->p_type != PT_MIPS_ABIFLAGS)
|
||||
return 0;
|
||||
|
||||
if (phdr32->p_filesz < sizeof(abiflags))
|
||||
return -EINVAL;
|
||||
|
||||
ret = kernel_read(elf, phdr32->p_offset,
|
||||
(char *)&abiflags,
|
||||
sizeof(abiflags));
|
||||
} else {
|
||||
/* FR=1 is really the only option for 64-bit */
|
||||
state->overall_fp_mode = FP_FR1;
|
||||
|
||||
if (phdr64->p_type != PT_MIPS_ABIFLAGS)
|
||||
return 0;
|
||||
if (phdr64->p_filesz < sizeof(abiflags))
|
||||
return -EINVAL;
|
||||
|
||||
ret = kernel_read(elf, phdr64->p_offset,
|
||||
(char *)&abiflags,
|
||||
sizeof(abiflags));
|
||||
}
|
||||
|
||||
ret = kernel_read(elf, phdr->p_offset, (char *)&abiflags,
|
||||
sizeof(abiflags));
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
if (ret != sizeof(abiflags))
|
||||
@@ -48,35 +131,30 @@ int arch_elf_pt_proc(void *_ehdr, void *_phdr, struct file *elf,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline unsigned get_fp_abi(struct elfhdr *ehdr, int in_abi)
|
||||
static inline unsigned get_fp_abi(int in_abi)
|
||||
{
|
||||
/* If the ABI requirement is provided, simply return that */
|
||||
if (in_abi != -1)
|
||||
if (in_abi != MIPS_ABI_FP_UNKNOWN)
|
||||
return in_abi;
|
||||
|
||||
/* If the EF_MIPS_FP64 flag was set, return MIPS_ABI_FP_64 */
|
||||
if (ehdr->e_flags & EF_MIPS_FP64)
|
||||
return MIPS_ABI_FP_64;
|
||||
|
||||
/* Default to MIPS_ABI_FP_DOUBLE */
|
||||
return MIPS_ABI_FP_DOUBLE;
|
||||
/* Unknown ABI */
|
||||
return MIPS_ABI_FP_UNKNOWN;
|
||||
}
|
||||
|
||||
int arch_check_elf(void *_ehdr, bool has_interpreter,
|
||||
struct arch_elf_state *state)
|
||||
{
|
||||
struct elfhdr *ehdr = _ehdr;
|
||||
unsigned fp_abi, interp_fp_abi, abi0, abi1;
|
||||
struct elf32_hdr *ehdr = _ehdr;
|
||||
struct mode_req prog_req, interp_req;
|
||||
int fp_abi, interp_fp_abi, abi0, abi1, max_abi;
|
||||
|
||||
/* Ignore non-O32 binaries */
|
||||
if (config_enabled(CONFIG_64BIT) &&
|
||||
(ehdr->e_ident[EI_CLASS] != ELFCLASS32))
|
||||
if (!config_enabled(CONFIG_MIPS_O32_FP64_SUPPORT))
|
||||
return 0;
|
||||
|
||||
fp_abi = get_fp_abi(ehdr, state->fp_abi);
|
||||
fp_abi = get_fp_abi(state->fp_abi);
|
||||
|
||||
if (has_interpreter) {
|
||||
interp_fp_abi = get_fp_abi(ehdr, state->interp_fp_abi);
|
||||
interp_fp_abi = get_fp_abi(state->interp_fp_abi);
|
||||
|
||||
abi0 = min(fp_abi, interp_fp_abi);
|
||||
abi1 = max(fp_abi, interp_fp_abi);
|
||||
@@ -84,108 +162,103 @@ int arch_check_elf(void *_ehdr, bool has_interpreter,
|
||||
abi0 = abi1 = fp_abi;
|
||||
}
|
||||
|
||||
state->overall_abi = FP_ERROR;
|
||||
/* ABI limits. O32 = FP_64A, N32/N64 = FP_SOFT */
|
||||
max_abi = ((ehdr->e_ident[EI_CLASS] == ELFCLASS32) &&
|
||||
(!(ehdr->e_flags & EF_MIPS_ABI2))) ?
|
||||
MIPS_ABI_FP_64A : MIPS_ABI_FP_SOFT;
|
||||
|
||||
if (abi0 == abi1) {
|
||||
state->overall_abi = abi0;
|
||||
} else if (abi0 == MIPS_ABI_FP_ANY) {
|
||||
state->overall_abi = abi1;
|
||||
} else if (abi0 == MIPS_ABI_FP_DOUBLE) {
|
||||
switch (abi1) {
|
||||
case MIPS_ABI_FP_XX:
|
||||
state->overall_abi = MIPS_ABI_FP_DOUBLE;
|
||||
break;
|
||||
|
||||
case MIPS_ABI_FP_64A:
|
||||
state->overall_abi = FP_DOUBLE_64A;
|
||||
break;
|
||||
}
|
||||
} else if (abi0 == MIPS_ABI_FP_SINGLE ||
|
||||
abi0 == MIPS_ABI_FP_SOFT) {
|
||||
/* Cannot link with other ABIs */
|
||||
} else if (abi0 == MIPS_ABI_FP_OLD_64) {
|
||||
switch (abi1) {
|
||||
case MIPS_ABI_FP_XX:
|
||||
case MIPS_ABI_FP_64:
|
||||
case MIPS_ABI_FP_64A:
|
||||
state->overall_abi = MIPS_ABI_FP_64;
|
||||
break;
|
||||
}
|
||||
} else if (abi0 == MIPS_ABI_FP_XX ||
|
||||
abi0 == MIPS_ABI_FP_64 ||
|
||||
abi0 == MIPS_ABI_FP_64A) {
|
||||
state->overall_abi = MIPS_ABI_FP_64;
|
||||
}
|
||||
|
||||
switch (state->overall_abi) {
|
||||
case MIPS_ABI_FP_64:
|
||||
case MIPS_ABI_FP_64A:
|
||||
case FP_DOUBLE_64A:
|
||||
if (!config_enabled(CONFIG_MIPS_O32_FP64_SUPPORT))
|
||||
return -ELIBBAD;
|
||||
break;
|
||||
|
||||
case FP_ERROR:
|
||||
if ((abi0 > max_abi && abi0 != MIPS_ABI_FP_UNKNOWN) ||
|
||||
(abi1 > max_abi && abi1 != MIPS_ABI_FP_UNKNOWN))
|
||||
return -ELIBBAD;
|
||||
|
||||
/* It's time to determine the FPU mode requirements */
|
||||
prog_req = (abi0 == MIPS_ABI_FP_UNKNOWN) ? none_req : fpu_reqs[abi0];
|
||||
interp_req = (abi1 == MIPS_ABI_FP_UNKNOWN) ? none_req : fpu_reqs[abi1];
|
||||
|
||||
/*
|
||||
* Check whether the program's and interp's ABIs have a matching FPU
|
||||
* mode requirement.
|
||||
*/
|
||||
prog_req.single = interp_req.single && prog_req.single;
|
||||
prog_req.soft = interp_req.soft && prog_req.soft;
|
||||
prog_req.fr1 = interp_req.fr1 && prog_req.fr1;
|
||||
prog_req.frdefault = interp_req.frdefault && prog_req.frdefault;
|
||||
prog_req.fre = interp_req.fre && prog_req.fre;
|
||||
|
||||
/*
|
||||
* Determine the desired FPU mode
|
||||
*
|
||||
* Decision making:
|
||||
*
|
||||
* - We want FR_FRE if FRE=1 and both FR=1 and FR=0 are false. This
|
||||
* means that we have a combination of program and interpreter
|
||||
* that inherently require the hybrid FP mode.
|
||||
* - If FR1 and FRDEFAULT is true, that means we hit the any-abi or
|
||||
* fpxx case. This is because, in any-ABI (or no-ABI) we have no FPU
|
||||
* instructions so we don't care about the mode. We will simply use
|
||||
* the one preferred by the hardware. In fpxx case, that ABI can
|
||||
* handle both FR=1 and FR=0, so, again, we simply choose the one
|
||||
* preferred by the hardware. Next, if we only use single-precision
|
||||
* FPU instructions, and the default ABI FPU mode is not good
|
||||
* (ie single + any ABI combination), we set again the FPU mode to the
|
||||
* one is preferred by the hardware. Next, if we know that the code
|
||||
* will only use single-precision instructions, shown by single being
|
||||
* true but frdefault being false, then we again set the FPU mode to
|
||||
* the one that is preferred by the hardware.
|
||||
* - We want FP_FR1 if that's the only matching mode and the default one
|
||||
* is not good.
|
||||
* - Return with -ELIBADD if we can't find a matching FPU mode.
|
||||
*/
|
||||
if (prog_req.fre && !prog_req.frdefault && !prog_req.fr1)
|
||||
state->overall_fp_mode = FP_FRE;
|
||||
else if ((prog_req.fr1 && prog_req.frdefault) ||
|
||||
(prog_req.single && !prog_req.frdefault))
|
||||
/* Make sure 64-bit MIPS III/IV/64R1 will not pick FR1 */
|
||||
state->overall_fp_mode = ((current_cpu_data.fpu_id & MIPS_FPIR_F64) &&
|
||||
cpu_has_mips_r2_r6) ?
|
||||
FP_FR1 : FP_FR0;
|
||||
else if (prog_req.fr1)
|
||||
state->overall_fp_mode = FP_FR1;
|
||||
else if (!prog_req.fre && !prog_req.frdefault &&
|
||||
!prog_req.fr1 && !prog_req.single && !prog_req.soft)
|
||||
return -ELIBBAD;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline void set_thread_fp_mode(int hybrid, int regs32)
|
||||
{
|
||||
if (hybrid)
|
||||
set_thread_flag(TIF_HYBRID_FPREGS);
|
||||
else
|
||||
clear_thread_flag(TIF_HYBRID_FPREGS);
|
||||
if (regs32)
|
||||
set_thread_flag(TIF_32BIT_FPREGS);
|
||||
else
|
||||
clear_thread_flag(TIF_32BIT_FPREGS);
|
||||
}
|
||||
|
||||
void mips_set_personality_fp(struct arch_elf_state *state)
|
||||
{
|
||||
if (config_enabled(CONFIG_FP32XX_HYBRID_FPRS)) {
|
||||
/*
|
||||
* Use hybrid FPRs for all code which can correctly execute
|
||||
* with that mode.
|
||||
*/
|
||||
switch (state->overall_abi) {
|
||||
case MIPS_ABI_FP_DOUBLE:
|
||||
case MIPS_ABI_FP_SINGLE:
|
||||
case MIPS_ABI_FP_SOFT:
|
||||
case MIPS_ABI_FP_XX:
|
||||
case MIPS_ABI_FP_ANY:
|
||||
/* FR=1, FRE=1 */
|
||||
clear_thread_flag(TIF_32BIT_FPREGS);
|
||||
set_thread_flag(TIF_HYBRID_FPREGS);
|
||||
return;
|
||||
}
|
||||
}
|
||||
/*
|
||||
* This function is only ever called for O32 ELFs so we should
|
||||
* not be worried about N32/N64 binaries.
|
||||
*/
|
||||
|
||||
switch (state->overall_abi) {
|
||||
case MIPS_ABI_FP_DOUBLE:
|
||||
case MIPS_ABI_FP_SINGLE:
|
||||
case MIPS_ABI_FP_SOFT:
|
||||
/* FR=0 */
|
||||
set_thread_flag(TIF_32BIT_FPREGS);
|
||||
clear_thread_flag(TIF_HYBRID_FPREGS);
|
||||
if (!config_enabled(CONFIG_MIPS_O32_FP64_SUPPORT))
|
||||
return;
|
||||
|
||||
switch (state->overall_fp_mode) {
|
||||
case FP_FRE:
|
||||
set_thread_fp_mode(1, 0);
|
||||
break;
|
||||
|
||||
case FP_DOUBLE_64A:
|
||||
/* FR=1, FRE=1 */
|
||||
clear_thread_flag(TIF_32BIT_FPREGS);
|
||||
set_thread_flag(TIF_HYBRID_FPREGS);
|
||||
case FP_FR0:
|
||||
set_thread_fp_mode(0, 1);
|
||||
break;
|
||||
|
||||
case MIPS_ABI_FP_64:
|
||||
case MIPS_ABI_FP_64A:
|
||||
/* FR=1, FRE=0 */
|
||||
clear_thread_flag(TIF_32BIT_FPREGS);
|
||||
clear_thread_flag(TIF_HYBRID_FPREGS);
|
||||
case FP_FR1:
|
||||
set_thread_fp_mode(0, 0);
|
||||
break;
|
||||
|
||||
case MIPS_ABI_FP_XX:
|
||||
case MIPS_ABI_FP_ANY:
|
||||
if (!config_enabled(CONFIG_MIPS_O32_FP64_SUPPORT))
|
||||
set_thread_flag(TIF_32BIT_FPREGS);
|
||||
else
|
||||
clear_thread_flag(TIF_32BIT_FPREGS);
|
||||
|
||||
clear_thread_flag(TIF_HYBRID_FPREGS);
|
||||
break;
|
||||
|
||||
default:
|
||||
case FP_ERROR:
|
||||
BUG();
|
||||
}
|
||||
}
|
||||
|
@@ -46,6 +46,11 @@ resume_userspace:
|
||||
local_irq_disable # make sure we dont miss an
|
||||
# interrupt setting need_resched
|
||||
# between sampling and return
|
||||
#ifdef CONFIG_MIPSR2_TO_R6_EMULATOR
|
||||
lw k0, TI_R2_EMUL_RET($28)
|
||||
bnez k0, restore_all_from_r2_emul
|
||||
#endif
|
||||
|
||||
LONG_L a2, TI_FLAGS($28) # current->work
|
||||
andi t0, a2, _TIF_WORK_MASK # (ignoring syscall_trace)
|
||||
bnez t0, work_pending
|
||||
@@ -114,6 +119,19 @@ restore_partial: # restore partial frame
|
||||
RESTORE_SP_AND_RET
|
||||
.set at
|
||||
|
||||
#ifdef CONFIG_MIPSR2_TO_R6_EMULATOR
|
||||
restore_all_from_r2_emul: # restore full frame
|
||||
.set noat
|
||||
sw zero, TI_R2_EMUL_RET($28) # reset it
|
||||
RESTORE_TEMP
|
||||
RESTORE_AT
|
||||
RESTORE_STATIC
|
||||
RESTORE_SOME
|
||||
LONG_L sp, PT_R29(sp)
|
||||
eretnc
|
||||
.set at
|
||||
#endif
|
||||
|
||||
work_pending:
|
||||
andi t0, a2, _TIF_NEED_RESCHED # a2 is preloaded with TI_FLAGS
|
||||
beqz t0, work_notifysig
|
||||
@@ -158,7 +176,8 @@ syscall_exit_work:
|
||||
jal syscall_trace_leave
|
||||
b resume_userspace
|
||||
|
||||
#if defined(CONFIG_CPU_MIPSR2) || defined(CONFIG_MIPS_MT)
|
||||
#if defined(CONFIG_CPU_MIPSR2) || defined(CONFIG_CPU_MIPSR6) || \
|
||||
defined(CONFIG_MIPS_MT)
|
||||
|
||||
/*
|
||||
* MIPS32R2 Instruction Hazard Barrier - must be called
|
||||
@@ -171,4 +190,4 @@ LEAF(mips_ihb)
|
||||
nop
|
||||
END(mips_ihb)
|
||||
|
||||
#endif /* CONFIG_CPU_MIPSR2 or CONFIG_MIPS_MT */
|
||||
#endif /* CONFIG_CPU_MIPSR2 or CONFIG_CPU_MIPSR6 or CONFIG_MIPS_MT */
|
||||
|
@@ -125,7 +125,7 @@ LEAF(__r4k_wait)
|
||||
nop
|
||||
nop
|
||||
#endif
|
||||
.set arch=r4000
|
||||
.set MIPS_ISA_ARCH_LEVEL_RAW
|
||||
wait
|
||||
/* end of rollback region (the region size must be power of two) */
|
||||
1:
|
||||
|
@@ -186,6 +186,7 @@ void __init check_wait(void)
|
||||
case CPU_PROAPTIV:
|
||||
case CPU_P5600:
|
||||
case CPU_M5150:
|
||||
case CPU_QEMU_GENERIC:
|
||||
cpu_wait = r4k_wait;
|
||||
if (read_c0_config7() & MIPS_CONF7_WII)
|
||||
cpu_wait = r4k_wait_irqoff;
|
||||
|
2378
arch/mips/kernel/mips-r2-to-r6-emul.c
Normal file
2378
arch/mips/kernel/mips-r2-to-r6-emul.c
Normal file
File diff suppressed because it is too large
Load Diff
@@ -77,11 +77,13 @@ EXPORT_SYMBOL(__strnlen_kernel_asm);
|
||||
EXPORT_SYMBOL(__strnlen_user_nocheck_asm);
|
||||
EXPORT_SYMBOL(__strnlen_user_asm);
|
||||
|
||||
#ifndef CONFIG_CPU_MIPSR6
|
||||
EXPORT_SYMBOL(csum_partial);
|
||||
EXPORT_SYMBOL(csum_partial_copy_nocheck);
|
||||
EXPORT_SYMBOL(__csum_partial_copy_kernel);
|
||||
EXPORT_SYMBOL(__csum_partial_copy_to_user);
|
||||
EXPORT_SYMBOL(__csum_partial_copy_from_user);
|
||||
#endif
|
||||
|
||||
EXPORT_SYMBOL(invalid_pte_table);
|
||||
#ifdef CONFIG_FUNCTION_TRACER
|
||||
|
@@ -82,7 +82,9 @@ static int show_cpuinfo(struct seq_file *m, void *v)
|
||||
seq_printf(m, "]\n");
|
||||
}
|
||||
|
||||
seq_printf(m, "isa\t\t\t: mips1");
|
||||
seq_printf(m, "isa\t\t\t:");
|
||||
if (cpu_has_mips_r1)
|
||||
seq_printf(m, " mips1");
|
||||
if (cpu_has_mips_2)
|
||||
seq_printf(m, "%s", " mips2");
|
||||
if (cpu_has_mips_3)
|
||||
@@ -95,10 +97,14 @@ static int show_cpuinfo(struct seq_file *m, void *v)
|
||||
seq_printf(m, "%s", " mips32r1");
|
||||
if (cpu_has_mips32r2)
|
||||
seq_printf(m, "%s", " mips32r2");
|
||||
if (cpu_has_mips32r6)
|
||||
seq_printf(m, "%s", " mips32r6");
|
||||
if (cpu_has_mips64r1)
|
||||
seq_printf(m, "%s", " mips64r1");
|
||||
if (cpu_has_mips64r2)
|
||||
seq_printf(m, "%s", " mips64r2");
|
||||
if (cpu_has_mips64r6)
|
||||
seq_printf(m, "%s", " mips64r6");
|
||||
seq_printf(m, "\n");
|
||||
|
||||
seq_printf(m, "ASEs implemented\t:");
|
||||
|
@@ -581,6 +581,10 @@ int mips_set_process_fp_mode(struct task_struct *task, unsigned int value)
|
||||
if ((value & PR_FP_MODE_FRE) && cpu_has_fpu && !cpu_has_fre)
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
/* FR = 0 not supported in MIPS R6 */
|
||||
if (!(value & PR_FP_MODE_FR) && cpu_has_fpu && cpu_has_mips_r6)
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
/* Save FP & vector context, then disable FPU & MSA */
|
||||
if (task->signal == current->signal)
|
||||
lose_fpu(1);
|
||||
|
@@ -34,7 +34,7 @@
|
||||
.endm
|
||||
|
||||
.set noreorder
|
||||
.set arch=r4000
|
||||
.set MIPS_ISA_ARCH_LEVEL_RAW
|
||||
|
||||
LEAF(_save_fp_context)
|
||||
.set push
|
||||
@@ -42,7 +42,8 @@ LEAF(_save_fp_context)
|
||||
cfc1 t1, fcr31
|
||||
.set pop
|
||||
|
||||
#if defined(CONFIG_64BIT) || defined(CONFIG_CPU_MIPS32_R2)
|
||||
#if defined(CONFIG_64BIT) || defined(CONFIG_CPU_MIPS32_R2) || \
|
||||
defined(CONFIG_CPU_MIPS32_R6)
|
||||
.set push
|
||||
SET_HARDFLOAT
|
||||
#ifdef CONFIG_CPU_MIPS32_R2
|
||||
@@ -105,10 +106,12 @@ LEAF(_save_fp_context32)
|
||||
SET_HARDFLOAT
|
||||
cfc1 t1, fcr31
|
||||
|
||||
#ifndef CONFIG_CPU_MIPS64_R6
|
||||
mfc0 t0, CP0_STATUS
|
||||
sll t0, t0, 5
|
||||
bgez t0, 1f # skip storing odd if FR=0
|
||||
nop
|
||||
#endif
|
||||
|
||||
/* Store the 16 odd double precision registers */
|
||||
EX sdc1 $f1, SC32_FPREGS+8(a0)
|
||||
@@ -163,7 +166,8 @@ LEAF(_save_fp_context32)
|
||||
LEAF(_restore_fp_context)
|
||||
EX lw t1, SC_FPC_CSR(a0)
|
||||
|
||||
#if defined(CONFIG_64BIT) || defined(CONFIG_CPU_MIPS32_R2)
|
||||
#if defined(CONFIG_64BIT) || defined(CONFIG_CPU_MIPS32_R2) || \
|
||||
defined(CONFIG_CPU_MIPS32_R6)
|
||||
.set push
|
||||
SET_HARDFLOAT
|
||||
#ifdef CONFIG_CPU_MIPS32_R2
|
||||
@@ -223,10 +227,12 @@ LEAF(_restore_fp_context32)
|
||||
SET_HARDFLOAT
|
||||
EX lw t1, SC32_FPC_CSR(a0)
|
||||
|
||||
#ifndef CONFIG_CPU_MIPS64_R6
|
||||
mfc0 t0, CP0_STATUS
|
||||
sll t0, t0, 5
|
||||
bgez t0, 1f # skip loading odd if FR=0
|
||||
nop
|
||||
#endif
|
||||
|
||||
EX ldc1 $f1, SC32_FPREGS+8(a0)
|
||||
EX ldc1 $f3, SC32_FPREGS+24(a0)
|
||||
|
@@ -115,7 +115,8 @@
|
||||
* Save a thread's fp context.
|
||||
*/
|
||||
LEAF(_save_fp)
|
||||
#if defined(CONFIG_64BIT) || defined(CONFIG_CPU_MIPS32_R2)
|
||||
#if defined(CONFIG_64BIT) || defined(CONFIG_CPU_MIPS32_R2) || \
|
||||
defined(CONFIG_CPU_MIPS32_R6)
|
||||
mfc0 t0, CP0_STATUS
|
||||
#endif
|
||||
fpu_save_double a0 t0 t1 # clobbers t1
|
||||
@@ -126,7 +127,8 @@ LEAF(_save_fp)
|
||||
* Restore a thread's fp context.
|
||||
*/
|
||||
LEAF(_restore_fp)
|
||||
#if defined(CONFIG_64BIT) || defined(CONFIG_CPU_MIPS32_R2)
|
||||
#if defined(CONFIG_64BIT) || defined(CONFIG_CPU_MIPS32_R2) || \
|
||||
defined(CONFIG_CPU_MIPS32_R6)
|
||||
mfc0 t0, CP0_STATUS
|
||||
#endif
|
||||
fpu_restore_double a0 t0 t1 # clobbers t1
|
||||
@@ -240,9 +242,9 @@ LEAF(_init_fpu)
|
||||
mtc1 t1, $f30
|
||||
mtc1 t1, $f31
|
||||
|
||||
#ifdef CONFIG_CPU_MIPS32_R2
|
||||
#if defined(CONFIG_CPU_MIPS32_R2) || defined(CONFIG_CPU_MIPS32_R6)
|
||||
.set push
|
||||
.set mips32r2
|
||||
.set MIPS_ISA_LEVEL_RAW
|
||||
.set fp=64
|
||||
sll t0, t0, 5 # is Status.FR set?
|
||||
bgez t0, 1f # no: skip setting upper 32b
|
||||
@@ -280,9 +282,9 @@ LEAF(_init_fpu)
|
||||
mthc1 t1, $f30
|
||||
mthc1 t1, $f31
|
||||
1: .set pop
|
||||
#endif /* CONFIG_CPU_MIPS32_R2 */
|
||||
#endif /* CONFIG_CPU_MIPS32_R2 || CONFIG_CPU_MIPS32_R6 */
|
||||
#else
|
||||
.set arch=r4000
|
||||
.set MIPS_ISA_ARCH_LEVEL_RAW
|
||||
dmtc1 t1, $f0
|
||||
dmtc1 t1, $f2
|
||||
dmtc1 t1, $f4
|
||||
|
@@ -208,6 +208,7 @@ void spram_config(void)
|
||||
case CPU_INTERAPTIV:
|
||||
case CPU_PROAPTIV:
|
||||
case CPU_P5600:
|
||||
case CPU_QEMU_GENERIC:
|
||||
config0 = read_c0_config();
|
||||
/* FIXME: addresses are Malta specific */
|
||||
if (config0 & (1<<24)) {
|
||||
|
@@ -136,7 +136,7 @@ static inline int mips_atomic_set(unsigned long addr, unsigned long new)
|
||||
: "memory");
|
||||
} else if (cpu_has_llsc) {
|
||||
__asm__ __volatile__ (
|
||||
" .set arch=r4000 \n"
|
||||
" .set "MIPS_ISA_ARCH_LEVEL" \n"
|
||||
" li %[err], 0 \n"
|
||||
"1: ll %[old], (%[addr]) \n"
|
||||
" move %[tmp], %[new] \n"
|
||||
|
@@ -46,6 +46,7 @@
|
||||
#include <asm/fpu.h>
|
||||
#include <asm/fpu_emulator.h>
|
||||
#include <asm/idle.h>
|
||||
#include <asm/mips-r2-to-r6-emul.h>
|
||||
#include <asm/mipsregs.h>
|
||||
#include <asm/mipsmtregs.h>
|
||||
#include <asm/module.h>
|
||||
@@ -837,7 +838,7 @@ out:
|
||||
exception_exit(prev_state);
|
||||
}
|
||||
|
||||
static void do_trap_or_bp(struct pt_regs *regs, unsigned int code,
|
||||
void do_trap_or_bp(struct pt_regs *regs, unsigned int code,
|
||||
const char *str)
|
||||
{
|
||||
siginfo_t info;
|
||||
@@ -1027,7 +1028,34 @@ asmlinkage void do_ri(struct pt_regs *regs)
|
||||
unsigned int opcode = 0;
|
||||
int status = -1;
|
||||
|
||||
/*
|
||||
* Avoid any kernel code. Just emulate the R2 instruction
|
||||
* as quickly as possible.
|
||||
*/
|
||||
if (mipsr2_emulation && cpu_has_mips_r6 &&
|
||||
likely(user_mode(regs))) {
|
||||
if (likely(get_user(opcode, epc) >= 0)) {
|
||||
status = mipsr2_decoder(regs, opcode);
|
||||
switch (status) {
|
||||
case 0:
|
||||
case SIGEMT:
|
||||
task_thread_info(current)->r2_emul_return = 1;
|
||||
return;
|
||||
case SIGILL:
|
||||
goto no_r2_instr;
|
||||
default:
|
||||
process_fpemu_return(status,
|
||||
¤t->thread.cp0_baduaddr);
|
||||
task_thread_info(current)->r2_emul_return = 1;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
no_r2_instr:
|
||||
|
||||
prev_state = exception_enter();
|
||||
|
||||
if (notify_die(DIE_RI, "RI Fault", regs, 0, regs_to_trapnr(regs),
|
||||
SIGILL) == NOTIFY_STOP)
|
||||
goto out;
|
||||
@@ -1559,6 +1587,7 @@ static inline void parity_protection_init(void)
|
||||
case CPU_INTERAPTIV:
|
||||
case CPU_PROAPTIV:
|
||||
case CPU_P5600:
|
||||
case CPU_QEMU_GENERIC:
|
||||
{
|
||||
#define ERRCTL_PE 0x80000000
|
||||
#define ERRCTL_L2P 0x00800000
|
||||
@@ -1648,7 +1677,7 @@ asmlinkage void cache_parity_error(void)
|
||||
printk("Decoded c0_cacheerr: %s cache fault in %s reference.\n",
|
||||
reg_val & (1<<30) ? "secondary" : "primary",
|
||||
reg_val & (1<<31) ? "data" : "insn");
|
||||
if (cpu_has_mips_r2 &&
|
||||
if ((cpu_has_mips_r2_r6) &&
|
||||
((current_cpu_data.processor_id & 0xff0000) == PRID_COMP_MIPS)) {
|
||||
pr_err("Error bits: %s%s%s%s%s%s%s%s\n",
|
||||
reg_val & (1<<29) ? "ED " : "",
|
||||
@@ -1688,7 +1717,7 @@ asmlinkage void do_ftlb(void)
|
||||
unsigned int reg_val;
|
||||
|
||||
/* For the moment, report the problem and hang. */
|
||||
if (cpu_has_mips_r2 &&
|
||||
if ((cpu_has_mips_r2_r6) &&
|
||||
((current_cpu_data.processor_id & 0xff0000) == PRID_COMP_MIPS)) {
|
||||
pr_err("FTLB error exception, cp0_ecc=0x%08x:\n",
|
||||
read_c0_ecc());
|
||||
@@ -1977,7 +2006,7 @@ static void configure_hwrena(void)
|
||||
{
|
||||
unsigned int hwrena = cpu_hwrena_impl_bits;
|
||||
|
||||
if (cpu_has_mips_r2)
|
||||
if (cpu_has_mips_r2_r6)
|
||||
hwrena |= 0x0000000f;
|
||||
|
||||
if (!noulri && cpu_has_userlocal)
|
||||
@@ -2021,7 +2050,7 @@ void per_cpu_trap_init(bool is_boot_cpu)
|
||||
* o read IntCtl.IPTI to determine the timer interrupt
|
||||
* o read IntCtl.IPPCI to determine the performance counter interrupt
|
||||
*/
|
||||
if (cpu_has_mips_r2) {
|
||||
if (cpu_has_mips_r2_r6) {
|
||||
cp0_compare_irq_shift = CAUSEB_TI - CAUSEB_IP;
|
||||
cp0_compare_irq = (read_c0_intctl() >> INTCTLB_IPTI) & 7;
|
||||
cp0_perfcount_irq = (read_c0_intctl() >> INTCTLB_IPPCI) & 7;
|
||||
@@ -2112,7 +2141,7 @@ void __init trap_init(void)
|
||||
#else
|
||||
ebase = CKSEG0;
|
||||
#endif
|
||||
if (cpu_has_mips_r2)
|
||||
if (cpu_has_mips_r2_r6)
|
||||
ebase += (read_c0_ebase() & 0x3ffff000);
|
||||
}
|
||||
|
||||
|
@@ -129,6 +129,7 @@ extern void show_registers(struct pt_regs *regs);
|
||||
: "=&r" (value), "=r" (res) \
|
||||
: "r" (addr), "i" (-EFAULT));
|
||||
|
||||
#ifndef CONFIG_CPU_MIPSR6
|
||||
#define LoadW(addr, value, res) \
|
||||
__asm__ __volatile__ ( \
|
||||
"1:\t"user_lwl("%0", "(%2)")"\n" \
|
||||
@@ -146,6 +147,39 @@ extern void show_registers(struct pt_regs *regs);
|
||||
".previous" \
|
||||
: "=&r" (value), "=r" (res) \
|
||||
: "r" (addr), "i" (-EFAULT));
|
||||
#else
|
||||
/* MIPSR6 has no lwl instruction */
|
||||
#define LoadW(addr, value, res) \
|
||||
__asm__ __volatile__ ( \
|
||||
".set\tpush\n" \
|
||||
".set\tnoat\n\t" \
|
||||
"1:"user_lb("%0", "0(%2)")"\n\t" \
|
||||
"2:"user_lbu("$1", "1(%2)")"\n\t" \
|
||||
"sll\t%0, 0x8\n\t" \
|
||||
"or\t%0, $1\n\t" \
|
||||
"3:"user_lbu("$1", "2(%2)")"\n\t" \
|
||||
"sll\t%0, 0x8\n\t" \
|
||||
"or\t%0, $1\n\t" \
|
||||
"4:"user_lbu("$1", "3(%2)")"\n\t" \
|
||||
"sll\t%0, 0x8\n\t" \
|
||||
"or\t%0, $1\n\t" \
|
||||
"li\t%1, 0\n" \
|
||||
".set\tpop\n" \
|
||||
"10:\n\t" \
|
||||
".insn\n\t" \
|
||||
".section\t.fixup,\"ax\"\n\t" \
|
||||
"11:\tli\t%1, %3\n\t" \
|
||||
"j\t10b\n\t" \
|
||||
".previous\n\t" \
|
||||
".section\t__ex_table,\"a\"\n\t" \
|
||||
STR(PTR)"\t1b, 11b\n\t" \
|
||||
STR(PTR)"\t2b, 11b\n\t" \
|
||||
STR(PTR)"\t3b, 11b\n\t" \
|
||||
STR(PTR)"\t4b, 11b\n\t" \
|
||||
".previous" \
|
||||
: "=&r" (value), "=r" (res) \
|
||||
: "r" (addr), "i" (-EFAULT));
|
||||
#endif /* CONFIG_CPU_MIPSR6 */
|
||||
|
||||
#define LoadHWU(addr, value, res) \
|
||||
__asm__ __volatile__ ( \
|
||||
@@ -169,6 +203,7 @@ extern void show_registers(struct pt_regs *regs);
|
||||
: "=&r" (value), "=r" (res) \
|
||||
: "r" (addr), "i" (-EFAULT));
|
||||
|
||||
#ifndef CONFIG_CPU_MIPSR6
|
||||
#define LoadWU(addr, value, res) \
|
||||
__asm__ __volatile__ ( \
|
||||
"1:\t"user_lwl("%0", "(%2)")"\n" \
|
||||
@@ -206,6 +241,87 @@ extern void show_registers(struct pt_regs *regs);
|
||||
".previous" \
|
||||
: "=&r" (value), "=r" (res) \
|
||||
: "r" (addr), "i" (-EFAULT));
|
||||
#else
|
||||
/* MIPSR6 has not lwl and ldl instructions */
|
||||
#define LoadWU(addr, value, res) \
|
||||
__asm__ __volatile__ ( \
|
||||
".set\tpush\n\t" \
|
||||
".set\tnoat\n\t" \
|
||||
"1:"user_lbu("%0", "0(%2)")"\n\t" \
|
||||
"2:"user_lbu("$1", "1(%2)")"\n\t" \
|
||||
"sll\t%0, 0x8\n\t" \
|
||||
"or\t%0, $1\n\t" \
|
||||
"3:"user_lbu("$1", "2(%2)")"\n\t" \
|
||||
"sll\t%0, 0x8\n\t" \
|
||||
"or\t%0, $1\n\t" \
|
||||
"4:"user_lbu("$1", "3(%2)")"\n\t" \
|
||||
"sll\t%0, 0x8\n\t" \
|
||||
"or\t%0, $1\n\t" \
|
||||
"li\t%1, 0\n" \
|
||||
".set\tpop\n" \
|
||||
"10:\n\t" \
|
||||
".insn\n\t" \
|
||||
".section\t.fixup,\"ax\"\n\t" \
|
||||
"11:\tli\t%1, %3\n\t" \
|
||||
"j\t10b\n\t" \
|
||||
".previous\n\t" \
|
||||
".section\t__ex_table,\"a\"\n\t" \
|
||||
STR(PTR)"\t1b, 11b\n\t" \
|
||||
STR(PTR)"\t2b, 11b\n\t" \
|
||||
STR(PTR)"\t3b, 11b\n\t" \
|
||||
STR(PTR)"\t4b, 11b\n\t" \
|
||||
".previous" \
|
||||
: "=&r" (value), "=r" (res) \
|
||||
: "r" (addr), "i" (-EFAULT));
|
||||
|
||||
#define LoadDW(addr, value, res) \
|
||||
__asm__ __volatile__ ( \
|
||||
".set\tpush\n\t" \
|
||||
".set\tnoat\n\t" \
|
||||
"1:lb\t%0, 0(%2)\n\t" \
|
||||
"2:lbu\t $1, 1(%2)\n\t" \
|
||||
"dsll\t%0, 0x8\n\t" \
|
||||
"or\t%0, $1\n\t" \
|
||||
"3:lbu\t$1, 2(%2)\n\t" \
|
||||
"dsll\t%0, 0x8\n\t" \
|
||||
"or\t%0, $1\n\t" \
|
||||
"4:lbu\t$1, 3(%2)\n\t" \
|
||||
"dsll\t%0, 0x8\n\t" \
|
||||
"or\t%0, $1\n\t" \
|
||||
"5:lbu\t$1, 4(%2)\n\t" \
|
||||
"dsll\t%0, 0x8\n\t" \
|
||||
"or\t%0, $1\n\t" \
|
||||
"6:lbu\t$1, 5(%2)\n\t" \
|
||||
"dsll\t%0, 0x8\n\t" \
|
||||
"or\t%0, $1\n\t" \
|
||||
"7:lbu\t$1, 6(%2)\n\t" \
|
||||
"dsll\t%0, 0x8\n\t" \
|
||||
"or\t%0, $1\n\t" \
|
||||
"8:lbu\t$1, 7(%2)\n\t" \
|
||||
"dsll\t%0, 0x8\n\t" \
|
||||
"or\t%0, $1\n\t" \
|
||||
"li\t%1, 0\n" \
|
||||
".set\tpop\n\t" \
|
||||
"10:\n\t" \
|
||||
".insn\n\t" \
|
||||
".section\t.fixup,\"ax\"\n\t" \
|
||||
"11:\tli\t%1, %3\n\t" \
|
||||
"j\t10b\n\t" \
|
||||
".previous\n\t" \
|
||||
".section\t__ex_table,\"a\"\n\t" \
|
||||
STR(PTR)"\t1b, 11b\n\t" \
|
||||
STR(PTR)"\t2b, 11b\n\t" \
|
||||
STR(PTR)"\t3b, 11b\n\t" \
|
||||
STR(PTR)"\t4b, 11b\n\t" \
|
||||
STR(PTR)"\t5b, 11b\n\t" \
|
||||
STR(PTR)"\t6b, 11b\n\t" \
|
||||
STR(PTR)"\t7b, 11b\n\t" \
|
||||
STR(PTR)"\t8b, 11b\n\t" \
|
||||
".previous" \
|
||||
: "=&r" (value), "=r" (res) \
|
||||
: "r" (addr), "i" (-EFAULT));
|
||||
#endif /* CONFIG_CPU_MIPSR6 */
|
||||
|
||||
|
||||
#define StoreHW(addr, value, res) \
|
||||
__asm__ __volatile__ ( \
|
||||
@@ -228,6 +344,7 @@ extern void show_registers(struct pt_regs *regs);
|
||||
: "=r" (res) \
|
||||
: "r" (value), "r" (addr), "i" (-EFAULT));
|
||||
|
||||
#ifndef CONFIG_CPU_MIPSR6
|
||||
#define StoreW(addr, value, res) \
|
||||
__asm__ __volatile__ ( \
|
||||
"1:\t"user_swl("%1", "(%2)")"\n" \
|
||||
@@ -263,9 +380,82 @@ extern void show_registers(struct pt_regs *regs);
|
||||
".previous" \
|
||||
: "=r" (res) \
|
||||
: "r" (value), "r" (addr), "i" (-EFAULT));
|
||||
#endif
|
||||
#else
|
||||
/* MIPSR6 has no swl and sdl instructions */
|
||||
#define StoreW(addr, value, res) \
|
||||
__asm__ __volatile__ ( \
|
||||
".set\tpush\n\t" \
|
||||
".set\tnoat\n\t" \
|
||||
"1:"user_sb("%1", "3(%2)")"\n\t" \
|
||||
"srl\t$1, %1, 0x8\n\t" \
|
||||
"2:"user_sb("$1", "2(%2)")"\n\t" \
|
||||
"srl\t$1, $1, 0x8\n\t" \
|
||||
"3:"user_sb("$1", "1(%2)")"\n\t" \
|
||||
"srl\t$1, $1, 0x8\n\t" \
|
||||
"4:"user_sb("$1", "0(%2)")"\n\t" \
|
||||
".set\tpop\n\t" \
|
||||
"li\t%0, 0\n" \
|
||||
"10:\n\t" \
|
||||
".insn\n\t" \
|
||||
".section\t.fixup,\"ax\"\n\t" \
|
||||
"11:\tli\t%0, %3\n\t" \
|
||||
"j\t10b\n\t" \
|
||||
".previous\n\t" \
|
||||
".section\t__ex_table,\"a\"\n\t" \
|
||||
STR(PTR)"\t1b, 11b\n\t" \
|
||||
STR(PTR)"\t2b, 11b\n\t" \
|
||||
STR(PTR)"\t3b, 11b\n\t" \
|
||||
STR(PTR)"\t4b, 11b\n\t" \
|
||||
".previous" \
|
||||
: "=&r" (res) \
|
||||
: "r" (value), "r" (addr), "i" (-EFAULT) \
|
||||
: "memory");
|
||||
|
||||
#define StoreDW(addr, value, res) \
|
||||
__asm__ __volatile__ ( \
|
||||
".set\tpush\n\t" \
|
||||
".set\tnoat\n\t" \
|
||||
"1:sb\t%1, 7(%2)\n\t" \
|
||||
"dsrl\t$1, %1, 0x8\n\t" \
|
||||
"2:sb\t$1, 6(%2)\n\t" \
|
||||
"dsrl\t$1, $1, 0x8\n\t" \
|
||||
"3:sb\t$1, 5(%2)\n\t" \
|
||||
"dsrl\t$1, $1, 0x8\n\t" \
|
||||
"4:sb\t$1, 4(%2)\n\t" \
|
||||
"dsrl\t$1, $1, 0x8\n\t" \
|
||||
"5:sb\t$1, 3(%2)\n\t" \
|
||||
"dsrl\t$1, $1, 0x8\n\t" \
|
||||
"6:sb\t$1, 2(%2)\n\t" \
|
||||
"dsrl\t$1, $1, 0x8\n\t" \
|
||||
"7:sb\t$1, 1(%2)\n\t" \
|
||||
"dsrl\t$1, $1, 0x8\n\t" \
|
||||
"8:sb\t$1, 0(%2)\n\t" \
|
||||
"dsrl\t$1, $1, 0x8\n\t" \
|
||||
".set\tpop\n\t" \
|
||||
"li\t%0, 0\n" \
|
||||
"10:\n\t" \
|
||||
".insn\n\t" \
|
||||
".section\t.fixup,\"ax\"\n\t" \
|
||||
"11:\tli\t%0, %3\n\t" \
|
||||
"j\t10b\n\t" \
|
||||
".previous\n\t" \
|
||||
".section\t__ex_table,\"a\"\n\t" \
|
||||
STR(PTR)"\t1b, 11b\n\t" \
|
||||
STR(PTR)"\t2b, 11b\n\t" \
|
||||
STR(PTR)"\t3b, 11b\n\t" \
|
||||
STR(PTR)"\t4b, 11b\n\t" \
|
||||
STR(PTR)"\t5b, 11b\n\t" \
|
||||
STR(PTR)"\t6b, 11b\n\t" \
|
||||
STR(PTR)"\t7b, 11b\n\t" \
|
||||
STR(PTR)"\t8b, 11b\n\t" \
|
||||
".previous" \
|
||||
: "=&r" (res) \
|
||||
: "r" (value), "r" (addr), "i" (-EFAULT) \
|
||||
: "memory");
|
||||
#endif /* CONFIG_CPU_MIPSR6 */
|
||||
|
||||
#else /* __BIG_ENDIAN */
|
||||
|
||||
#ifdef __LITTLE_ENDIAN
|
||||
#define LoadHW(addr, value, res) \
|
||||
__asm__ __volatile__ (".set\tnoat\n" \
|
||||
"1:\t"user_lb("%0", "1(%2)")"\n" \
|
||||
@@ -286,6 +476,7 @@ extern void show_registers(struct pt_regs *regs);
|
||||
: "=&r" (value), "=r" (res) \
|
||||
: "r" (addr), "i" (-EFAULT));
|
||||
|
||||
#ifndef CONFIG_CPU_MIPSR6
|
||||
#define LoadW(addr, value, res) \
|
||||
__asm__ __volatile__ ( \
|
||||
"1:\t"user_lwl("%0", "3(%2)")"\n" \
|
||||
@@ -303,6 +494,40 @@ extern void show_registers(struct pt_regs *regs);
|
||||
".previous" \
|
||||
: "=&r" (value), "=r" (res) \
|
||||
: "r" (addr), "i" (-EFAULT));
|
||||
#else
|
||||
/* MIPSR6 has no lwl instruction */
|
||||
#define LoadW(addr, value, res) \
|
||||
__asm__ __volatile__ ( \
|
||||
".set\tpush\n" \
|
||||
".set\tnoat\n\t" \
|
||||
"1:"user_lb("%0", "3(%2)")"\n\t" \
|
||||
"2:"user_lbu("$1", "2(%2)")"\n\t" \
|
||||
"sll\t%0, 0x8\n\t" \
|
||||
"or\t%0, $1\n\t" \
|
||||
"3:"user_lbu("$1", "1(%2)")"\n\t" \
|
||||
"sll\t%0, 0x8\n\t" \
|
||||
"or\t%0, $1\n\t" \
|
||||
"4:"user_lbu("$1", "0(%2)")"\n\t" \
|
||||
"sll\t%0, 0x8\n\t" \
|
||||
"or\t%0, $1\n\t" \
|
||||
"li\t%1, 0\n" \
|
||||
".set\tpop\n" \
|
||||
"10:\n\t" \
|
||||
".insn\n\t" \
|
||||
".section\t.fixup,\"ax\"\n\t" \
|
||||
"11:\tli\t%1, %3\n\t" \
|
||||
"j\t10b\n\t" \
|
||||
".previous\n\t" \
|
||||
".section\t__ex_table,\"a\"\n\t" \
|
||||
STR(PTR)"\t1b, 11b\n\t" \
|
||||
STR(PTR)"\t2b, 11b\n\t" \
|
||||
STR(PTR)"\t3b, 11b\n\t" \
|
||||
STR(PTR)"\t4b, 11b\n\t" \
|
||||
".previous" \
|
||||
: "=&r" (value), "=r" (res) \
|
||||
: "r" (addr), "i" (-EFAULT));
|
||||
#endif /* CONFIG_CPU_MIPSR6 */
|
||||
|
||||
|
||||
#define LoadHWU(addr, value, res) \
|
||||
__asm__ __volatile__ ( \
|
||||
@@ -326,6 +551,7 @@ extern void show_registers(struct pt_regs *regs);
|
||||
: "=&r" (value), "=r" (res) \
|
||||
: "r" (addr), "i" (-EFAULT));
|
||||
|
||||
#ifndef CONFIG_CPU_MIPSR6
|
||||
#define LoadWU(addr, value, res) \
|
||||
__asm__ __volatile__ ( \
|
||||
"1:\t"user_lwl("%0", "3(%2)")"\n" \
|
||||
@@ -363,6 +589,86 @@ extern void show_registers(struct pt_regs *regs);
|
||||
".previous" \
|
||||
: "=&r" (value), "=r" (res) \
|
||||
: "r" (addr), "i" (-EFAULT));
|
||||
#else
|
||||
/* MIPSR6 has not lwl and ldl instructions */
|
||||
#define LoadWU(addr, value, res) \
|
||||
__asm__ __volatile__ ( \
|
||||
".set\tpush\n\t" \
|
||||
".set\tnoat\n\t" \
|
||||
"1:"user_lbu("%0", "3(%2)")"\n\t" \
|
||||
"2:"user_lbu("$1", "2(%2)")"\n\t" \
|
||||
"sll\t%0, 0x8\n\t" \
|
||||
"or\t%0, $1\n\t" \
|
||||
"3:"user_lbu("$1", "1(%2)")"\n\t" \
|
||||
"sll\t%0, 0x8\n\t" \
|
||||
"or\t%0, $1\n\t" \
|
||||
"4:"user_lbu("$1", "0(%2)")"\n\t" \
|
||||
"sll\t%0, 0x8\n\t" \
|
||||
"or\t%0, $1\n\t" \
|
||||
"li\t%1, 0\n" \
|
||||
".set\tpop\n" \
|
||||
"10:\n\t" \
|
||||
".insn\n\t" \
|
||||
".section\t.fixup,\"ax\"\n\t" \
|
||||
"11:\tli\t%1, %3\n\t" \
|
||||
"j\t10b\n\t" \
|
||||
".previous\n\t" \
|
||||
".section\t__ex_table,\"a\"\n\t" \
|
||||
STR(PTR)"\t1b, 11b\n\t" \
|
||||
STR(PTR)"\t2b, 11b\n\t" \
|
||||
STR(PTR)"\t3b, 11b\n\t" \
|
||||
STR(PTR)"\t4b, 11b\n\t" \
|
||||
".previous" \
|
||||
: "=&r" (value), "=r" (res) \
|
||||
: "r" (addr), "i" (-EFAULT));
|
||||
|
||||
#define LoadDW(addr, value, res) \
|
||||
__asm__ __volatile__ ( \
|
||||
".set\tpush\n\t" \
|
||||
".set\tnoat\n\t" \
|
||||
"1:lb\t%0, 7(%2)\n\t" \
|
||||
"2:lbu\t$1, 6(%2)\n\t" \
|
||||
"dsll\t%0, 0x8\n\t" \
|
||||
"or\t%0, $1\n\t" \
|
||||
"3:lbu\t$1, 5(%2)\n\t" \
|
||||
"dsll\t%0, 0x8\n\t" \
|
||||
"or\t%0, $1\n\t" \
|
||||
"4:lbu\t$1, 4(%2)\n\t" \
|
||||
"dsll\t%0, 0x8\n\t" \
|
||||
"or\t%0, $1\n\t" \
|
||||
"5:lbu\t$1, 3(%2)\n\t" \
|
||||
"dsll\t%0, 0x8\n\t" \
|
||||
"or\t%0, $1\n\t" \
|
||||
"6:lbu\t$1, 2(%2)\n\t" \
|
||||
"dsll\t%0, 0x8\n\t" \
|
||||
"or\t%0, $1\n\t" \
|
||||
"7:lbu\t$1, 1(%2)\n\t" \
|
||||
"dsll\t%0, 0x8\n\t" \
|
||||
"or\t%0, $1\n\t" \
|
||||
"8:lbu\t$1, 0(%2)\n\t" \
|
||||
"dsll\t%0, 0x8\n\t" \
|
||||
"or\t%0, $1\n\t" \
|
||||
"li\t%1, 0\n" \
|
||||
".set\tpop\n\t" \
|
||||
"10:\n\t" \
|
||||
".insn\n\t" \
|
||||
".section\t.fixup,\"ax\"\n\t" \
|
||||
"11:\tli\t%1, %3\n\t" \
|
||||
"j\t10b\n\t" \
|
||||
".previous\n\t" \
|
||||
".section\t__ex_table,\"a\"\n\t" \
|
||||
STR(PTR)"\t1b, 11b\n\t" \
|
||||
STR(PTR)"\t2b, 11b\n\t" \
|
||||
STR(PTR)"\t3b, 11b\n\t" \
|
||||
STR(PTR)"\t4b, 11b\n\t" \
|
||||
STR(PTR)"\t5b, 11b\n\t" \
|
||||
STR(PTR)"\t6b, 11b\n\t" \
|
||||
STR(PTR)"\t7b, 11b\n\t" \
|
||||
STR(PTR)"\t8b, 11b\n\t" \
|
||||
".previous" \
|
||||
: "=&r" (value), "=r" (res) \
|
||||
: "r" (addr), "i" (-EFAULT));
|
||||
#endif /* CONFIG_CPU_MIPSR6 */
|
||||
|
||||
#define StoreHW(addr, value, res) \
|
||||
__asm__ __volatile__ ( \
|
||||
@@ -384,7 +690,7 @@ extern void show_registers(struct pt_regs *regs);
|
||||
".previous" \
|
||||
: "=r" (res) \
|
||||
: "r" (value), "r" (addr), "i" (-EFAULT));
|
||||
|
||||
#ifndef CONFIG_CPU_MIPSR6
|
||||
#define StoreW(addr, value, res) \
|
||||
__asm__ __volatile__ ( \
|
||||
"1:\t"user_swl("%1", "3(%2)")"\n" \
|
||||
@@ -420,6 +726,79 @@ extern void show_registers(struct pt_regs *regs);
|
||||
".previous" \
|
||||
: "=r" (res) \
|
||||
: "r" (value), "r" (addr), "i" (-EFAULT));
|
||||
#else
|
||||
/* MIPSR6 has no swl and sdl instructions */
|
||||
#define StoreW(addr, value, res) \
|
||||
__asm__ __volatile__ ( \
|
||||
".set\tpush\n\t" \
|
||||
".set\tnoat\n\t" \
|
||||
"1:"user_sb("%1", "0(%2)")"\n\t" \
|
||||
"srl\t$1, %1, 0x8\n\t" \
|
||||
"2:"user_sb("$1", "1(%2)")"\n\t" \
|
||||
"srl\t$1, $1, 0x8\n\t" \
|
||||
"3:"user_sb("$1", "2(%2)")"\n\t" \
|
||||
"srl\t$1, $1, 0x8\n\t" \
|
||||
"4:"user_sb("$1", "3(%2)")"\n\t" \
|
||||
".set\tpop\n\t" \
|
||||
"li\t%0, 0\n" \
|
||||
"10:\n\t" \
|
||||
".insn\n\t" \
|
||||
".section\t.fixup,\"ax\"\n\t" \
|
||||
"11:\tli\t%0, %3\n\t" \
|
||||
"j\t10b\n\t" \
|
||||
".previous\n\t" \
|
||||
".section\t__ex_table,\"a\"\n\t" \
|
||||
STR(PTR)"\t1b, 11b\n\t" \
|
||||
STR(PTR)"\t2b, 11b\n\t" \
|
||||
STR(PTR)"\t3b, 11b\n\t" \
|
||||
STR(PTR)"\t4b, 11b\n\t" \
|
||||
".previous" \
|
||||
: "=&r" (res) \
|
||||
: "r" (value), "r" (addr), "i" (-EFAULT) \
|
||||
: "memory");
|
||||
|
||||
#define StoreDW(addr, value, res) \
|
||||
__asm__ __volatile__ ( \
|
||||
".set\tpush\n\t" \
|
||||
".set\tnoat\n\t" \
|
||||
"1:sb\t%1, 0(%2)\n\t" \
|
||||
"dsrl\t$1, %1, 0x8\n\t" \
|
||||
"2:sb\t$1, 1(%2)\n\t" \
|
||||
"dsrl\t$1, $1, 0x8\n\t" \
|
||||
"3:sb\t$1, 2(%2)\n\t" \
|
||||
"dsrl\t$1, $1, 0x8\n\t" \
|
||||
"4:sb\t$1, 3(%2)\n\t" \
|
||||
"dsrl\t$1, $1, 0x8\n\t" \
|
||||
"5:sb\t$1, 4(%2)\n\t" \
|
||||
"dsrl\t$1, $1, 0x8\n\t" \
|
||||
"6:sb\t$1, 5(%2)\n\t" \
|
||||
"dsrl\t$1, $1, 0x8\n\t" \
|
||||
"7:sb\t$1, 6(%2)\n\t" \
|
||||
"dsrl\t$1, $1, 0x8\n\t" \
|
||||
"8:sb\t$1, 7(%2)\n\t" \
|
||||
"dsrl\t$1, $1, 0x8\n\t" \
|
||||
".set\tpop\n\t" \
|
||||
"li\t%0, 0\n" \
|
||||
"10:\n\t" \
|
||||
".insn\n\t" \
|
||||
".section\t.fixup,\"ax\"\n\t" \
|
||||
"11:\tli\t%0, %3\n\t" \
|
||||
"j\t10b\n\t" \
|
||||
".previous\n\t" \
|
||||
".section\t__ex_table,\"a\"\n\t" \
|
||||
STR(PTR)"\t1b, 11b\n\t" \
|
||||
STR(PTR)"\t2b, 11b\n\t" \
|
||||
STR(PTR)"\t3b, 11b\n\t" \
|
||||
STR(PTR)"\t4b, 11b\n\t" \
|
||||
STR(PTR)"\t5b, 11b\n\t" \
|
||||
STR(PTR)"\t6b, 11b\n\t" \
|
||||
STR(PTR)"\t7b, 11b\n\t" \
|
||||
STR(PTR)"\t8b, 11b\n\t" \
|
||||
".previous" \
|
||||
: "=&r" (res) \
|
||||
: "r" (value), "r" (addr), "i" (-EFAULT) \
|
||||
: "memory");
|
||||
#endif /* CONFIG_CPU_MIPSR6 */
|
||||
#endif
|
||||
|
||||
static void emulate_load_store_insn(struct pt_regs *regs,
|
||||
@@ -703,10 +1082,13 @@ static void emulate_load_store_insn(struct pt_regs *regs,
|
||||
break;
|
||||
return;
|
||||
|
||||
#ifndef CONFIG_CPU_MIPSR6
|
||||
/*
|
||||
* COP2 is available to implementor for application specific use.
|
||||
* It's up to applications to register a notifier chain and do
|
||||
* whatever they have to do, including possible sending of signals.
|
||||
*
|
||||
* This instruction has been reallocated in Release 6
|
||||
*/
|
||||
case lwc2_op:
|
||||
cu2_notifier_call_chain(CU2_LWC2_OP, regs);
|
||||
@@ -723,7 +1105,7 @@ static void emulate_load_store_insn(struct pt_regs *regs,
|
||||
case sdc2_op:
|
||||
cu2_notifier_call_chain(CU2_SDC2_OP, regs);
|
||||
break;
|
||||
|
||||
#endif
|
||||
default:
|
||||
/*
|
||||
* Pheeee... We encountered an yet unknown instruction or
|
||||
|
Reference in New Issue
Block a user