]> git.lizzy.rs Git - plan9front.git/blob - sys/src/9/bcm/vfp3.c
add raspberry pi kernel (from sources)
[plan9front.git] / sys / src / 9 / bcm / vfp3.c
1 /*
2  * VFPv2 or VFPv3 floating point unit
3  */
4 #include "u.h"
5 #include "../port/lib.h"
6 #include "mem.h"
7 #include "dat.h"
8 #include "fns.h"
9 #include "ureg.h"
10 #include "arm.h"
11
12 /* subarchitecture code in m->havefp */
13 enum {
14         VFPv2   = 2,
15         VFPv3   = 3,
16 };
17
18 /* fp control regs.  most are read-only */
19 enum {
20         Fpsid = 0,
21         Fpscr = 1,                      /* rw */
22         Mvfr1 = 6,
23         Mvfr0 = 7,
24         Fpexc = 8,                      /* rw */
25         Fpinst= 9,                      /* optional, for exceptions */
26         Fpinst2=10,
27 };
28 enum {
29         /* Fpexc bits */
30         Fpex =          1u << 31,
31         Fpenabled =     1 << 30,
32         Fpdex =         1 << 29,        /* defined synch exception */
33 //      Fp2v =          1 << 28,        /* Fpinst2 reg is valid */
34 //      Fpvv =          1 << 27,        /* if Fpdex, vecitr is valid */
35 //      Fptfv =         1 << 26,        /* trapped fault is valid */
36 //      Fpvecitr =      MASK(3) << 8,
37         /* FSR bits appear here */
38         Fpmbc =         Fpdex,          /* bits exception handler must clear */
39
40         /* Fpscr bits; see u.h for more */
41         Stride =        MASK(2) << 20,
42         Len =           MASK(3) << 16,
43         Dn=             1 << 25,
44         Fz=             1 << 24,
45         /* trap exception enables (not allowed in vfp3) */
46         FPIDNRM =       1 << 15,        /* input denormal */
47         Alltraps = FPIDNRM | FPINEX | FPUNFL | FPOVFL | FPZDIV | FPINVAL,
48         /* pending exceptions */
49         FPAIDNRM =      1 << 7,         /* input denormal */
50         Allexc = FPAIDNRM | FPAINEX | FPAUNFL | FPAOVFL | FPAZDIV | FPAINVAL,
51         /* condition codes */
52         Allcc =         MASK(4) << 28,
53 };
54 enum {
55         /* CpCPaccess bits */
56         Cpaccnosimd =   1u << 31,
57         Cpaccd16 =      1 << 30,
58 };
59
60 static char *
61 subarch(int impl, uint sa)
62 {
63         static char *armarchs[] = {
64                 "VFPv1 (unsupported)",
65                 "VFPv2",
66                 "VFPv3+ with common VFP subarch v2",
67                 "VFPv3+ with null subarch",
68                 "VFPv3+ with common VFP subarch v3",
69         };
70
71         if (impl != 'A' || sa >= nelem(armarchs))
72                 return "GOK";
73         else
74                 return armarchs[sa];
75 }
76
77 static char *
78 implement(uchar impl)
79 {
80         if (impl == 'A')
81                 return "arm";
82         else
83                 return "unknown";
84 }
85
86 static int
87 havefp(void)
88 {
89         int gotfp;
90         ulong acc, sid;
91
92         if (m->havefpvalid)
93                 return m->havefp;
94
95         m->havefp = 0;
96         gotfp = 1 << CpFP | 1 << CpDFP;
97         cpwrsc(0, CpCONTROL, 0, CpCPaccess, MASK(28));
98         acc = cprdsc(0, CpCONTROL, 0, CpCPaccess);
99         if ((acc & (MASK(2) << (2*CpFP))) == 0) {
100                 gotfp &= ~(1 << CpFP);
101                 print("fpon: no single FP coprocessor\n");
102         }
103         if ((acc & (MASK(2) << (2*CpDFP))) == 0) {
104                 gotfp &= ~(1 << CpDFP);
105                 print("fpon: no double FP coprocessor\n");
106         }
107         if (!gotfp) {
108                 print("fpon: no FP coprocessors\n");
109                 m->havefpvalid = 1;
110                 return 0;
111         }
112         m->fpon = 1;                    /* don't panic */
113         sid = fprd(Fpsid);
114         m->fpon = 0;
115         switch((sid >> 16) & MASK(7)){
116         case 0:                         /* VFPv1 */
117                 break;
118         case 1:                         /* VFPv2 */
119                 m->havefp = VFPv2;
120                 m->fpnregs = 16;
121                 break;
122         default:                        /* VFPv3 or later */
123                 m->havefp = VFPv3;
124                 m->fpnregs = (acc & Cpaccd16) ? 16 : 32;
125                 break;
126         }
127         if (m->machno == 0)
128                 print("fp: %d registers, %s simd\n", m->fpnregs,
129                         (acc & Cpaccnosimd? " no": ""));
130         m->havefpvalid = 1;
131         return 1;
132 }
133
134 /*
135  * these can be called to turn the fpu on or off for user procs,
136  * not just at system start up or shutdown.
137  */
138
139 void
140 fpoff(void)
141 {
142         if (m->fpon) {
143                 fpwr(Fpexc, 0);
144                 m->fpon = 0;
145         }
146 }
147
148 void
149 fpononly(void)
150 {
151         if (!m->fpon && havefp()) {
152                 /* enable fp.  must be first operation on the FPUs. */
153                 fpwr(Fpexc, Fpenabled);
154                 m->fpon = 1;
155         }
156 }
157
158 static void
159 fpcfg(void)
160 {
161         int impl;
162         ulong sid;
163         static int printed;
164
165         /* clear pending exceptions; no traps in vfp3; all v7 ops are scalar */
166         m->fpscr = Dn | Fz | FPRNR | (FPINVAL | FPZDIV | FPOVFL) & ~Alltraps;
167         fpwr(Fpscr, m->fpscr);
168         m->fpconfiged = 1;
169
170         if (printed)
171                 return;
172         sid = fprd(Fpsid);
173         impl = sid >> 24;
174         print("fp: %s arch %s; rev %ld\n", implement(impl),
175                 subarch(impl, (sid >> 16) & MASK(7)), sid & MASK(4));
176         printed = 1;
177 }
178
179 void
180 fpinit(void)
181 {
182         if (havefp()) {
183                 fpononly();
184                 fpcfg();
185         }
186 }
187
188 void
189 fpon(void)
190 {
191         if (havefp()) {
192                 fpononly();
193                 if (m->fpconfiged)
194                         fpwr(Fpscr, (fprd(Fpscr) & Allcc) | m->fpscr);
195                 else
196                         fpcfg();        /* 1st time on this fpu; configure it */
197         }
198 }
199
200 void
201 fpclear(void)
202 {
203 //      ulong scr;
204
205         fpon();
206 //      scr = fprd(Fpscr);
207 //      m->fpscr = scr & ~Allexc;
208 //      fpwr(Fpscr, m->fpscr);
209
210         fpwr(Fpexc, fprd(Fpexc) & ~Fpmbc);
211 }
212
213
214 /*
215  * Called when a note is about to be delivered to a
216  * user process, usually at the end of a system call.
217  * Note handlers are not allowed to use the FPU so
218  * the state is marked (after saving if necessary) and
219  * checked in the Device Not Available handler.
220  */
221 void
222 fpunotify(Ureg*)
223 {
224         if(up->fpstate == FPactive){
225                 fpsave(&up->fpsave);
226                 up->fpstate = FPinactive;
227         }
228         up->fpstate |= FPillegal;
229 }
230
231 /*
232  * Called from sysnoted() via the machine-dependent
233  * noted() routine.
234  * Clear the flag set above in fpunotify().
235  */
236 void
237 fpunoted(void)
238 {
239         up->fpstate &= ~FPillegal;
240 }
241
242 /*
243  * Called early in the non-interruptible path of
244  * sysrfork() via the machine-dependent syscall() routine.
245  * Save the state so that it can be easily copied
246  * to the child process later.
247  */
248 void
249 fpusysrfork(Ureg*)
250 {
251         if(up->fpstate == FPactive){
252                 fpsave(&up->fpsave);
253                 up->fpstate = FPinactive;
254         }
255 }
256
257 /*
258  * Called later in sysrfork() via the machine-dependent
259  * sysrforkchild() routine.
260  * Copy the parent FPU state to the child.
261  */
262 void
263 fpusysrforkchild(Proc *p, Ureg *, Proc *up)
264 {
265         /* don't penalize the child, it hasn't done FP in a note handler. */
266         p->fpstate = up->fpstate & ~FPillegal;
267 }
268
269 /* should only be called if p->fpstate == FPactive */
270 void
271 fpsave(FPsave *fps)
272 {
273         int n;
274
275         fpon();
276         fps->control = fps->status = fprd(Fpscr);
277         assert(m->fpnregs);
278         for (n = 0; n < m->fpnregs; n++)
279                 fpsavereg(n, (uvlong *)fps->regs[n]);
280         fpoff();
281 }
282
283 static void
284 fprestore(Proc *p)
285 {
286         int n;
287
288         fpon();
289         fpwr(Fpscr, p->fpsave.control);
290         m->fpscr = fprd(Fpscr) & ~Allcc;
291         assert(m->fpnregs);
292         for (n = 0; n < m->fpnregs; n++)
293                 fprestreg(n, *(uvlong *)p->fpsave.regs[n]);
294 }
295
296 /*
297  * Called from sched() and sleep() via the machine-dependent
298  * procsave() routine.
299  * About to go in to the scheduler.
300  * If the process wasn't using the FPU
301  * there's nothing to do.
302  */
303 void
304 fpuprocsave(Proc *p)
305 {
306         if(p->fpstate == FPactive){
307                 if(p->state == Moribund)
308                         fpclear();
309                 else{
310                         /*
311                          * Fpsave() stores without handling pending
312                          * unmasked exeptions. Postnote() can't be called
313                          * here as sleep() already has up->rlock, so
314                          * the handling of pending exceptions is delayed
315                          * until the process runs again and generates an
316                          * emulation fault to activate the FPU.
317                          */
318                         fpsave(&p->fpsave);
319                 }
320                 p->fpstate = FPinactive;
321         }
322 }
323
324 /*
325  * The process has been rescheduled and is about to run.
326  * Nothing to do here right now. If the process tries to use
327  * the FPU again it will cause a Device Not Available
328  * exception and the state will then be restored.
329  */
330 void
331 fpuprocrestore(Proc *)
332 {
333 }
334
335 /*
336  * Disable the FPU.
337  * Called from sysexec() via sysprocsetup() to
338  * set the FPU for the new process.
339  */
340 void
341 fpusysprocsetup(Proc *p)
342 {
343         p->fpstate = FPinit;
344         fpoff();
345 }
346
347 static void
348 mathnote(void)
349 {
350         ulong status;
351         char *msg, note[ERRMAX];
352
353         status = up->fpsave.status;
354
355         /*
356          * Some attention should probably be paid here to the
357          * exception masks and error summary.
358          */
359         if (status & FPAINEX)
360                 msg = "inexact";
361         else if (status & FPAOVFL)
362                 msg = "overflow";
363         else if (status & FPAUNFL)
364                 msg = "underflow";
365         else if (status & FPAZDIV)
366                 msg = "divide by zero";
367         else if (status & FPAINVAL)
368                 msg = "bad operation";
369         else
370                 msg = "spurious";
371         snprint(note, sizeof note, "sys: fp: %s fppc=%#p status=%#lux",
372                 msg, up->fpsave.pc, status);
373         postnote(up, 1, note, NDebug);
374 }
375
376 static void
377 mathemu(Ureg *)
378 {
379         if(m->havefp == VFPv3 && !(fprd(Fpexc) & (Fpex|Fpdex)))
380                 iprint("mathemu: not an FP exception but an unknown FP opcode\n");
381         switch(up->fpstate){
382         case FPemu:
383                 error("illegal instruction: VFP opcode in emulated mode");
384         case FPinit:
385                 fpinit();
386                 up->fpstate = FPactive;
387                 break;
388         case FPinactive:
389                 /*
390                  * Before restoring the state, check for any pending
391                  * exceptions.  There's no way to restore the state without
392                  * generating an unmasked exception.
393                  * More attention should probably be paid here to the
394                  * exception masks and error summary.
395                  */
396                 if(up->fpsave.status & (FPAINEX|FPAUNFL|FPAOVFL|FPAZDIV|FPAINVAL)){
397                         mathnote();
398                         break;
399                 }
400                 fprestore(up);
401                 up->fpstate = FPactive;
402                 break;
403         case FPactive:
404                 error("illegal instruction: bad vfp fpu opcode");
405                 break;
406         }
407         fpclear();
408 }
409
410 void
411 fpstuck(uintptr pc)
412 {
413         if (m->fppc == pc && m->fppid == up->pid) {
414                 m->fpcnt++;
415                 if (m->fpcnt > 4)
416                         panic("fpuemu: cpu%d stuck at pid %ld %s pc %#p "
417                                 "instr %#8.8lux", m->machno, up->pid, up->text,
418                                 pc, *(ulong *)pc);
419         } else {
420                 m->fppid = up->pid;
421                 m->fppc = pc;
422                 m->fpcnt = 0;
423         }
424 }
425
426 enum {
427         N = 1<<31,
428         Z = 1<<30,
429         C = 1<<29,
430         V = 1<<28,
431         REGPC = 15,
432 };
433
434 static int
435 condok(int cc, int c)
436 {
437         switch(c){
438         case 0: /* Z set */
439                 return cc&Z;
440         case 1: /* Z clear */
441                 return (cc&Z) == 0;
442         case 2: /* C set */
443                 return cc&C;
444         case 3: /* C clear */
445                 return (cc&C) == 0;
446         case 4: /* N set */
447                 return cc&N;
448         case 5: /* N clear */
449                 return (cc&N) == 0;
450         case 6: /* V set */
451                 return cc&V;
452         case 7: /* V clear */
453                 return (cc&V) == 0;
454         case 8: /* C set and Z clear */
455                 return cc&C && (cc&Z) == 0;
456         case 9: /* C clear or Z set */
457                 return (cc&C) == 0 || cc&Z;
458         case 10:        /* N set and V set, or N clear and V clear */
459                 return (~cc&(N|V))==0 || (cc&(N|V)) == 0;
460         case 11:        /* N set and V clear, or N clear and V set */
461                 return (cc&(N|V))==N || (cc&(N|V))==V;
462         case 12:        /* Z clear, and either N set and V set or N clear and V clear */
463                 return (cc&Z) == 0 && ((~cc&(N|V))==0 || (cc&(N|V))==0);
464         case 13:        /* Z set, or N set and V clear or N clear and V set */
465                 return (cc&Z) || (cc&(N|V))==N || (cc&(N|V))==V;
466         case 14:        /* always */
467                 return 1;
468         case 15:        /* never (reserved) */
469                 return 0;
470         }
471         return 0;       /* not reached */
472 }
473
474 /* only called to deal with user-mode instruction faults */
475 int
476 fpuemu(Ureg* ureg)
477 {
478         int s, nfp, cop, op;
479         uintptr pc;
480
481         if(waserror()){
482                 postnote(up, 1, up->errstr, NDebug);
483                 return 1;
484         }
485
486         if(up->fpstate & FPillegal)
487                 error("floating point in note handler");
488
489         nfp = 0;
490         pc = ureg->pc;
491         validaddr(pc, 4, 0);
492         if(!condok(ureg->psr, *(ulong*)pc >> 28))
493                 iprint("fpuemu: conditional instr shouldn't have got here\n");
494         op  = (*(ulong *)pc >> 24) & MASK(4);
495         cop = (*(ulong *)pc >>  8) & MASK(4);
496         if(m->fpon)
497                 fpstuck(pc);            /* debugging; could move down 1 line */
498         if (ISFPAOP(cop, op)) {         /* old arm 7500 fpa opcode? */
499 //              iprint("fpuemu: fpa instr %#8.8lux at %#p\n", *(ulong *)pc, pc);
500 //              error("illegal instruction: old arm 7500 fpa opcode");
501                 s = spllo();
502                 if(waserror()){
503                         splx(s);
504                         nexterror();
505                 }
506                 nfp = fpiarm(ureg);     /* advances pc past emulated instr(s) */
507                 if (nfp > 1)            /* could adjust this threshold */
508                         m->fppc = m->fpcnt = 0;
509                 splx(s);
510                 poperror();
511         } else if (ISVFPOP(cop, op)) {  /* if vfp, fpu must be off */
512                 mathemu(ureg);          /* enable fpu & retry */
513                 nfp = 1;
514         }
515
516         poperror();
517         return nfp;
518 }