#include <stdlib.h>
#include <stdio.h>
#include <string.h>

#include <sys/types.h>

#include "zdtmtst.h"

/*
 * This test is specific to PowerPC
 */
#ifndef _ARCH_PPC64
int main(int argc, char *argv[])
{
	test_init(argc, argv);
	skip("Unsupported arch");
	return 0;
}

#else

#include <sys/auxv.h>
#include <asm/cputable.h>

/*
 * This test verifies that data stored in the VSX registers are still there
 * once the restart is done.
 *
 * The test is filling the registers with dedicated values and then check
 * their content.
 */

const char *test_doc	= "Test if data in vector registers do survive the c/r";
const char *test_author	= "Laurent Dufour <ldufour@linux.vnet.ibm.com>";

int is_test_doable(void)
{
	unsigned long val;

	val = getauxval(AT_HWCAP);
#define CHECK_FEATURE(f) do {						\
		if (!(val & f)) {					\
			test_msg("CPU feature " #f " is missing\n");	\
			return 0;					\
		}							\
	} while(0)

	CHECK_FEATURE(PPC_FEATURE_64);
	CHECK_FEATURE(PPC_FEATURE_HAS_ALTIVEC);
	CHECK_FEATURE(PPC_FEATURE_HAS_VSX);
	return 1;
}

void fill_vsx(uint64_t *pt)
{
	asm volatile(
	    "lis	3, 0		\n"

	    "lxvd2x	0, 3, %0	\n"
	    "addi	3, 3, 16	\n" /* move to the next qword */
	    "lxvd2x	1, 3, %0	\n"
	    "addi	3, 3, 16	\n" /* move to the next qword */
	    "lxvd2x	2, 3, %0	\n"
	    "addi	3, 3, 16	\n" /* move to the next qword */
	    "lxvd2x	3, 3, %0	\n"
	    "addi	3, 3, 16	\n" /* move to the next qword */
	    "lxvd2x	4, 3, %0	\n"
	    "addi	3, 3, 16	\n" /* move to the next qword */
	    "lxvd2x	5, 3, %0	\n"
	    "addi	3, 3, 16	\n" /* move to the next qword */
	    "lxvd2x	6, 3, %0	\n"
	    "addi	3, 3, 16	\n" /* move to the next qword */
	    "lxvd2x	7, 3, %0	\n"
	    "addi	3, 3, 16	\n" /* move to the next qword */
	    "lxvd2x	8, 3, %0	\n"
	    "addi	3, 3, 16	\n" /* move to the next qword */
	    "lxvd2x	9, 3, %0	\n"
	    "addi	3, 3, 16	\n" /* move to the next qword */

	    "lxvd2x	10, 3, %0	\n"
	    "addi	3, 3, 16	\n" /* move to the next qword */
	    "lxvd2x	11, 3, %0	\n"
	    "addi	3, 3, 16	\n" /* move to the next qword */
	    "lxvd2x	12, 3, %0	\n"
	    "addi	3, 3, 16	\n" /* move to the next qword */
	    "lxvd2x	13, 3, %0	\n"
	    "addi	3, 3, 16	\n" /* move to the next qword */
	    "lxvd2x	14, 3, %0	\n"
	    "addi	3, 3, 16	\n" /* move to the next qword */
	    "lxvd2x	15, 3, %0	\n"
	    "addi	3, 3, 16	\n" /* move to the next qword */
	    "lxvd2x	16, 3, %0	\n"
	    "addi	3, 3, 16	\n" /* move to the next qword */
	    "lxvd2x	17, 3, %0	\n"
	    "addi	3, 3, 16	\n" /* move to the next qword */
	    "lxvd2x	18, 3, %0	\n"
	    "addi	3, 3, 16	\n" /* move to the next qword */
	    "lxvd2x	19, 3, %0	\n"
	    "addi	3, 3, 16	\n" /* move to the next qword */

	    "lxvd2x	20, 3, %0	\n"
	    "addi	3, 3, 16	\n" /* move to the next qword */
	    "lxvd2x	21, 3, %0	\n"
	    "addi	3, 3, 16	\n" /* move to the next qword */
	    "lxvd2x	22, 3, %0	\n"
	    "addi	3, 3, 16	\n" /* move to the next qword */
	    "lxvd2x	23, 3, %0	\n"
	    "addi	3, 3, 16	\n" /* move to the next qword */
	    "lxvd2x	24, 3, %0	\n"
	    "addi	3, 3, 16	\n" /* move to the next qword */
	    "lxvd2x	25, 3, %0	\n"
	    "addi	3, 3, 16	\n" /* move to the next qword */
	    "lxvd2x	26, 3, %0	\n"
	    "addi	3, 3, 16	\n" /* move to the next qword */
	    "lxvd2x	27, 3, %0	\n"
	    "addi	3, 3, 16	\n" /* move to the next qword */
	    "lxvd2x	28, 3, %0	\n"
	    "addi	3, 3, 16	\n" /* move to the next qword */
	    "lxvd2x	29, 3, %0	\n"
	    "addi	3, 3, 16	\n" /* move to the next qword */

	    "lxvd2x	30, 3, %0	\n"
	    "addi	3, 3, 16	\n" /* move to the next qword */
	    "lxvd2x	31, 3, %0	\n"
	    "addi	3, 3, 16	\n" /* move to the next qword */
	    "lxvd2x	32, 3, %0	\n"
	    "addi	3, 3, 16	\n" /* move to the next qword */
	    "lxvd2x	33, 3, %0	\n"
	    "addi	3, 3, 16	\n" /* move to the next qword */
	    "lxvd2x	34, 3, %0	\n"
	    "addi	3, 3, 16	\n" /* move to the next qword */
	    "lxvd2x	35, 3, %0	\n"
	    "addi	3, 3, 16	\n" /* move to the next qword */
	    "lxvd2x	36, 3, %0	\n"
	    "addi	3, 3, 16	\n" /* move to the next qword */
	    "lxvd2x	37, 3, %0	\n"
	    "addi	3, 3, 16	\n" /* move to the next qword */
	    "lxvd2x	38, 3, %0	\n"
	    "addi	3, 3, 16	\n" /* move to the next qword */
	    "lxvd2x	39, 3, %0	\n"
	    "addi	3, 3, 16	\n" /* move to the next qword */

	    "lxvd2x	40, 3, %0	\n"
	    "addi	3, 3, 16	\n" /* move to the next qword */
	    "lxvd2x	41, 3, %0	\n"
	    "addi	3, 3, 16	\n" /* move to the next qword */
	    "lxvd2x	42, 3, %0	\n"
	    "addi	3, 3, 16	\n" /* move to the next qword */
	    "lxvd2x	43, 3, %0	\n"
	    "addi	3, 3, 16	\n" /* move to the next qword */
	    "lxvd2x	44, 3, %0	\n"
	    "addi	3, 3, 16	\n" /* move to the next qword */
	    "lxvd2x	45, 3, %0	\n"
	    "addi	3, 3, 16	\n" /* move to the next qword */
	    "lxvd2x	46, 3, %0	\n"
	    "addi	3, 3, 16	\n" /* move to the next qword */
	    "lxvd2x	47, 3, %0	\n"
	    "addi	3, 3, 16	\n" /* move to the next qword */
	    "lxvd2x	48, 3, %0	\n"
	    "addi	3, 3, 16	\n" /* move to the next qword */
	    "lxvd2x	49, 3, %0	\n"
	    "addi	3, 3, 16	\n" /* move to the next qword */

	    "lxvd2x	50, 3, %0	\n"
	    "addi	3, 3, 16	\n" /* move to the next qword */
	    "lxvd2x	51, 3, %0	\n"
	    "addi	3, 3, 16	\n" /* move to the next qword */
	    "lxvd2x	52, 3, %0	\n"
	    "addi	3, 3, 16	\n" /* move to the next qword */
	    "lxvd2x	53, 3, %0	\n"
	    "addi	3, 3, 16	\n" /* move to the next qword */
	    "lxvd2x	54, 3, %0	\n"
	    "addi	3, 3, 16	\n" /* move to the next qword */
	    "lxvd2x	55, 3, %0	\n"
	    "addi	3, 3, 16	\n" /* move to the next qword */
	    "lxvd2x	56, 3, %0	\n"
	    "addi	3, 3, 16	\n" /* move to the next qword */
	    "lxvd2x	57, 3, %0	\n"
	    "addi	3, 3, 16	\n" /* move to the next qword */
	    "lxvd2x	58, 3, %0	\n"
	    "addi	3, 3, 16	\n" /* move to the next qword */
	    "lxvd2x	59, 3, %0	\n"
	    "addi	3, 3, 16	\n" /* move to the next qword */

	    "lxvd2x	60, 3, %0	\n"
	    "addi	3, 3, 16	\n" /* move to the next qword */
	    "lxvd2x	61, 3, %0	\n"
	    "addi	3, 3, 16	\n" /* move to the next qword */
	    "lxvd2x	62, 3, %0	\n"
	    "addi	3, 3, 16	\n" /* move to the next qword */
	    "lxvd2x	63, 3, %0	\n"
	    : /* no output */
	    : "r" (pt)
	    : "3");
}

void read_vsx(uint64_t *pt)
{
	asm volatile(
	    "lis	3, 0		\n"

	    "stxvd2x	0, 3, %0	\n"
	    "addi	3, 3, 16	\n" /* move to the next qword */
	    "stxvd2x	1, 3, %0	\n"
	    "addi	3, 3, 16	\n" /* move to the next qword */
	    "stxvd2x	2, 3, %0	\n"
	    "addi	3, 3, 16	\n" /* move to the next qword */
	    "stxvd2x	3, 3, %0	\n"
	    "addi	3, 3, 16	\n" /* move to the next qword */
	    "stxvd2x	4, 3, %0	\n"
	    "addi	3, 3, 16	\n" /* move to the next qword */
	    "stxvd2x	5, 3, %0	\n"
	    "addi	3, 3, 16	\n" /* move to the next qword */
	    "stxvd2x	6, 3, %0	\n"
	    "addi	3, 3, 16	\n" /* move to the next qword */
	    "stxvd2x	7, 3, %0	\n"
	    "addi	3, 3, 16	\n" /* move to the next qword */
	    "stxvd2x	8, 3, %0	\n"
	    "addi	3, 3, 16	\n" /* move to the next qword */
	    "stxvd2x	9, 3, %0	\n"
	    "addi	3, 3, 16	\n" /* move to the next qword */

	    "stxvd2x	10, 3, %0	\n"
	    "addi	3, 3, 16	\n" /* move to the next qword */
	    "stxvd2x	11, 3, %0	\n"
	    "addi	3, 3, 16	\n" /* move to the next qword */
	    "stxvd2x	12, 3, %0	\n"
	    "addi	3, 3, 16	\n" /* move to the next qword */
	    "stxvd2x	13, 3, %0	\n"
	    "addi	3, 3, 16	\n" /* move to the next qword */
	    "stxvd2x	14, 3, %0	\n"
	    "addi	3, 3, 16	\n" /* move to the next qword */
	    "stxvd2x	15, 3, %0	\n"
	    "addi	3, 3, 16	\n" /* move to the next qword */
	    "stxvd2x	16, 3, %0	\n"
	    "addi	3, 3, 16	\n" /* move to the next qword */
	    "stxvd2x	17, 3, %0	\n"
	    "addi	3, 3, 16	\n" /* move to the next qword */
	    "stxvd2x	18, 3, %0	\n"
	    "addi	3, 3, 16	\n" /* move to the next qword */
	    "stxvd2x	19, 3, %0	\n"
	    "addi	3, 3, 16	\n" /* move to the next qword */

	    "stxvd2x	20, 3, %0	\n"
	    "addi	3, 3, 16	\n" /* move to the next qword */
	    "stxvd2x	21, 3, %0	\n"
	    "addi	3, 3, 16	\n" /* move to the next qword */
	    "stxvd2x	22, 3, %0	\n"
	    "addi	3, 3, 16	\n" /* move to the next qword */
	    "stxvd2x	23, 3, %0	\n"
	    "addi	3, 3, 16	\n" /* move to the next qword */
	    "stxvd2x	24, 3, %0	\n"
	    "addi	3, 3, 16	\n" /* move to the next qword */
	    "stxvd2x	25, 3, %0	\n"
	    "addi	3, 3, 16	\n" /* move to the next qword */
	    "stxvd2x	26, 3, %0	\n"
	    "addi	3, 3, 16	\n" /* move to the next qword */
	    "stxvd2x	27, 3, %0	\n"
	    "addi	3, 3, 16	\n" /* move to the next qword */
	    "stxvd2x	28, 3, %0	\n"
	    "addi	3, 3, 16	\n" /* move to the next qword */
	    "stxvd2x	29, 3, %0	\n"
	    "addi	3, 3, 16	\n" /* move to the next qword */

	    "stxvd2x	30, 3, %0	\n"
	    "addi	3, 3, 16	\n" /* move to the next qword */
	    "stxvd2x	31, 3, %0	\n"
	    "addi	3, 3, 16	\n" /* move to the next qword */
	    "stxvd2x	32, 3, %0	\n"
	    "addi	3, 3, 16	\n" /* move to the next qword */
	    "stxvd2x	33, 3, %0	\n"
	    "addi	3, 3, 16	\n" /* move to the next qword */
	    "stxvd2x	34, 3, %0	\n"
	    "addi	3, 3, 16	\n" /* move to the next qword */
	    "stxvd2x	35, 3, %0	\n"
	    "addi	3, 3, 16	\n" /* move to the next qword */
	    "stxvd2x	36, 3, %0	\n"
	    "addi	3, 3, 16	\n" /* move to the next qword */
	    "stxvd2x	37, 3, %0	\n"
	    "addi	3, 3, 16	\n" /* move to the next qword */
	    "stxvd2x	38, 3, %0	\n"
	    "addi	3, 3, 16	\n" /* move to the next qword */
	    "stxvd2x	39, 3, %0	\n"
	    "addi	3, 3, 16	\n" /* move to the next qword */

	    "stxvd2x	40, 3, %0	\n"
	    "addi	3, 3, 16	\n" /* move to the next qword */
	    "stxvd2x	41, 3, %0	\n"
	    "addi	3, 3, 16	\n" /* move to the next qword */
	    "stxvd2x	42, 3, %0	\n"
	    "addi	3, 3, 16	\n" /* move to the next qword */
	    "stxvd2x	43, 3, %0	\n"
	    "addi	3, 3, 16	\n" /* move to the next qword */
	    "stxvd2x	44, 3, %0	\n"
	    "addi	3, 3, 16	\n" /* move to the next qword */
	    "stxvd2x	45, 3, %0	\n"
	    "addi	3, 3, 16	\n" /* move to the next qword */
	    "stxvd2x	46, 3, %0	\n"
	    "addi	3, 3, 16	\n" /* move to the next qword */
	    "stxvd2x	47, 3, %0	\n"
	    "addi	3, 3, 16	\n" /* move to the next qword */
	    "stxvd2x	48, 3, %0	\n"
	    "addi	3, 3, 16	\n" /* move to the next qword */
	    "stxvd2x	49, 3, %0	\n"
	    "addi	3, 3, 16	\n" /* move to the next qword */

	    "stxvd2x	50, 3, %0	\n"
	    "addi	3, 3, 16	\n" /* move to the next qword */
	    "stxvd2x	51, 3, %0	\n"
	    "addi	3, 3, 16	\n" /* move to the next qword */
	    "stxvd2x	52, 3, %0	\n"
	    "addi	3, 3, 16	\n" /* move to the next qword */
	    "stxvd2x	53, 3, %0	\n"
	    "addi	3, 3, 16	\n" /* move to the next qword */
	    "stxvd2x	54, 3, %0	\n"
	    "addi	3, 3, 16	\n" /* move to the next qword */
	    "stxvd2x	55, 3, %0	\n"
	    "addi	3, 3, 16	\n" /* move to the next qword */
	    "stxvd2x	56, 3, %0	\n"
	    "addi	3, 3, 16	\n" /* move to the next qword */
	    "stxvd2x	57, 3, %0	\n"
	    "addi	3, 3, 16	\n" /* move to the next qword */
	    "stxvd2x	58, 3, %0	\n"
	    "addi	3, 3, 16	\n" /* move to the next qword */
	    "stxvd2x	59, 3, %0	\n"
	    "addi	3, 3, 16	\n" /* move to the next qword */

	    "stxvd2x	60, 3, %0	\n"
	    "addi	3, 3, 16	\n" /* move to the next qword */
	    "stxvd2x	61, 3, %0	\n"
	    "addi	3, 3, 16	\n" /* move to the next qword */
	    "stxvd2x	62, 3, %0	\n"
	    "addi	3, 3, 16	\n" /* move to the next qword */
	    "stxvd2x	63, 3, %0	\n"

	    : /* no output */
	    : "r" (pt)
	    : "3");
}

int main(int argc, char *argv[])
{
	/* A random buffer of 1024 bytes (64 * 128bit registers to fill) */
	static const char ibuffer[128/8*64]=
		"sahwoleiGun9loosliz0Aech9aiph5eiIengei7Ogh8zu7ye"
		"Aeshie6vai0thaehool1ooK6ayaj3Neitahn8yeeh5ahfuiT"
		"uCeir1bife4ieceema8choo2Wengaec1seDaidohteipa4ai"
		"aequee7AiriejaeJar1giak8Gei2uathloh5uemaeG6EiSoo"
		"PhaenaethoPhej8nEecheegeihosho8Zohroo8ea6Juuheif"
		"nu2Hahvai1tuf0Zeeeveephu2EitaexiVaekieboac7Nushu"
		"aeTh6Quoo3iozeisaudaGheed0aPah2Schoog0eiChaeN5su"
		"xoo1phoic1mahXohSai1thoogo0oesooeaxai7eBahHahMue"
		"quiloh2ooPahpiujeithae0Dau0shuwicobinaaYooj0ajiw"
		"iiheeS4awoh3haevlaiGe8phaev3eiluaChaF6ieng4aith4"
		"aif3TooYo1aigoomZiuhai8eesoo4maiLahr3PoM8Eir5ooz"
		"Iequ9ahre4Op4bahaiso6ohnah8Shokimooch1Oafahf5aih"
		"xohphee1pi5Iecaiaigh7Eisah2uew5acie7wi6Zo0Eelah9"
		"woi8QueerohfeiThaBoh5jaic3peiPohAhng0bu5shoop7ca"
		"Qui5kodaika8quioahmohreeVe8loquaeeLi5ze3oceiHa0l"
		"roh8Ooxae7uish9ioog7ieS3aibeo2thOosiuvaiS5lohp4U"
		"emieG0eit6Bien8EzaiwiTh3geighaexshee8eHiec1TooH2"
		"Eeceacai0inaejieboo8NeishieweiraHooj9apeecooy0th"
		"daThei6aexeisahdsei3keik0diPheejchais6ezo0iep5Ae"
		"Wiqu6aepeing4ba8diek3aev9waYooveAebai9eef6Iex6vo"
		"Quee9MeitahMighoHuo3seveeMoh3ohtoxaib6ootaiF5EeT"
		"Ohb9eijoonoh6ich";
	char obuffer[128/8*64];
	int do_test;

	test_init(argc, argv);

	do_test = is_test_doable();

	if (do_test) {
		memset(obuffer, 0xFF, sizeof(obuffer));
		fill_vsx((uint64_t *)ibuffer);
	}

	test_daemon();
	test_waitsig();

	if (do_test) {
		read_vsx((uint64_t *)obuffer);

		if (!memcmp(ibuffer, obuffer, sizeof(ibuffer)))
			pass();
		else {
			test_msg("Data mismatch\n");
			fail();
		}
	}
	else {
		test_msg("The CPU is missing some features.\n");
		fail();
	}

	return 0;
}

#endif /* _ARCH_PPC64 */
