patch-2.1.79 linux/arch/sparc64/kernel/unaligned.c
Next file: linux/arch/sparc64/kernel/winfixup.S
Previous file: linux/arch/sparc64/kernel/ttable.S
Back to the patch index
Back to the overall index
- Lines: 462
- Date:
Mon Jan 12 15:15:44 1998
- Orig file:
v2.1.78/linux/arch/sparc64/kernel/unaligned.c
- Orig date:
Thu Sep 4 17:07:30 1997
diff -u --recursive --new-file v2.1.78/linux/arch/sparc64/kernel/unaligned.c linux/arch/sparc64/kernel/unaligned.c
@@ -1,4 +1,4 @@
-/* $Id: unaligned.c,v 1.4 1997/08/19 15:25:11 jj Exp $
+/* $Id: unaligned.c,v 1.8 1997/10/14 16:21:24 jj Exp $
* unaligned.c: Unaligned load/store trap handling with special
* cases for the kernel to do them more quickly.
*
@@ -17,6 +17,8 @@
#include <asm/uaccess.h>
#include <linux/smp.h>
#include <linux/smp_lock.h>
+#include <asm/fpumacro.h>
+#include <asm/bitops.h>
/* #define DEBUG_MNA */
@@ -24,8 +26,8 @@
load, /* ld, ldd, ldh, ldsh */
store, /* st, std, sth, stsh */
both, /* Swap, ldstub, cas, ... */
- fpload,
- fpstore,
+ fpld,
+ fpst,
invalid,
};
@@ -101,34 +103,52 @@
return imm << 51 >> 51;
}
-static inline unsigned long fetch_reg(unsigned int reg, struct pt_regs *regs)
+static unsigned long fetch_reg(unsigned int reg, struct pt_regs *regs)
{
- struct reg_window *win;
-
+ unsigned long value;
+
if(reg < 16)
return (!reg ? 0 : regs->u_regs[reg]);
-
- /* Ho hum, the slightly complicated case. */
- win = (struct reg_window *) regs->u_regs[UREG_FP];
- return win->locals[reg - 16]; /* yes, I know what this does... */
+ if (regs->tstate & TSTATE_PRIV) {
+ struct reg_window *win;
+ win = (struct reg_window *)(regs->u_regs[UREG_FP] + STACK_BIAS);
+ value = win->locals[reg - 16];
+ } else if (current->tss.flags & SPARC_FLAG_32BIT) {
+ struct reg_window32 *win32;
+ win32 = (struct reg_window32 *)((unsigned long)((u32)regs->u_regs[UREG_FP]));
+ get_user(value, &win32->locals[reg - 16]);
+ } else {
+ struct reg_window *win;
+ win = (struct reg_window *)(regs->u_regs[UREG_FP] + STACK_BIAS);
+ get_user(value, &win->locals[reg - 16]);
+ }
+ return value;
}
-static inline unsigned long *fetch_reg_addr(unsigned int reg, struct pt_regs *regs)
+static unsigned long *fetch_reg_addr(unsigned int reg, struct pt_regs *regs)
{
- struct reg_window *win;
-
if(reg < 16)
return ®s->u_regs[reg];
- win = (struct reg_window *) regs->u_regs[UREG_FP];
- return &win->locals[reg - 16];
+ if (regs->tstate & TSTATE_PRIV) {
+ struct reg_window *win;
+ win = (struct reg_window *)(regs->u_regs[UREG_FP] + STACK_BIAS);
+ return &win->locals[reg - 16];
+ } else if (current->tss.flags & SPARC_FLAG_32BIT) {
+ struct reg_window32 *win32;
+ win32 = (struct reg_window32 *)((unsigned long)((u32)regs->u_regs[UREG_FP]));
+ return (unsigned long *)&win32->locals[reg - 16];
+ } else {
+ struct reg_window *win;
+ win = (struct reg_window *)(regs->u_regs[UREG_FP] + STACK_BIAS);
+ return &win->locals[reg - 16];
+ }
}
static inline unsigned long compute_effective_address(struct pt_regs *regs,
- unsigned int insn)
+ unsigned int insn, unsigned int rd)
{
unsigned int rs1 = (insn >> 14) & 0x1f;
unsigned int rs2 = insn & 0x1f;
- unsigned int rd = (insn >> 25) & 0x1f;
if(insn & 0x2000) {
maybe_flush_windows(rs1, 0, rd);
@@ -326,7 +346,7 @@
unsigned long fixup = search_exception_table (regs->tpc, &g2);
if (!fixup) {
- unsigned long address = compute_effective_address(regs, insn);
+ unsigned long address = compute_effective_address(regs, insn, ((insn >> 25) & 0x1f));
if(address < PAGE_SIZE) {
printk(KERN_ALERT "Unable to handle kernel NULL pointer dereference in mna handler");
} else
@@ -344,7 +364,7 @@
regs->u_regs [UREG_G2] = g2;
}
-asmlinkage void kernel_unaligned_trap(struct pt_regs *regs, unsigned int insn)
+asmlinkage void kernel_unaligned_trap(struct pt_regs *regs, unsigned int insn, unsigned long sfar, unsigned long sfsr)
{
enum direction dir = decode_direction(insn);
int size = decode_access_size(insn);
@@ -365,7 +385,7 @@
: "o0", "o1", "o2", "o3", "o4", "o5", "o7",
"g1", "g2", "g3", "g4", "g5", "g7", "cc");
} else {
- unsigned long addr = compute_effective_address(regs, insn);
+ unsigned long addr = compute_effective_address(regs, insn, ((insn >> 25) & 0x1f));
#ifdef DEBUG_MNA
printk("KMNA: pc=%016lx [dir=%s addr=%016lx size=%d] retpc[%016lx]\n",
@@ -401,117 +421,243 @@
unlock_kernel();
}
-#if 0 /* XXX: Implement user mna some day */
-static inline int ok_for_user(struct pt_regs *regs, unsigned int insn,
- enum direction dir)
-{
- unsigned int reg;
- int retval, check = (dir == load) ? VERIFY_READ : VERIFY_WRITE;
- int size = ((insn >> 19) & 3) == 3 ? 8 : 4;
-
- if((regs->pc | regs->npc) & 3)
- return 0;
+static char popc_helper[] = {
+0, 1, 1, 2, 1, 2, 2, 3,
+1, 2, 2, 3, 2, 3, 3, 4,
+};
- /* Must verify_area() in all the necessary places. */
-#define WINREG_ADDR(regnum) ((void *)(((unsigned long *)regs->u_regs[UREG_FP])+(regnum)))
- retval = 0;
- reg = (insn >> 25) & 0x1f;
- if(reg >= 16) {
- retval = verify_area(check, WINREG_ADDR(reg - 16), size);
- if(retval)
- return retval;
+int handle_popc(u32 insn, struct pt_regs *regs)
+{
+ u64 value;
+ int ret, i, rd = ((insn >> 25) & 0x1f);
+
+ if (insn & 0x2000) {
+ maybe_flush_windows(0, 0, rd);
+ value = sign_extend_imm13(insn);
+ } else {
+ maybe_flush_windows(0, insn & 0x1f, rd);
+ value = fetch_reg(insn & 0x1f, regs);
}
- reg = (insn >> 14) & 0x1f;
- if(reg >= 16) {
- retval = verify_area(check, WINREG_ADDR(reg - 16), size);
- if(retval)
- return retval;
+ for (ret = 0, i = 0; i < 16; i++) {
+ ret += popc_helper[value & 0xf];
+ value >>= 4;
}
- if(!(insn & 0x2000)) {
- reg = (insn & 0x1f);
- if(reg >= 16) {
- retval = verify_area(check, WINREG_ADDR(reg - 16), size);
- if(retval)
- return retval;
+ if(rd < 16) {
+ if (rd)
+ regs->u_regs[rd] = ret;
+ } else {
+ if (current->tss.flags & SPARC_FLAG_32BIT) {
+ struct reg_window32 *win32;
+ win32 = (struct reg_window32 *)((unsigned long)((u32)regs->u_regs[UREG_FP]));
+ put_user(ret, &win32->locals[rd - 16]);
+ } else {
+ struct reg_window *win;
+ win = (struct reg_window *)(regs->u_regs[UREG_FP] + STACK_BIAS);
+ get_user(ret, &win->locals[rd - 16]);
}
}
- return retval;
-#undef WINREG_ADDR
+ advance(regs);
+ return 1;
}
-void user_mna_trap_fault(struct pt_regs *regs, unsigned int insn) __asm__ ("user_mna_trap_fault");
+extern void do_fpother(struct pt_regs *regs);
+extern void do_privact(struct pt_regs *regs);
+extern void data_access_exception(struct pt_regs *regs);
+
+int handle_ldq_stq(u32 insn, struct pt_regs *regs)
+{
+ unsigned long addr = compute_effective_address(regs, insn, 0);
+ int freg = ((insn >> 25) & 0x1e) | ((insn >> 20) & 0x20);
+ struct fpustate *f = FPUSTATE;
+ int asi = decode_asi(insn, regs);
+ int flag = (freg < 32) ? SPARC_FLAG_USEDFPUL : SPARC_FLAG_USEDFPUU;
+
+ f->fsr &= ~0x1c000;
+ if (freg & 3) {
+ f->fsr |= (6 << 14) /* invalid_fp_register */;
+ do_fpother(regs);
+ return 0;
+ }
+ if (insn & 0x200000) {
+ /* STQ */
+ u64 first = 0, second = 0;
+
+ if (current->tss.flags & flag) {
+ first = *(u64 *)&f->regs[freg];
+ second = *(u64 *)&f->regs[freg+2];
+ }
+ if (asi < 0x80) {
+ do_privact(regs);
+ return 1;
+ }
+ switch (asi) {
+ case ASI_P:
+ case ASI_S: break;
+ case ASI_PL:
+ case ASI_SL:
+ {
+ /* Need to convert endians */
+ u64 tmp = __swab64p(&first);
+
+ first = __swab64p(&second);
+ second = tmp;
+ break;
+ }
+ default:
+ data_access_exception(regs);
+ return 1;
+ }
+ if (put_user (first >> 32, (u32 *)addr) ||
+ __put_user ((u32)first, (u32 *)(addr + 4)) ||
+ __put_user (second >> 32, (u32 *)(addr + 8)) ||
+ __put_user ((u32)second, (u32 *)(addr + 12))) {
+ data_access_exception(regs);
+ return 1;
+ }
+ } else {
+ /* LDQ */
+ u32 first, second, third, fourth;
-void user_mna_trap_fault(struct pt_regs *regs, unsigned int insn)
-{
- current->tss.sig_address = regs->pc;
- current->tss.sig_desc = SUBSIG_PRIVINST;
- send_sig(SIGBUS, current, 1);
+ if (asi < 0x80) {
+ do_privact(regs);
+ return 1;
+ } else if (asi > ASI_SNFL) {
+ data_access_exception(regs);
+ return 1;
+ }
+ if (get_user (first, (u32 *)addr) ||
+ __get_user (second, (u32 *)(addr + 4)) ||
+ __get_user (third, (u32 *)(addr + 8)) ||
+ __get_user (fourth, (u32 *)(addr + 12))) {
+ if (asi & 0x2) /* NF */ {
+ first = 0; second = 0; third = 0; fourth = 0;
+ } else {
+ data_access_exception(regs);
+ return 1;
+ }
+ }
+ if (asi & 0x8) /* Little */ {
+ u32 tmp = le32_to_cpup(&first);
+
+ first = le32_to_cpup(&fourth);
+ fourth = tmp;
+ tmp = le32_to_cpup(&second);
+ second = le32_to_cpup(&third);
+ third = tmp;
+ }
+ regs->fprs |= FPRS_FEF;
+ if (!(current->tss.flags & SPARC_FLAG_USEDFPU)) {
+ current->tss.flags |= SPARC_FLAG_USEDFPU;
+ f->fsr = 0;
+ f->gsr = 0;
+ }
+ if (!(current->tss.flags & flag)) {
+ if (freg < 32)
+ memset(f->regs, 0, 32*sizeof(u32));
+ else
+ memset(f->regs+32, 0, 32*sizeof(u32));
+ }
+ f->regs[freg] = first;
+ f->regs[freg+1] = second;
+ f->regs[freg+2] = third;
+ f->regs[freg+3] = fourth;
+ current->tss.flags |= flag;
+ }
+ advance(regs);
+ return 1;
}
-asmlinkage void user_unaligned_trap(struct pt_regs *regs, unsigned int insn)
+void handle_lddfmna(struct pt_regs *regs, unsigned long sfar, unsigned long sfsr)
{
- enum direction dir;
-
- lock_kernel();
- if(!(current->tss.flags & SPARC_FLAG_UNALIGNED) ||
- (((insn >> 30) & 3) != 3))
- goto kill_user;
- dir = decode_direction(insn);
- if(!ok_for_user(regs, insn, dir)) {
- goto kill_user;
- } else {
- int size = decode_access_size(insn);
- unsigned long addr;
-
- if(floating_point_load_or_store_p(insn)) {
- printk("User FPU load/store unaligned unsupported.\n");
- goto kill_user;
+ unsigned long pc = regs->tpc;
+ unsigned long tstate = regs->tstate;
+ u32 insn;
+ u32 first, second;
+ u64 value;
+ u8 asi, freg;
+ int flag;
+ struct fpustate *f = FPUSTATE;
+
+ if(tstate & TSTATE_PRIV)
+ die_if_kernel("lddfmna from kernel", regs);
+ if(current->tss.flags & SPARC_FLAG_32BIT)
+ pc = (u32)pc;
+ if (get_user(insn, (u32 *)pc) != -EFAULT) {
+ asi = sfsr >> 16;
+ if (asi > ASI_SNFL)
+ goto daex;
+ if (get_user(first, (u32 *)sfar) ||
+ get_user(second, (u32 *)(sfar + 4))) {
+ if (asi & 0x2) /* NF */ {
+ first = 0; second = 0;
+ } else
+ goto daex;
}
-
- addr = compute_effective_address(regs, insn);
- switch(dir) {
- case load:
- do_integer_load(fetch_reg_addr(((insn>>25)&0x1f), regs),
- size, (unsigned long *) addr,
- decode_signedness(insn),
- user_unaligned_trap_fault);
- break;
-
- case store:
- do_integer_store(((insn>>25)&0x1f), size,
- (unsigned long *) addr, regs,
- user_unaligned_trap_fault);
- break;
-
- case both:
- do_atomic(fetch_reg_addr(((insn>>25)&0x1f), regs),
- (unsigned long *) addr,
- user_unaligned_trap_fault);
- break;
-
- default:
- unaligned_panic("Impossible user unaligned trap.");
-
- __asm__ __volatile__ ("\n"
-"user_unaligned_trap_fault:\n\t"
- "mov %0, %%o0\n\t"
- "call user_mna_trap_fault\n\t"
- " mov %1, %%o1\n\t"
- :
- : "r" (regs), "r" (insn)
- : "o0", "o1", "o2", "o3", "o4", "o5", "o7",
- "g1", "g2", "g3", "g4", "g5", "g7", "cc");
- goto out;
+ freg = ((insn >> 25) & 0x1e) | ((insn >> 20) & 0x20);
+ value = (((u64)first) << 32) | second;
+ if (asi & 0x8) /* Little */
+ value = __swab64p(&value);
+ flag = (freg < 32) ? SPARC_FLAG_USEDFPUL : SPARC_FLAG_USEDFPUU;
+ regs->fprs |= FPRS_FEF;
+ if (!(current->tss.flags & SPARC_FLAG_USEDFPU)) {
+ current->tss.flags |= SPARC_FLAG_USEDFPU;
+ f->fsr = 0;
+ f->gsr = 0;
}
- advance(regs);
- goto out;
+ if (!(current->tss.flags & flag)) {
+ if (freg < 32)
+ memset(f->regs, 0, 32*sizeof(u32));
+ else
+ memset(f->regs+32, 0, 32*sizeof(u32));
+ }
+ *(u64 *)(f->regs + freg) = value;
+ current->tss.flags |= flag;
+ } else {
+daex: data_access_exception(regs);
+ return;
}
+ advance(regs);
+ return;
+}
-kill_user:
- current->tss.sig_address = regs->pc;
- current->tss.sig_desc = SUBSIG_PRIVINST;
- send_sig(SIGBUS, current, 1);
-out:
- unlock_kernel();
+void handle_stdfmna(struct pt_regs *regs, unsigned long sfar, unsigned long sfsr)
+{
+ unsigned long pc = regs->tpc;
+ unsigned long tstate = regs->tstate;
+ u32 insn;
+ u64 value;
+ u8 asi, freg;
+ int flag;
+ struct fpustate *f = FPUSTATE;
+
+ if(tstate & TSTATE_PRIV)
+ die_if_kernel("stdfmna from kernel", regs);
+ if(current->tss.flags & SPARC_FLAG_32BIT)
+ pc = (u32)pc;
+ if (get_user(insn, (u32 *)pc) != -EFAULT) {
+ freg = ((insn >> 25) & 0x1e) | ((insn >> 20) & 0x20);
+ asi = sfsr >> 16;
+ value = 0;
+ flag = (freg < 32) ? SPARC_FLAG_USEDFPUL : SPARC_FLAG_USEDFPUU;
+ if (asi > ASI_SNFL)
+ goto daex;
+ if (current->tss.flags & flag)
+ value = *(u64 *)&f->regs[freg];
+ switch (asi) {
+ case ASI_P:
+ case ASI_S: break;
+ case ASI_PL:
+ case ASI_SL:
+ value = __swab64p(&value); break;
+ default: goto daex;
+ }
+ if (put_user (value >> 32, (u32 *)sfar) ||
+ __put_user ((u32)value, (u32 *)(sfar + 4)))
+ goto daex;
+ } else {
+daex: data_access_exception(regs);
+ return;
+ }
+ advance(regs);
+ return;
}
-#endif
FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen, slshen@lbl.gov