1/*
2 * BK Id: SCCS/s.softemu8xx.c 1.8 05/17/01 18:14:22 cort
3 */
4/*
5 * Software emulation of some PPC instructions for the 8xx core.
6 *
7 * Copyright (C) 1998 Dan Malek (dmalek@jlc.net)
8 *
9 * Software floating emuation for the MPC8xx processor.  I did this mostly
10 * because it was easier than trying to get the libraries compiled for
11 * software floating point.  The goal is still to get the libraries done,
12 * but I lost patience and needed some hacks to at least get init and
13 * shells running.  The first problem is the setjmp/longjmp that save
14 * and restore the floating point registers.
15 *
16 * For this emulation, our working registers are found on the register
17 * save area.
18 */
19
20#include <linux/errno.h>
21#include <linux/sched.h>
22#include <linux/kernel.h>
23#include <linux/mm.h>
24#include <linux/stddef.h>
25#include <linux/unistd.h>
26#include <linux/ptrace.h>
27#include <linux/slab.h>
28#include <linux/user.h>
29#include <linux/a.out.h>
30#include <linux/interrupt.h>
31
32#include <asm/pgtable.h>
33#include <asm/uaccess.h>
34#include <asm/system.h>
35#include <asm/io.h>
36#include <asm/processor.h>
37
38/* Eventually we may need a look-up table, but this works for now.
39*/
40#define LFS	48
41#define LFD	50
42#define LFDU	51
43#define STFD	54
44#define STFDU	55
45#define FMR	63
46
47/*
48 * We return 0 on success, 1 on unimplemented instruction, and EFAULT
49 * if a load/store faulted.
50 */
51int
52Soft_emulate_8xx(struct pt_regs *regs)
53{
54	uint	inst, instword;
55	uint	flreg, idxreg, disp;
56	uint	retval;
57	signed short sdisp;
58	uint	*ea, *ip;
59
60	retval = 0;
61
62	instword = *((uint *)regs->nip);
63	inst = instword >> 26;
64
65	flreg = (instword >> 21) & 0x1f;
66	idxreg = (instword >> 16) & 0x1f;
67	disp = instword & 0xffff;
68
69	ea = (uint *)(regs->gpr[idxreg] + disp);
70	ip = (uint *)&current->thread.fpr[flreg];
71
72	switch ( inst )
73	{
74	case LFD:
75		/* this is a 16 bit quantity that is sign extended
76		 * so use a signed short here -- Cort
77		 */
78		sdisp = (instword & 0xffff);
79		ea = (uint *)(regs->gpr[idxreg] + sdisp);
80		if (copy_from_user(ip, ea, sizeof(double)))
81			retval = -EFAULT;
82		break;
83
84	case LFDU:
85		if (copy_from_user(ip, ea, sizeof(double)))
86			retval = -EFAULT;
87		else
88			regs->gpr[idxreg] = (uint)ea;
89		break;
90	case LFS:
91		sdisp = (instword & 0xffff);
92		ea = (uint *)(regs->gpr[idxreg] + sdisp);
93		if (copy_from_user(ip, ea, sizeof(float)))
94			retval = -EFAULT;
95		break;
96	case STFD:
97		/* this is a 16 bit quantity that is sign extended
98		 * so use a signed short here -- Cort
99		 */
100		sdisp = (instword & 0xffff);
101		ea = (uint *)(regs->gpr[idxreg] + sdisp);
102		if (copy_to_user(ea, ip, sizeof(double)))
103			retval = -EFAULT;
104		break;
105
106	case STFDU:
107		if (copy_to_user(ea, ip, sizeof(double)))
108			retval = -EFAULT;
109		else
110			regs->gpr[idxreg] = (uint)ea;
111		break;
112	case FMR:
113		/* assume this is a fp move -- Cort */
114		memcpy( ip, &current->thread.fpr[(instword>>11)&0x1f],
115			sizeof(double) );
116		break;
117	default:
118		retval = 1;
119		printk("Bad emulation %s/%d\n"
120		       " NIP: %08x instruction: %08x opcode: %x "
121		       "A: %x B: %x C: %x code: %x rc: %x\n",
122		       current->comm,current->pid,
123		       regs->nip,
124		       instword,inst,
125		       (instword>>16)&0x1f,
126		       (instword>>11)&0x1f,
127		       (instword>>6)&0x1f,
128		       (instword>>1)&0x3ff,
129		       instword&1);
130		{
131			int pa;
132			print_8xx_pte(current->mm,regs->nip);
133			pa = get_8xx_pte(current->mm,regs->nip) & PAGE_MASK;
134			pa |= (regs->nip & ~PAGE_MASK);
135			pa = __va(pa);
136			printk("Kernel VA for NIP %x ", pa);
137			print_8xx_pte(current->mm,pa);
138		}
139
140	}
141
142	if (retval == 0)
143		regs->nip += 4;
144	return(retval);
145}
146
147