powerpc/vdso: Remove redundant locking in update_vsyscall_tz()
[cascardo/linux.git] / arch / powerpc / kernel / softemu8xx.c
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
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
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  */
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 *)&current->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, &current->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 }