summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorRalf Baechle <ralf@linux-mips.org>2001-04-08 13:24:27 +0000
committerRalf Baechle <ralf@linux-mips.org>2001-04-08 13:24:27 +0000
commitb3874975097055d9c6cd059372751a44374e4c3a (patch)
treebff1fee2a6f69cdc45dd6efbef9882408f6d7aa4
parent986608d5047c413481b0b2db2f54426210e27810 (diff)
Fix ll/sc emulation. Extracted from Linux-VR tree by Harald.
-rw-r--r--arch/mips/kernel/proc.c9
-rw-r--r--arch/mips/kernel/r2300_switch.S3
-rw-r--r--arch/mips/kernel/r4k_switch.S3
-rw-r--r--arch/mips/kernel/sysmips.c4
-rw-r--r--arch/mips/kernel/traps.c119
5 files changed, 96 insertions, 42 deletions
diff --git a/arch/mips/kernel/proc.c b/arch/mips/kernel/proc.c
index 008db22f4..bbf7c1b5e 100644
--- a/arch/mips/kernel/proc.c
+++ b/arch/mips/kernel/proc.c
@@ -14,6 +14,9 @@
unsigned long unaligned_instructions;
unsigned int vced_count, vcei_count;
+#ifdef CONFIG_PROC_FS
+unsigned long ll_ops, sc_ops;
+#endif
/*
* BUFFER is PAGE_SIZE bytes long.
@@ -84,6 +87,12 @@ int get_cpuinfo(char *buffer)
len += sprintf(buffer + len, fmt, 'D', vced_count);
len += sprintf(buffer + len, fmt, 'I', vcei_count);
+#if !defined(CONFIG_CPU_HAS_LLSC)
+ len += sprintf(buffer + len, "ll emulations\t: %lu\n",
+ ll_ops);
+ len += sprintf(buffer + len, "sc emulations\t: %lu\n",
+ sc_ops);
+#endif
return len;
}
diff --git a/arch/mips/kernel/r2300_switch.S b/arch/mips/kernel/r2300_switch.S
index 4a546ebb5..a17873ab5 100644
--- a/arch/mips/kernel/r2300_switch.S
+++ b/arch/mips/kernel/r2300_switch.S
@@ -34,6 +34,9 @@
* task_struct *next)
*/
LEAF(resume)
+#ifndef CONFIG_CPU_HAS_LLSC
+ sw zero, ll_bit
+#endif
mfc0 t1, CP0_STATUS
sw t1, THREAD_STATUS(a0)
CPU_SAVE_NONSCRATCH(a0)
diff --git a/arch/mips/kernel/r4k_switch.S b/arch/mips/kernel/r4k_switch.S
index 10328f4ad..140adcbeb 100644
--- a/arch/mips/kernel/r4k_switch.S
+++ b/arch/mips/kernel/r4k_switch.S
@@ -37,6 +37,9 @@
.set noreorder
.align 5
LEAF(resume)
+#ifndef CONFIG_CPU_HAS_LLSC
+ sw zero, ll_bit
+#endif
mfc0 t1, CP0_STATUS
sw t1, THREAD_STATUS(a0)
CPU_SAVE_NONSCRATCH(a0)
diff --git a/arch/mips/kernel/sysmips.c b/arch/mips/kernel/sysmips.c
index 0d6a445a3..ab028258a 100644
--- a/arch/mips/kernel/sysmips.c
+++ b/arch/mips/kernel/sysmips.c
@@ -75,6 +75,7 @@ sys_sysmips(int cmd, int arg1, int arg2, int arg3)
}
case MIPS_ATOMIC_SET: {
+#ifdef CONFIG_CPU_HAS_LLSC
unsigned int tmp;
p = (int *) arg1;
@@ -118,6 +119,9 @@ sys_sysmips(int cmd, int arg1, int arg2, int arg3)
: /* No outputs */
: "r" (&cmd));
/* Unreached */
+#else
+ printk("sys_sysmips(MIPS_ATOMIC_SET, ...) not ready for !CONFIG_CPU_HAS_LLSC\n");
+#endif
}
case MIPS_FIXADE:
diff --git a/arch/mips/kernel/traps.c b/arch/mips/kernel/traps.c
index c49755fe2..ecb0fcde1 100644
--- a/arch/mips/kernel/traps.c
+++ b/arch/mips/kernel/traps.c
@@ -93,9 +93,9 @@ void simulate_sc(struct pt_regs *regs, unsigned int opcode);
#define RT 0x001f0000
#define OFFSET 0x0000ffff
#define LL 0xc0000000
-#define SC 0xd0000000
+#define SC 0xe0000000
-#define DEBUG_LLSC
+#undef DEBUG_LLSC
#endif
/*
@@ -492,49 +492,69 @@ void do_tr(struct pt_regs *regs)
#if !defined(CONFIG_CPU_HAS_LLSC)
+#ifdef CONFIG_SMP
+#error "ll/sc emulation is not SMP safe"
+#endif
+
/*
* userland emulation for R2300 CPUs
* needed for the multithreading part of glibc
+ *
+ * this implementation can handle only sychronization between 2 or more
+ * user contexts and is not SMP safe.
*/
void do_ri(struct pt_regs *regs)
{
unsigned int opcode;
+ if (!user_mode(regs))
+ BUG();
+
if (!get_insn_opcode(regs, &opcode)) {
- if ((opcode & OPCODE) == LL)
+ if ((opcode & OPCODE) == LL) {
simulate_ll(regs, opcode);
- if ((opcode & OPCODE) == SC)
+ return;
+ }
+ if ((opcode & OPCODE) == SC) {
simulate_sc(regs, opcode);
- } else {
- printk("[%s:%ld] Illegal instruction %08x at %08lx ra=%08lx\n",
- current->comm, (unsigned long)current->pid, opcode,
- regs->cp0_epc, regs->regs[31]);
+ return;
+ }
}
+ printk("[%s:%d] Illegal instruction %08lx at %08lx, ra=%08lx, CP0_STATUS=%08lx\n",
+ current->comm, current->pid, *((unsigned long*)regs->cp0_epc), regs->cp0_epc,
+ regs->regs[31], regs->cp0_status);
if (compute_return_epc(regs))
return;
force_sig(SIGILL, current);
}
/*
- * the ll_bit will be cleared by r2300_switch.S
+ * The ll_bit is cleared by r*_switch.S
*/
-unsigned long ll_bit, *lladdr;
-
+
+unsigned long ll_bit;
+#ifdef CONFIG_PROC_FS
+extern unsigned long ll_ops;
+extern unsigned long sc_ops;
+#endif
+
+static struct task_struct *ll_task = NULL;
+
void simulate_ll(struct pt_regs *regp, unsigned int opcode)
{
- unsigned long *addr, *vaddr;
+ unsigned long value, *vaddr;
long offset;
-
+ int signal = 0;
+
/*
* analyse the ll instruction that just caused a ri exception
* and put the referenced address to addr.
*/
+
/* sign extend offset */
offset = opcode & OFFSET;
- if (offset & 0x00008000)
- offset = -(offset & 0x00007fff);
- else
- offset = (offset & 0x00007fff);
+ offset <<= 16;
+ offset >>= 16;
vaddr = (unsigned long *)((long)(regp->regs[(opcode & BASE) >> 21]) + offset);
@@ -542,31 +562,44 @@ void simulate_ll(struct pt_regs *regp, unsigned int opcode)
printk("ll: vaddr = 0x%08x, reg = %d\n", (unsigned int)vaddr, (opcode & RT) >> 16);
#endif
- /*
- * TODO: compute physical address from vaddr
- */
- panic("ll: emulation not yet finished!");
+#ifdef CONFIG_PROC_FS
+ ll_ops++;
+#endif
- lladdr = addr;
- ll_bit = 1;
- regp->regs[(opcode & RT) >> 16] = *addr;
+ if ((unsigned long)vaddr & 3)
+ signal = SIGBUS;
+ else if (get_user(value, vaddr))
+ signal = SIGSEGV;
+ else {
+ if (ll_task == NULL || ll_task == current) {
+ ll_bit = 1;
+ } else {
+ ll_bit = 0;
+ }
+ ll_task = current;
+ regp->regs[(opcode & RT) >> 16] = value;
+ }
+ if (compute_return_epc(regp))
+ return;
+ if (signal)
+ send_sig(signal, current, 1);
}
-
+
void simulate_sc(struct pt_regs *regp, unsigned int opcode)
{
- unsigned long *addr, *vaddr, reg;
+ unsigned long *vaddr, reg;
long offset;
+ int signal = 0;
/*
* analyse the sc instruction that just caused a ri exception
* and put the referenced address to addr.
*/
+
/* sign extend offset */
offset = opcode & OFFSET;
- if (offset & 0x00008000)
- offset = -(offset & 0x00007fff);
- else
- offset = (offset & 0x00007fff);
+ offset <<= 16;
+ offset >>= 16;
vaddr = (unsigned long *)((long)(regp->regs[(opcode & BASE) >> 21]) + offset);
reg = (opcode & RT) >> 16;
@@ -575,20 +608,22 @@ void simulate_sc(struct pt_regs *regp, unsigned int opcode)
printk("sc: vaddr = 0x%08x, reg = %d\n", (unsigned int)vaddr, (unsigned int)reg);
#endif
- /*
- * TODO: compute physical address from vaddr
- */
- panic("sc: emulation not yet finished!");
-
- lladdr = addr;
+#ifdef CONFIG_PROC_FS
+ sc_ops++;
+#endif
- if (ll_bit == 0) {
+ if ((unsigned long)vaddr & 3)
+ signal = SIGBUS;
+ else if (ll_bit == 0 || ll_task != current)
regp->regs[reg] = 0;
+ else if (put_user(regp->regs[reg], vaddr))
+ signal = SIGSEGV;
+ else
+ regp->regs[reg] = 1;
+ if (compute_return_epc(regp))
return;
- }
-
- *addr = regp->regs[reg];
- regp->regs[reg] = 1;
+ if (signal)
+ send_sig(signal, current, 1);
}
#else /* MIPS 2 or higher */
@@ -599,7 +634,7 @@ void do_ri(struct pt_regs *regs)
get_insn_opcode(regs, &opcode);
printk("[%s:%ld] Illegal instruction %08x at %08lx ra=%08lx\n",
- current->comm, (unsigned long)current->pid, opcode,
+ current->comm, (unsigned long)current->pid, opcode,
regs->cp0_epc, regs->regs[31]);
if (compute_return_epc(regs))
return;