Subversion Repositories planix.SVN

Rev

Details | Last modification | View Log | RSS feed

Rev Author Line No. Line
2 - 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
	switch(up->fpstate){
380
	case FPemu:
381
		error("illegal instruction: VFP opcode in emulated mode");
382
	case FPinit:
383
		fpinit();
384
		up->fpstate = FPactive;
385
		break;
386
	case FPinactive:
387
		/*
388
		 * Before restoring the state, check for any pending
389
		 * exceptions.  There's no way to restore the state without
390
		 * generating an unmasked exception.
391
		 * More attention should probably be paid here to the
392
		 * exception masks and error summary.
393
		 */
394
		if(up->fpsave.status & (FPAINEX|FPAUNFL|FPAOVFL|FPAZDIV|FPAINVAL)){
395
			mathnote();
396
			break;
397
		}
398
		fprestore(up);
399
		up->fpstate = FPactive;
400
		break;
401
	case FPactive:
402
		error("illegal instruction: bad vfp fpu opcode");
403
		break;
404
	}
405
	fpclear();
406
}
407
 
408
void
409
fpstuck(uintptr pc)
410
{
411
	if (m->fppc == pc && m->fppid == up->pid) {
412
		m->fpcnt++;
413
		if (m->fpcnt > 4)
414
			panic("fpuemu: cpu%d stuck at pid %ld %s pc %#p "
415
				"instr %#8.8lux", m->machno, up->pid, up->text,
416
				pc, *(ulong *)pc);
417
	} else {
418
		m->fppid = up->pid;
419
		m->fppc = pc;
420
		m->fpcnt = 0;
421
	}
422
}
423
 
424
enum {
425
	N = 1<<31,
426
	Z = 1<<30,
427
	C = 1<<29,
428
	V = 1<<28,
429
	REGPC = 15,
430
};
431
 
432
static int
433
condok(int cc, int c)
434
{
435
	switch(c){
436
	case 0:	/* Z set */
437
		return cc&Z;
438
	case 1:	/* Z clear */
439
		return (cc&Z) == 0;
440
	case 2:	/* C set */
441
		return cc&C;
442
	case 3:	/* C clear */
443
		return (cc&C) == 0;
444
	case 4:	/* N set */
445
		return cc&N;
446
	case 5:	/* N clear */
447
		return (cc&N) == 0;
448
	case 6:	/* V set */
449
		return cc&V;
450
	case 7:	/* V clear */
451
		return (cc&V) == 0;
452
	case 8:	/* C set and Z clear */
453
		return cc&C && (cc&Z) == 0;
454
	case 9:	/* C clear or Z set */
455
		return (cc&C) == 0 || cc&Z;
456
	case 10:	/* N set and V set, or N clear and V clear */
457
		return (~cc&(N|V))==0 || (cc&(N|V)) == 0;
458
	case 11:	/* N set and V clear, or N clear and V set */
459
		return (cc&(N|V))==N || (cc&(N|V))==V;
460
	case 12:	/* Z clear, and either N set and V set or N clear and V clear */
461
		return (cc&Z) == 0 && ((~cc&(N|V))==0 || (cc&(N|V))==0);
462
	case 13:	/* Z set, or N set and V clear or N clear and V set */
463
		return (cc&Z) || (cc&(N|V))==N || (cc&(N|V))==V;
464
	case 14:	/* always */
465
		return 1;
466
	case 15:	/* never (reserved) */
467
		return 0;
468
	}
469
	return 0;	/* not reached */
470
}
471
 
472
/* only called to deal with user-mode instruction faults */
473
int
474
fpuemu(Ureg* ureg)
475
{
476
	int s, nfp, cop, op;
477
	uintptr pc;
478
 
479
	if(waserror()){
480
		postnote(up, 1, up->errstr, NDebug);
481
		return 1;
482
	}
483
 
484
	if(up->fpstate & FPillegal)
485
		error("floating point in note handler");
486
 
487
	nfp = 0;
488
	pc = ureg->pc;
489
	validaddr(pc, 4, 0);
490
	if(!condok(ureg->psr, *(ulong*)pc >> 28))
491
		iprint("fpuemu: conditional instr shouldn't have got here\n");
492
	op  = (*(ulong *)pc >> 24) & MASK(4);
493
	cop = (*(ulong *)pc >>  8) & MASK(4);
494
	if(m->fpon)
495
		fpstuck(pc);		/* debugging; could move down 1 line */
496
	if (ISFPAOP(cop, op)) {		/* old arm 7500 fpa opcode? */
497
//		iprint("fpuemu: fpa instr %#8.8lux at %#p\n", *(ulong *)pc, pc);
498
//		error("illegal instruction: old arm 7500 fpa opcode");
499
		s = spllo();
500
		if(waserror()){
501
			splx(s);
502
			nexterror();
503
		}
504
		nfp = fpiarm(ureg);	/* advances pc past emulated instr(s) */
505
		if (nfp > 1)		/* could adjust this threshold */
506
			m->fppc = m->fpcnt = 0;
507
		splx(s);
508
		poperror();
509
	} else if (ISVFPOP(cop, op)) {	/* if vfp, fpu must be off */
510
		mathemu(ureg);		/* enable fpu & retry */
511
		nfp = 1;
512
	}
513
 
514
	poperror();
515
	return nfp;
516
}