1 /*
2 * Software emulation of some PPC instructions for the 8xx core.
3 *
4 * Copyright (C) 1998 Dan Malek (dmalek@jlc.net)
5 *
6 * Software floating emuation for the MPC8xx processor. I did this mostly
7 * because it was easier than trying to get the libraries compiled for
8 * software floating point. The goal is still to get the libraries done,
9 * but I lost patience and needed some hacks to at least get init and
10 * shells running. The first problem is the setjmp/longjmp that save
11 * and restore the floating point registers.
12 *
13 * For this emulation, our working registers are found on the register
14 * save area.
15 */
16
17 #include <linux/errno.h>
18 #include <linux/sched.h>
19 #include <linux/kernel.h>
20 #include <linux/mm.h>
21 #include <linux/stddef.h>
22 #include <linux/unistd.h>
23 #include <linux/ptrace.h>
24 #include <linux/user.h>
25 #include <linux/interrupt.h>
26
27 #include <asm/pgtable.h>
28 #include <asm/uaccess.h>
29 #include <asm/io.h>
30
31 /* Eventually we may need a look-up table, but this works for now.
32 */
33 #define LFS 48
34 #define LFD 50
35 #define LFDU 51
36 #define STFD 54
37 #define STFDU 55
38 #define FMR 63
39
print_8xx_pte(struct mm_struct * mm,unsigned long addr)40 void print_8xx_pte(struct mm_struct *mm, unsigned long addr)
41 {
42 pgd_t *pgd;
43 pmd_t *pmd;
44 pte_t *pte;
45
46 printk(" pte @ 0x%8lx: ", addr);
47 pgd = pgd_offset(mm, addr & PAGE_MASK);
48 if (pgd) {
49 pmd = pmd_offset(pud_offset(pgd, addr & PAGE_MASK),
50 addr & PAGE_MASK);
51 if (pmd && pmd_present(*pmd)) {
52 pte = pte_offset_kernel(pmd, addr & PAGE_MASK);
53 if (pte) {
54 printk(" (0x%08lx)->(0x%08lx)->0x%08lx\n",
55 (long)pgd, (long)pte, (long)pte_val(*pte));
56 #define pp ((long)pte_val(*pte))
57 printk(" RPN: %05lx PP: %lx SPS: %lx SH: %lx "
58 "CI: %lx v: %lx\n",
59 pp>>12, /* rpn */
60 (pp>>10)&3, /* pp */
61 (pp>>3)&1, /* small */
62 (pp>>2)&1, /* shared */
63 (pp>>1)&1, /* cache inhibit */
64 pp&1 /* valid */
65 );
66 #undef pp
67 }
68 else {
69 printk("no pte\n");
70 }
71 }
72 else {
73 printk("no pmd\n");
74 }
75 }
76 else {
77 printk("no pgd\n");
78 }
79 }
80
get_8xx_pte(struct mm_struct * mm,unsigned long addr)81 int get_8xx_pte(struct mm_struct *mm, unsigned long addr)
82 {
83 pgd_t *pgd;
84 pmd_t *pmd;
85 pte_t *pte;
86 int retval = 0;
87
88 pgd = pgd_offset(mm, addr & PAGE_MASK);
89 if (pgd) {
90 pmd = pmd_offset(pud_offset(pgd, addr & PAGE_MASK),
91 addr & PAGE_MASK);
92 if (pmd && pmd_present(*pmd)) {
93 pte = pte_offset_kernel(pmd, addr & PAGE_MASK);
94 if (pte) {
95 retval = (int)pte_val(*pte);
96 }
97 }
98 }
99 return retval;
100 }
101
102 /*
103 * We return 0 on success, 1 on unimplemented instruction, and EFAULT
104 * if a load/store faulted.
105 */
Soft_emulate_8xx(struct pt_regs * regs)106 int Soft_emulate_8xx(struct pt_regs *regs)
107 {
108 u32 inst, instword;
109 u32 flreg, idxreg, disp;
110 int retval;
111 s16 sdisp;
112 u32 *ea, *ip;
113
114 retval = 0;
115
116 instword = *((u32 *)regs->nip);
117 inst = instword >> 26;
118
119 flreg = (instword >> 21) & 0x1f;
120 idxreg = (instword >> 16) & 0x1f;
121 disp = instword & 0xffff;
122
123 ea = (u32 *)(regs->gpr[idxreg] + disp);
124 ip = (u32 *)¤t->thread.TS_FPR(flreg);
125
126 switch ( inst )
127 {
128 case LFD:
129 /* this is a 16 bit quantity that is sign extended
130 * so use a signed short here -- Cort
131 */
132 sdisp = (instword & 0xffff);
133 ea = (u32 *)(regs->gpr[idxreg] + sdisp);
134 if (copy_from_user(ip, ea, sizeof(double)))
135 retval = -EFAULT;
136 break;
137
138 case LFDU:
139 if (copy_from_user(ip, ea, sizeof(double)))
140 retval = -EFAULT;
141 else
142 regs->gpr[idxreg] = (u32)ea;
143 break;
144 case LFS:
145 sdisp = (instword & 0xffff);
146 ea = (u32 *)(regs->gpr[idxreg] + sdisp);
147 if (copy_from_user(ip, ea, sizeof(float)))
148 retval = -EFAULT;
149 break;
150 case STFD:
151 /* this is a 16 bit quantity that is sign extended
152 * so use a signed short here -- Cort
153 */
154 sdisp = (instword & 0xffff);
155 ea = (u32 *)(regs->gpr[idxreg] + sdisp);
156 if (copy_to_user(ea, ip, sizeof(double)))
157 retval = -EFAULT;
158 break;
159
160 case STFDU:
161 if (copy_to_user(ea, ip, sizeof(double)))
162 retval = -EFAULT;
163 else
164 regs->gpr[idxreg] = (u32)ea;
165 break;
166 case FMR:
167 /* assume this is a fp move -- Cort */
168 memcpy(ip, ¤t->thread.TS_FPR((instword>>11)&0x1f),
169 sizeof(double));
170 break;
171 default:
172 retval = 1;
173 printk("Bad emulation %s/%d\n"
174 " NIP: %08lx instruction: %08x opcode: %x "
175 "A: %x B: %x C: %x code: %x rc: %x\n",
176 current->comm,current->pid,
177 regs->nip,
178 instword,inst,
179 (instword>>16)&0x1f,
180 (instword>>11)&0x1f,
181 (instword>>6)&0x1f,
182 (instword>>1)&0x3ff,
183 instword&1);
184 {
185 int pa;
186 print_8xx_pte(current->mm,regs->nip);
187 pa = get_8xx_pte(current->mm,regs->nip) & PAGE_MASK;
188 pa |= (regs->nip & ~PAGE_MASK);
189 pa = (unsigned long)__va(pa);
190 printk("Kernel VA for NIP %x ", pa);
191 print_8xx_pte(current->mm,pa);
192 }
193 }
194
195 if (retval == 0)
196 regs->nip += 4;
197
198 return retval;
199 }
200