diff options
author | Ralf Baechle <ralf@linux-mips.org> | 2001-04-08 13:24:27 +0000 |
---|---|---|
committer | Ralf Baechle <ralf@linux-mips.org> | 2001-04-08 13:24:27 +0000 |
commit | b3874975097055d9c6cd059372751a44374e4c3a (patch) | |
tree | bff1fee2a6f69cdc45dd6efbef9882408f6d7aa4 /arch | |
parent | 986608d5047c413481b0b2db2f54426210e27810 (diff) |
Fix ll/sc emulation. Extracted from Linux-VR tree by Harald.
Diffstat (limited to 'arch')
-rw-r--r-- | arch/mips/kernel/proc.c | 9 | ||||
-rw-r--r-- | arch/mips/kernel/r2300_switch.S | 3 | ||||
-rw-r--r-- | arch/mips/kernel/r4k_switch.S | 3 | ||||
-rw-r--r-- | arch/mips/kernel/sysmips.c | 4 | ||||
-rw-r--r-- | arch/mips/kernel/traps.c | 119 |
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; |