vfp3.c 10 KB

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