Linux Kernel  3.7.1
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
softemu8xx.c
Go to the documentation of this file.
1 /*
2  * Software emulation of some PPC instructions for the 8xx core.
3  *
4  * Copyright (C) 1998 Dan Malek ([email protected])
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  */
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 }