Skip to content

Commit

Permalink
---
Browse files Browse the repository at this point in the history
yaml
---
r: 70914
b: refs/heads/master
c: 60b0d65
h: refs/heads/master
v: v3
  • Loading branch information
Maciej W. Rozycki authored and Ralf Baechle committed Oct 17, 2007
1 parent 49bd35a commit 8d2c3ed
Show file tree
Hide file tree
Showing 2 changed files with 87 additions and 79 deletions.
2 changes: 1 addition & 1 deletion [refs]
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
---
refs/heads/master: 396a2ae08e5080b140330645743ab2567f6bc426
refs/heads/master: 60b0d65541b581955279221e060f8a0a221151b4
164 changes: 86 additions & 78 deletions trunk/arch/mips/kernel/traps.c
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,10 @@
* Copyright (C) 1999 Silicon Graphics, Inc.
* Kevin D. Kissell, kevink@mips.com and Carsten Langgaard, carstenl@mips.com
* Copyright (C) 2000, 01 MIPS Technologies, Inc.
* Copyright (C) 2002, 2003, 2004, 2005 Maciej W. Rozycki
* Copyright (C) 2002, 2003, 2004, 2005, 2007 Maciej W. Rozycki
*/
#include <linux/bug.h>
#include <linux/compiler.h>
#include <linux/init.h>
#include <linux/mm.h>
#include <linux/module.h>
Expand Down Expand Up @@ -410,7 +411,7 @@ asmlinkage void do_be(struct pt_regs *regs)
}

/*
* ll/sc emulation
* ll/sc, rdhwr, sync emulation
*/

#define OPCODE 0xfc000000
Expand All @@ -419,9 +420,11 @@ asmlinkage void do_be(struct pt_regs *regs)
#define OFFSET 0x0000ffff
#define LL 0xc0000000
#define SC 0xe0000000
#define SPEC0 0x00000000
#define SPEC3 0x7c000000
#define RD 0x0000f800
#define FUNC 0x0000003f
#define SYNC 0x0000000f
#define RDHWR 0x0000003b

/*
Expand All @@ -432,11 +435,10 @@ unsigned long ll_bit;

static struct task_struct *ll_task = NULL;

static inline void simulate_ll(struct pt_regs *regs, unsigned int opcode)
static inline int simulate_ll(struct pt_regs *regs, unsigned int opcode)
{
unsigned long value, __user *vaddr;
long offset;
int signal = 0;

/*
* analyse the ll instruction that just caused a ri exception
Expand All @@ -451,14 +453,10 @@ static inline void simulate_ll(struct pt_regs *regs, unsigned int opcode)
vaddr = (unsigned long __user *)
((unsigned long)(regs->regs[(opcode & BASE) >> 21]) + offset);

if ((unsigned long)vaddr & 3) {
signal = SIGBUS;
goto sig;
}
if (get_user(value, vaddr)) {
signal = SIGSEGV;
goto sig;
}
if ((unsigned long)vaddr & 3)
return SIGBUS;
if (get_user(value, vaddr))
return SIGSEGV;

preempt_disable();

Expand All @@ -471,22 +469,16 @@ static inline void simulate_ll(struct pt_regs *regs, unsigned int opcode)

preempt_enable();

compute_return_epc(regs);

regs->regs[(opcode & RT) >> 16] = value;

return;

sig:
force_sig(signal, current);
return 0;
}

static inline void simulate_sc(struct pt_regs *regs, unsigned int opcode)
static inline int simulate_sc(struct pt_regs *regs, unsigned int opcode)
{
unsigned long __user *vaddr;
unsigned long reg;
long offset;
int signal = 0;

/*
* analyse the sc instruction that just caused a ri exception
Expand All @@ -502,34 +494,25 @@ static inline void simulate_sc(struct pt_regs *regs, unsigned int opcode)
((unsigned long)(regs->regs[(opcode & BASE) >> 21]) + offset);
reg = (opcode & RT) >> 16;

if ((unsigned long)vaddr & 3) {
signal = SIGBUS;
goto sig;
}
if ((unsigned long)vaddr & 3)
return SIGBUS;

preempt_disable();

if (ll_bit == 0 || ll_task != current) {
compute_return_epc(regs);
regs->regs[reg] = 0;
preempt_enable();
return;
return 0;
}

preempt_enable();

if (put_user(regs->regs[reg], vaddr)) {
signal = SIGSEGV;
goto sig;
}
if (put_user(regs->regs[reg], vaddr))
return SIGSEGV;

compute_return_epc(regs);
regs->regs[reg] = 1;

return;

sig:
force_sig(signal, current);
return 0;
}

/*
Expand All @@ -539,44 +522,24 @@ static inline void simulate_sc(struct pt_regs *regs, unsigned int opcode)
* few processors such as NEC's VR4100 throw reserved instruction exceptions
* instead, so we're doing the emulation thing in both exception handlers.
*/
static inline int simulate_llsc(struct pt_regs *regs)
static int simulate_llsc(struct pt_regs *regs, unsigned int opcode)
{
unsigned int opcode;

if (get_user(opcode, (unsigned int __user *) exception_epc(regs)))
goto out_sigsegv;

if ((opcode & OPCODE) == LL) {
simulate_ll(regs, opcode);
return 0;
}
if ((opcode & OPCODE) == SC) {
simulate_sc(regs, opcode);
return 0;
}

return -EFAULT; /* Strange things going on ... */
if ((opcode & OPCODE) == LL)
return simulate_ll(regs, opcode);
if ((opcode & OPCODE) == SC)
return simulate_sc(regs, opcode);

out_sigsegv:
force_sig(SIGSEGV, current);
return -EFAULT;
return -1; /* Must be something else ... */
}

/*
* Simulate trapping 'rdhwr' instructions to provide user accessible
* registers not implemented in hardware. The only current use of this
* is the thread area pointer.
*/
static inline int simulate_rdhwr(struct pt_regs *regs)
static int simulate_rdhwr(struct pt_regs *regs, unsigned int opcode)
{
struct thread_info *ti = task_thread_info(current);
unsigned int opcode;

if (get_user(opcode, (unsigned int __user *) exception_epc(regs)))
goto out_sigsegv;

if (unlikely(compute_return_epc(regs)))
return -EFAULT;

if ((opcode & OPCODE) == SPEC3 && (opcode & FUNC) == RDHWR) {
int rd = (opcode & RD) >> 11;
Expand All @@ -586,16 +549,20 @@ static inline int simulate_rdhwr(struct pt_regs *regs)
regs->regs[rt] = ti->tp_value;
return 0;
default:
return -EFAULT;
return -1;
}
}

/* Not ours. */
return -EFAULT;
return -1;
}

out_sigsegv:
force_sig(SIGSEGV, current);
return -EFAULT;
static int simulate_sync(struct pt_regs *regs, unsigned int opcode)
{
if ((opcode & OPCODE) == SPEC0 && (opcode & FUNC) == SYNC)
return 0;

return -1; /* Must be something else ... */
}

asmlinkage void do_ov(struct pt_regs *regs)
Expand Down Expand Up @@ -767,16 +734,35 @@ asmlinkage void do_tr(struct pt_regs *regs)

asmlinkage void do_ri(struct pt_regs *regs)
{
die_if_kernel("Reserved instruction in kernel code", regs);
unsigned int __user *epc = (unsigned int __user *)exception_epc(regs);
unsigned long old_epc = regs->cp0_epc;
unsigned int opcode = 0;
int status = -1;

if (!cpu_has_llsc)
if (!simulate_llsc(regs))
return;
die_if_kernel("Reserved instruction in kernel code", regs);

if (!simulate_rdhwr(regs))
if (unlikely(compute_return_epc(regs) < 0))
return;

force_sig(SIGILL, current);
if (unlikely(get_user(opcode, epc) < 0))
status = SIGSEGV;

if (!cpu_has_llsc && status < 0)
status = simulate_llsc(regs, opcode);

if (status < 0)
status = simulate_rdhwr(regs, opcode);

if (status < 0)
status = simulate_sync(regs, opcode);

if (status < 0)
status = SIGILL;

if (unlikely(status > 0)) {
regs->cp0_epc = old_epc; /* Undo skip-over. */
force_sig(status, current);
}
}

/*
Expand Down Expand Up @@ -808,22 +794,44 @@ static void mt_ase_fp_affinity(void)

asmlinkage void do_cpu(struct pt_regs *regs)
{
unsigned int __user *epc;
unsigned long old_epc;
unsigned int opcode;
unsigned int cpid;
int status;

die_if_kernel("do_cpu invoked from kernel context!", regs);

cpid = (regs->cp0_cause >> CAUSEB_CE) & 3;

switch (cpid) {
case 0:
if (!cpu_has_llsc)
if (!simulate_llsc(regs))
return;
epc = (unsigned int __user *)exception_epc(regs);
old_epc = regs->cp0_epc;
opcode = 0;
status = -1;

if (!simulate_rdhwr(regs))
if (unlikely(compute_return_epc(regs) < 0))
return;

break;
if (unlikely(get_user(opcode, epc) < 0))
status = SIGSEGV;

if (!cpu_has_llsc && status < 0)
status = simulate_llsc(regs, opcode);

if (status < 0)
status = simulate_rdhwr(regs, opcode);

if (status < 0)
status = SIGILL;

if (unlikely(status > 0)) {
regs->cp0_epc = old_epc; /* Undo skip-over. */
force_sig(status, current);
}

return;

case 1:
if (used_math()) /* Using the FPU again. */
Expand Down

0 comments on commit 8d2c3ed

Please sign in to comment.