1/*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
7 *
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
12 *
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 *
19 * CDDL HEADER END
20 */
21
22/*
23 * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
24 * Use is subject to license terms.
25 */
26
27#pragma ident	"%Z%%M%	%I%	%E% SMI"
28
29#ifdef __lint
30#pragma error_messages(off, E_VALUE_TYPE)
31#endif
32
33#include <stdlib.h>
34#include <unistd.h>
35#include <fp.h>
36#include <fps_ereport.h>
37
38#define	EXPECTED	1.9999999999999998E+00
39
40static void fdivd(double *f22, double *f2, double *f12);
41static void fmuld(double *x, double *y, double *z, double *z1);
42static void fmulx(uint64_t *rs1, uint64_t *rs2, uint64_t *rd);
43int fpu_fdivd(int rloop, struct fps_test_ereport *report);
44int fpu_fmuld(int rloop, struct fps_test_ereport *report);
45int fpu_fmulx(int rloop, struct fps_test_ereport *report);
46
47#ifdef V9B
48
49/* Lint doesn't recognize .il files where these are defined */
50#ifdef __lint
51
52unsigned long fcmpgt16(double in1, double in2);
53unsigned long fcmpne16(double in1, double in2);
54unsigned long setgsr(unsigned long);
55
56#else
57
58extern float fpackfix(double num);
59extern unsigned long fcmpgt16(double in1, double in2);
60extern unsigned long fcmpne16(double in1, double in2);
61extern unsigned long setgsr(unsigned long);
62
63#endif
64
65int align_data(int loop,
66    struct fps_test_ereport *report);
67int vis_test(struct fps_test_ereport *report);
68static int align_error_create(char *err, uint32_t start, uint32_t offest,
69    int loop, uint32_t count);
70static int do_aligndata(uchar_t *from, uint32_t *offset, size_t sz,
71    uchar_t *f0, uchar_t *f2, uint32_t bmask);
72static int visgt16(struct fps_test_ereport *report);
73static int visne16(struct fps_test_ereport *report);
74static int vispackfix(struct fps_test_ereport *report);
75
76#endif
77
78
79/*
80 * fpu_fdivd(int rloop, int unit, struct fps_test_ereport *report)
81 * returns whether the correct value is calculated each time
82 * rloop times. If an error is found, the relevant data is stored
83 * in report. The test uses internally generated random double
84 * precision within a certain range to conduct the following test:
85 *
86 * (a * 2^1022) / ((a+e) * 2^1021)
87 *
88 * which is guaranteed to fill the resulting mantissa with all ones.
89 *
90 */
91int
92fpu_fdivd(int rloop, struct fps_test_ereport *report)
93{
94
95	char err_data[MAX_INFO_SIZE];
96	double expect_ans = EXPECTED;
97	double f12 = 0;
98	double f2;
99	double f22;
100	int loop = 0;
101	uint64_t expect;
102	uint64_t observe;
103
104	srand48(1L);
105
106	while (loop < rloop) {
107		loop++;
108
109		*(uint32_t *)& f22 = mrand48();
110		*(uint32_t *)& f22 &= 0x80069fff;
111		*(uint32_t *)& f22 |= 0x7fd69f00;
112
113#ifdef __lint
114		(void) f22;
115#endif
116
117		*((uint32_t *)& f22 + 1) = mrand48();
118		*((uint32_t *)& f22 + 1) |= 0x00000001;
119
120		*(uint64_t *)& f2 = *(uint64_t *)& f22 + 1;
121		*(uint32_t *)& f2 &= 0x800FFFFF;
122		*(uint32_t *)& f2 |= 0x7FC00000;
123#ifdef __lint
124		(void) f2;
125#endif
126
127		fdivd(&f22, &f2, &f12);
128
129		if (f12 != expect_ans) {
130			(void) snprintf(err_data, sizeof (err_data),
131			    "\nExpected: %.16e,\nObserved: %.16e",
132			    expect_ans, f12);
133			expect = *(uint64_t *)&expect_ans;
134			observe = *(uint64_t *)&f12;
135			setup_fps_test_struct(IS_EREPORT_INFO, report,
136			    6340, &observe, &expect, 1, 1, err_data);
137
138			return (-1);
139		}
140	}
141
142	return (0);
143}
144
145/*
146 * fdivd(uint64_t *rs1, uint64_t *rs2, uint64_t *rd)
147 * performs the assembly level instructions for
148 * fpu_fdivd.
149 */
150/* ARGSUSED */
151static void
152fdivd(double *f22, double *f2, double *f12)
153{
154	asm("ldd	[%i0], %f22");
155	asm("ldd	[%i1], %f2");
156	asm("fdivd   	%f22, %f2, %f12");
157	asm("std	%f12,[%i2]");
158	asm("membar #Sync");
159}
160
161/*
162 * fpu_fmuld(int rloop, int unit, struct fps_test_ereport *report)
163 * returns whether the correct value is calculated each time
164 * rloop times. If an error is found, the relevant data is stored
165 * in report. The goal is to check if (x * y) == (y * x). The
166 * data pattern is important, and the back-to-back fmuld's are
167 * important.
168 */
169int
170fpu_fmuld(int rloop, struct fps_test_ereport *report)
171{
172	char err_data[MAX_INFO_SIZE];
173	double x;
174	double y;
175	double z;
176	double z1;
177	int loop;
178	uint64_t expect;
179	uint64_t observe;
180	uint64_t *px;
181	uint64_t *py;
182
183	loop = 0;
184	px = (uint64_t *)& x;
185	py = (uint64_t *)& y;
186	*px = 0x2FEBD8507111CDE5UL;	/* 4865027 */
187	*py = 0x2FE284A9A98EAA26UL;
188
189#ifdef __lint
190	(void) x;
191	(void) y;
192#endif
193
194	while (loop < rloop) {
195		loop++;
196		z = z1 = 0.0;
197
198		/*
199		 * Data pattern and back-to-back fmuld() are
200		 * important
201		 */
202		fmuld(&x, &y, &z, &z1);
203
204		if (*(uint64_t *)&z != *(uint64_t *)&z1) {
205			(void) snprintf(err_data, sizeof (err_data),
206			    "\nExpected: %.16e,\nObserved: %.16e",
207			    *(uint64_t *)&z, *(uint64_t *)&z1);
208			expect = *(uint64_t *)&z;
209			observe = *(uint64_t *)&z1;
210			setup_fps_test_struct(IS_EREPORT_INFO, report,
211			    6341, &observe, &expect, 1, 1, err_data);
212
213			return (-1);
214		}
215	}
216
217	return (0);
218}
219
220/*
221 * fmuld(double *x,double *y, double *z, double *z1)
222 * performs the assembly level instructions for
223 * fpu_fmuld.
224 */
225/* ARGSUSED */
226static void
227fmuld(double *x, double *y, double *z, double *z1)
228{
229	asm("ldd[%i0], %f0");
230	asm("ldd[%i1], %f4");
231	asm("fmuld%f0, %f4, %f2");
232	asm("fmuld%f4, %f0, %f6");
233	asm("std%f2, [%i2]");
234	asm("std%f6, [%i3]");
235	asm("membar #Sync");
236}
237
238
239/*
240 * fpu_fmulx(int rloop, int unit, struct fps_test_ereport *report)
241 * returns whether the correct value is calculated each time
242 * rloop times. If an error is found, the relevant data is stored
243 * in report. The goal is to check if (x * y) == (y * x) with
244 * 64-bit intgers.
245 */
246int
247fpu_fmulx(int rloop, struct fps_test_ereport *report)
248{
249	char err_data[MAX_INFO_SIZE];
250	int loop;
251	int loop_lim;
252	uint32_t *rs1;
253	uint32_t *rs2;
254	uint64_t expect;
255	uint64_t observe;
256	uint64_t v1;
257	uint64_t v2;
258	uint64_t vd1;
259	uint64_t vd2;
260	uint64_t *rd1;
261	uint64_t *rd2;
262
263	v1 = v2 = vd1 = vd2 = 0;
264	loop = 0;
265	loop_lim = rloop;
266
267	if (loop_lim < 10)
268		loop_lim = 10;
269
270	if (loop_lim > 100000)
271		loop_lim = 100000;
272
273	rs1 = (uint32_t *)& v1;
274	rs2 = (uint32_t *)& v2;
275	rd1 = &vd1;
276	rd2 = &vd2;
277
278#ifdef __lint
279	(void) v1;
280	(void) v2;
281#endif
282
283	srand(0l);
284	while (loop < loop_lim) {
285		loop++;
286
287#ifndef __lint
288
289		*rs1 = mrand48();
290		*(rs1 + 1) = mrand48();
291		*rs2 = mrand48();
292		*(rs2 + 1) = mrand48();
293#endif
294
295		/* LINTED */
296		fmulx((uint64_t *)rs1, (uint64_t *)rs2, rd1);
297
298		/* LINTED */
299		fmulx((uint64_t *)rs2, (uint64_t *)rs1, rd2);
300
301		if (*rd1 != *rd2) {
302			expect = (uint64_t)*rd1;
303			observe = (uint64_t)*rd2;
304			(void) snprintf(err_data, sizeof (err_data),
305			    "\nExpected: %lld\nObserved: %lld", *rd1, *rd2);
306			setup_fps_test_struct(IS_EREPORT_INFO, report,
307			    6356, &observe, &expect, 1, 1, err_data);
308
309		return (-1);
310		}
311	}
312
313	return (0);
314}
315
316/*
317 * fmulx(uint64_t *rs1, uint64_t *rs2, uint64_t *rd)
318 * performs the assembly level instructions for
319 * fpu_fmulx.
320 */
321/* ARGSUSED */
322static void
323fmulx(uint64_t *rs1, uint64_t *rs2, uint64_t *rd)
324{
325	asm("ldx   [%i0], %l0");
326	asm("ldx   [%i1], %l1");
327	asm("mulx  %l0, %l1, %l2");
328	asm("stx   %l2, [%i2]");
329	asm("membar	#Sync");
330
331}
332
333
334
335#ifdef V9B
336
337#pragma align 64  (f0)
338#pragma align 8  (f2)
339
340#define	MEMSIZE	2048*3
341
342static uchar_t f0[64];
343static uchar_t f2[8];
344
345static uint32_t bmask[] = {0x01234567, 0x12345678,
346			0x23456789, 0x3456789a,
347			0x456789ab, 0x56789abc,
348			0x6789abcd, 0x789abcde,
349			0x89abcdef, 0x9abcdef0,
350			0xabcdef01, 0xbcdef012,
351			0xcdef0123, 0xdef01234,
352			0xef012345, 0xf0123456,
353			0x55555555, 0xaaaaaaaa,
354			0x00000000, 0xffffffff};
355
356#ifdef __lint
357
358/*ARGSUSED*/
359unsigned long
360setgsr(unsigned long arg1)
361{
362	return (0);
363}
364
365/*ARGSUSED*/
366float
367fpackfix(double arg1)
368{
369	return (0.0);
370}
371
372/*ARGSUSED*/
373unsigned long
374fcmpne16(double arg1, double arg2)
375{
376	return (0);
377}
378
379/*ARGSUSED*/
380unsigned long
381fcmpgt16(double arg1, double arg2)
382{
383	return (0);
384}
385
386#endif /* LINT */
387
388/*
389 * align_data(int loop, struct fps_test_ereport *report)
390 * returns whether a miscompare was found after running alignment tests
391 * loop amount of times. If an error is found, relevant data is stored
392 * in report. This test exercises the alignaddr and aligndata
393 * instructions with different byte alignments to ensure proper
394 * operation. These two instructions are used extensively by the kernel
395 * to move data size greater than 512 bytes. User level memcpy and
396 * memmove library also use these instructions for data size
397 * greater than 256 bytes.
398 */
399int
400align_data(int loop, struct fps_test_ereport *report)
401{
402	char err[MAX_INFO_SIZE];
403	int test_ret;
404	int nr_malloc;
405	size_t memsize;
406	struct timeval timeout;
407	uchar_t c;
408	uchar_t *pf0;
409	uchar_t *pf2;
410	uchar_t *src;
411	uint32_t cnt;
412	uint32_t i;
413	uint32_t offset;
414	uint32_t start;
415	uint64_t expect[2];
416	uint64_t observe[2];
417
418	timeout.tv_sec = 0;
419	timeout.tv_usec = 10000;
420	nr_malloc = 0;
421	err[0] = '\0';
422
423	/* Make sure memsize is 64 bytes aligned  with minimum of 64 bytes */
424	memsize = MEMSIZE;
425	memsize = memsize / 64 * 64;
426
427	if (memsize < 64)
428		memsize = 64;
429
430	src = (uchar_t *)memalign(64, memsize + 64);
431
432	while (src == NULL && nr_malloc < 10) {
433		(void) select(1, NULL, NULL, NULL, &timeout);
434		nr_malloc++;
435		src = (uchar_t *)memalign(64, memsize + 64);
436	}
437
438	if (src == NULL)
439		_exit(FPU_SYSCALL_FAIL);
440
441	/* Initialize source array with sequential data */
442	c = 0;
443
444	for (i = 0; i < memsize + 64; i++)
445		*(src + i) = c++;
446
447	for (cnt = 0; cnt < loop; cnt++) {
448		for (start = 1; start < 64; start += 1) {
449			offset = 0;
450
451			test_ret = do_aligndata(src + start, &offset,
452			    memsize, f0, f2, bmask[cnt % 20]);
453
454			/*
455			 * Miscompare on the two aligndata
456			 * instructions. Calculate offset to source
457			 * array and get miscompare data
458			 */
459
460			if (test_ret != 0) {
461				pf0 = f0 + offset % 64;
462				pf2 = f2;
463
464				for (i = 0; i < 8; i++) {
465					if (*(pf0 + i) != *(pf2 + i))
466						break;
467				}
468
469				(void) align_error_create(err, start,
470				    offset + start + i, loop, cnt);
471				expect[0] =
472				    (uint64_t)(*(uint8_t *)
473				    (src + offset + start + i));
474				expect[1] = (uint64_t)0;
475				observe[0] = (uint64_t)(*(uint8_t *)(pf0 + i));
476				observe[1] = (uint64_t)(*(uint8_t *)(pf2 + i));
477				setup_fps_test_struct(
478				    IS_EREPORT_INFO,
479				    report, 6344, observe,
480				    expect, 1, 2, err);
481
482				free(src);
483
484				return (-1);
485			}
486
487			/*
488			 * No miscompare on the aligndata
489			 * instructions. Check to see whether the
490			 * last 64 bytes matches the input
491			 */
492			if (test_ret == 0) {
493				pf2 = src + offset + start;
494
495				for (i = 0; i < 64; i++) {
496					if (f0[i] != *(pf2 + i)) {
497
498						(void) align_error_create(err,
499						    start,
500						    offset + start + i,
501						    loop, cnt);
502						expect[0] =
503						    (uint64_t)(*(uint8_t *)
504						    (pf2 + i));
505						expect[1] = (uint64_t)0;
506						observe[0] = (uint64_t)f0[i];
507						observe[1] = (uint64_t)0;
508						setup_fps_test_struct(
509						    IS_EREPORT_INFO,
510						    report, 6343, observe,
511						    expect, 1, 1, err);
512
513						free(src);
514						return (-1);
515					}
516				}
517			}
518		}
519	}
520
521	free(src);
522
523	return (0);
524}
525
526/*
527 * align_error_create(char *err, int start, int offset, int loop, int count)
528 * returns if a successful snprintf was performed when creating an align_data
529 * error message for align_data.
530 */
531static int
532align_error_create(char *err, uint32_t start,
533	uint32_t offset, int loop, uint32_t count)
534{
535	if (err == NULL)
536		return (-1);
537
538	return snprintf(err, sizeof (err),
539	    "Start = %2.2d offset = %2.2d loop = %d cnt = %d",
540	    start, offset, loop, count);
541}
542
543/*
544 * do_aligndata(uchar_t *from, uint32_t *offset, size_t sz,
545 * uchar_t *f0, uchar_t *f2, uint32_t bmask) performs
546 * the assembly lvl routines for align_data.
547 */
548/*ARGSUSED*/
549static int
550do_aligndata(uchar_t *from, uint32_t *offset, size_t sz,
551	uchar_t *f0, uchar_t *f2, uint32_t bmask)
552{
553	int ret = 1;
554
555	asm("bmask	%i5,%g0,%g0");
556	/* produce GSR.offset and align %l0 to 8 bytes boundary */
557	asm("alignaddr	%i0, %g0, %l0");
558	/* %i0 then used as error register, assume error */
559	asm("mov	1,%i0");
560	/* %l1 used as offset counter */
561	asm("mov	-8,%l1");
562	asm("ldd	[%l0], %f0");
563
564	asm("next_read:");
565
566	asm("ldd	[%l0+8], %f2");
567	asm("ldd	[%l0+0x10], %f4");
568	asm("faligndata	%f0, %f2, %f32");
569	asm("faligndata	%f0, %f2, %f48");
570	asm("fcmpd	%fcc0,%f32,%f48");
571	asm("fblg,pn	%fcc0,error");
572	/* %l1 contains offset value */
573	asm("add	%l1,8,%l1");
574	/* 0 - 7 */
575
576	asm("ldd	[%l0+0x18], %f6");
577	asm("faligndata	%f2, %f4, %f34");
578	asm("faligndata	%f2, %f4, %f48");
579	asm("fcmpd	%fcc0,%f34,%f48");
580	asm("fblg,pn	%fcc0,error");
581	/* %l1 contains offset value */
582	asm("add	%l1,8,%l1");
583	/* 9 - 15 */
584
585	asm("ldd	[%l0+0x20], %f8");
586	asm("faligndata	%f4, %f6, %f36");
587	asm("faligndata	%f4, %f6, %f48");
588	asm("fcmpd	%fcc0,%f36,%f48");
589	asm("fblg,pn	%fcc0,error");
590	/* %l1 contains offset value */
591	asm("add	%l1,8,%l1");
592	/* 16 - 23 */
593
594	asm("ldd	[%l0+0x28], %f10");
595	asm("faligndata	%f6, %f8, %f38");
596	asm("faligndata	%f6, %f8, %f48");
597	asm("fcmpd	%fcc0,%f38,%f48");
598	asm("fblg,pn	%fcc0,error");
599	/* contains offset value */
600	asm("add	%l1,8,%l1");
601	/* 24 - 31 */
602
603	asm("ldd	[%l0+0x28], %f10");
604	asm("faligndata	%f8, %f10, %f40");
605	asm("faligndata	%f8, %f10, %f48");
606	asm("fcmpd	%fcc0,%f40,%f48");
607	asm("fblg,pn	%fcc0,error");
608	/* %l1 contains offset value */
609	asm("add	%l1,8,%l1");
610	/* 32 - 39 */
611
612	asm("ldd	[%l0+0x30], %f12");
613	asm("faligndata	%f10, %f12, %f42");
614	asm("faligndata	%f10, %f12, %f48");
615	asm("fcmpd	%fcc0,%f42,%f48");
616	asm("fblg,pn	%fcc0,error");
617	/* %l1 contains offset value */
618	asm("add	%l1,8,%l1");
619	/* 40 - 47 */
620
621	asm("ldd	[%l0+0x38], %f14");
622	asm("faligndata	%f12, %f14, %f44");
623	asm("faligndata	%f12, %f14, %f48");
624	asm("fcmpd	%fcc0,%f44,%f48");
625	asm("fblg,pn	%fcc0,error");
626	/* %l1 contains offset value */
627	asm("add	%l1,8,%l1");
628	/* 48 - 55 */
629
630	asm("ldd	[%l0+0x40], %f0");
631	asm("faligndata	%f14, %f0, %f46");
632	asm("faligndata	%f14, %f0, %f48");
633	asm("fcmpd	%fcc0,%f46,%f48");
634	asm("fblg,pn	%fcc0,error");
635	/* %l1 contains offset value */
636	asm("add	%l1,8,%l1");
637	/* 56 - 63 */
638
639	asm("subcc	%i2,64,%i2");
640	asm("bg		next_read");
641	asm("add	%l0,64,%l0");
642
643	/* no miscompare error */
644	asm("mov	0,%i0");
645	ret = 0;
646	/* no error, move back to last 64 bytes boundary */
647	asm("sub	%l1,56,%l1");
648
649	asm("error:");
650	asm("stda	%f32,[%i3]0xf0");
651	asm("std	%f48,[%i4]");
652	/* store offset value */
653	asm("st 	%l1,[%i1]");
654	asm("membar	#Sync");
655
656	return (ret);
657}
658
659/*
660 * vis_test(struct fps_test_ereport *report)
661 * checks if various RISC operations are performed
662 * succesfully. If an error is found, relevant data
663 * is stored in report.
664 */
665int
666vis_test(struct fps_test_ereport *report)
667{
668	int v1;
669	int v2;
670	int v3;
671
672	v1 = visgt16(report);
673	v2 = visne16(report);
674	v3 = vispackfix(report);
675
676	if ((0 != v1) || (0 != v2) || (0 != v3))
677		return (-1);
678
679	return (0);
680}
681
682/*
683 * visgt16(struct fps_test_ereport *report)
684 * does a greater-than compare instruction and returns if
685 * successful or not. If an error, relevant data is
686 * stored in report.
687 */
688static int
689visgt16(struct fps_test_ereport *report)
690{
691	uint64_t expected;
692	uint64_t observed;
693	unsigned long a = 0x0000000000000001;
694	unsigned long b = 0x8000000008000008;
695	unsigned long c = fcmpgt16(*((double *)&a), *((double *)&b));
696
697	if (c == 0x8)
698		return (0);
699	else {
700		expected = (uint64_t)0x8;
701		observed = (*(uint64_t *)&c);
702		setup_fps_test_struct(NO_EREPORT_INFO, report,
703		    6364, &observed, &expected, 1, 1);
704
705		return (-1);
706	}
707}
708
709/*
710 * visne16(struct fps_test_ereport *report)
711 * does a not-equal compare instruction and returns if
712 * successful or not. If an error, relevant data is
713 * stored in report.
714 */
715static int
716visne16(struct fps_test_ereport *report)
717{
718	uint64_t expected;
719	uint64_t observed;
720	unsigned long a = 0x0000000000000001;
721	unsigned long b = 0x0001000000001001;
722	unsigned long c = fcmpne16(*((double *)&a), *((double *)&b));
723
724	if (c == 0x9)
725		return (0);
726	else {
727		expected = (uint64_t)0x9;
728		observed = (*(uint64_t *)&c);
729		setup_fps_test_struct(NO_EREPORT_INFO, report,
730		    6365, &observed, &expected, 1, 1);
731
732		return (-1);
733	}
734}
735
736/*
737 * vispackfix(struct fps_test_ereport *report)
738 * does four 16-bit pack conversions to a lower precsion
739 * format and returns if successful or not. If an error,
740 * relevant data is stored in report.
741 */
742static int
743vispackfix(struct fps_test_ereport *report)
744{
745	float b;
746	uint64_t expected;
747	uint64_t observed;
748	unsigned int c;
749	unsigned long a = 0x8008000008008008;
750	unsigned long gsr = 0;
751
752	(void) setgsr(gsr);
753
754	b = fpackfix(*((double *)&a));
755	c = *((unsigned int *)&b);
756
757	if (c == 0x80080800)
758		return (0);
759	else {
760		expected = (uint64_t)0x80080800;
761		observed = (uint64_t)c;
762		setup_fps_test_struct(NO_EREPORT_INFO, report,
763		    6366, &observed, &expected, 1, 1);
764
765		return (-1);
766	}
767}
768
769#endif
770