xref: /kvm-unit-tests/s390x/emulator.c (revision a299895b7abb54e7ba6bb4108f202acbb484ac65)
1559eaa1eSDavid Hildenbrand /*
2559eaa1eSDavid Hildenbrand  * Emulator tests - for s390x CPU instructions that are usually interpreted
3559eaa1eSDavid Hildenbrand  *                  by the hardware
4559eaa1eSDavid Hildenbrand  *
5559eaa1eSDavid Hildenbrand  * Copyright (c) 2017 Red Hat Inc
6559eaa1eSDavid Hildenbrand  *
7559eaa1eSDavid Hildenbrand  * Authors:
8559eaa1eSDavid Hildenbrand  *  David Hildenbrand <david@redhat.com>
9559eaa1eSDavid Hildenbrand  *
10559eaa1eSDavid Hildenbrand  * This code is free software; you can redistribute it and/or modify it
11559eaa1eSDavid Hildenbrand  * under the terms of the GNU Library General Public License version 2.
12559eaa1eSDavid Hildenbrand  */
13559eaa1eSDavid Hildenbrand #include <libcflat.h>
1440c8f4ceSDavid Hildenbrand #include <asm/cpacf.h>
1540c8f4ceSDavid Hildenbrand #include <asm/interrupt.h>
162d0d5f41SDavid Hildenbrand #include <asm/float.h>
172d0d5f41SDavid Hildenbrand 
182d0d5f41SDavid Hildenbrand struct lowcore *lc = NULL;
19559eaa1eSDavid Hildenbrand 
20559eaa1eSDavid Hildenbrand static inline void __test_spm_ipm(uint8_t cc, uint8_t key)
21559eaa1eSDavid Hildenbrand {
22559eaa1eSDavid Hildenbrand 	uint64_t in = (cc << 28) | (key << 24);
23559eaa1eSDavid Hildenbrand 	uint64_t out = ~0ULL;
24559eaa1eSDavid Hildenbrand 
25559eaa1eSDavid Hildenbrand 	report_prefix_pushf("cc=%d,key=%x", cc, key);
26559eaa1eSDavid Hildenbrand 
27559eaa1eSDavid Hildenbrand 	asm volatile ("spm %1\n"
28559eaa1eSDavid Hildenbrand 		      "ipm %0\n"
29559eaa1eSDavid Hildenbrand 		      : "+r"(out) : "r"(in) : "cc");
30559eaa1eSDavid Hildenbrand 
31*a299895bSThomas Huth 	report(!(out & 0xc0000000UL), "bit 32 and 33 set to zero");
32*a299895bSThomas Huth 	report((out & ~0xff000000ULL) == ~0xff000000ULL,
33*a299895bSThomas Huth 	       "bit 0-31, 40-63 unchanged");
34*a299895bSThomas Huth 	report(!((in ^ out) & 0x3f000000UL), "cc and key applied");
35559eaa1eSDavid Hildenbrand 
36559eaa1eSDavid Hildenbrand 	report_prefix_pop();
37559eaa1eSDavid Hildenbrand }
38559eaa1eSDavid Hildenbrand 
39559eaa1eSDavid Hildenbrand /* Test the SET PROGRAM PARAMETER and INSERT PROGRAM PARAMETER instruction */
40559eaa1eSDavid Hildenbrand static void test_spm_ipm(void)
41559eaa1eSDavid Hildenbrand {
42559eaa1eSDavid Hildenbrand 	__test_spm_ipm(0, 0xf);
43559eaa1eSDavid Hildenbrand 	__test_spm_ipm(1, 0x9);
44559eaa1eSDavid Hildenbrand 	__test_spm_ipm(2, 0x5);
45559eaa1eSDavid Hildenbrand 	__test_spm_ipm(3, 0x3);
46559eaa1eSDavid Hildenbrand 	__test_spm_ipm(0, 0);
47559eaa1eSDavid Hildenbrand }
48559eaa1eSDavid Hildenbrand 
4940c8f4ceSDavid Hildenbrand static inline void __test_cpacf(unsigned int opcode, unsigned long func,
5040c8f4ceSDavid Hildenbrand 				unsigned int r1, unsigned int r2,
5140c8f4ceSDavid Hildenbrand 				unsigned int r3)
5240c8f4ceSDavid Hildenbrand {
5340c8f4ceSDavid Hildenbrand 	register unsigned long gr0 asm("0") = func;
5440c8f4ceSDavid Hildenbrand 	cpacf_mask_t mask;
5540c8f4ceSDavid Hildenbrand 	register unsigned long gr1 asm("1") = (unsigned long)&mask;
5640c8f4ceSDavid Hildenbrand 
5740c8f4ceSDavid Hildenbrand 	asm volatile(".insn rrf,%[opc] << 16,%[r1],%[r2],%[r3],0\n"
5840c8f4ceSDavid Hildenbrand 		     : : "d" (gr0), "d" (gr1), [opc] "i" (opcode),
5940c8f4ceSDavid Hildenbrand 		         [r1] "i" (r1), [r2] "i" (r2), [r3] "i" (r3));
6040c8f4ceSDavid Hildenbrand }
6140c8f4ceSDavid Hildenbrand 
6240c8f4ceSDavid Hildenbrand static inline void __test_cpacf_r1_odd(unsigned int opcode)
6340c8f4ceSDavid Hildenbrand {
6440c8f4ceSDavid Hildenbrand 	report_prefix_push("r1 odd");
6540c8f4ceSDavid Hildenbrand 	expect_pgm_int();
6640c8f4ceSDavid Hildenbrand 	__test_cpacf(opcode, 0, 1, 4, 6);
6740c8f4ceSDavid Hildenbrand 	check_pgm_int_code(PGM_INT_CODE_SPECIFICATION);
6840c8f4ceSDavid Hildenbrand 	report_prefix_pop();
6940c8f4ceSDavid Hildenbrand }
7040c8f4ceSDavid Hildenbrand 
7140c8f4ceSDavid Hildenbrand static inline void __test_cpacf_r1_null(unsigned int opcode)
7240c8f4ceSDavid Hildenbrand {
7340c8f4ceSDavid Hildenbrand 	report_prefix_push("r1 null");
7440c8f4ceSDavid Hildenbrand 	expect_pgm_int();
7540c8f4ceSDavid Hildenbrand 	__test_cpacf(opcode, 0, 0, 4, 6);
7640c8f4ceSDavid Hildenbrand 	check_pgm_int_code(PGM_INT_CODE_SPECIFICATION);
7740c8f4ceSDavid Hildenbrand 	report_prefix_pop();
7840c8f4ceSDavid Hildenbrand }
7940c8f4ceSDavid Hildenbrand 
8040c8f4ceSDavid Hildenbrand static inline void __test_cpacf_r2_odd(unsigned int opcode)
8140c8f4ceSDavid Hildenbrand {
8240c8f4ceSDavid Hildenbrand 	report_prefix_push("r2 odd");
8340c8f4ceSDavid Hildenbrand 	expect_pgm_int();
8440c8f4ceSDavid Hildenbrand 	__test_cpacf(opcode, 0, 2, 3, 6);
8540c8f4ceSDavid Hildenbrand 	check_pgm_int_code(PGM_INT_CODE_SPECIFICATION);
8640c8f4ceSDavid Hildenbrand 	report_prefix_pop();
8740c8f4ceSDavid Hildenbrand }
8840c8f4ceSDavid Hildenbrand 
8940c8f4ceSDavid Hildenbrand static inline void __test_cpacf_r2_null(unsigned int opcode)
9040c8f4ceSDavid Hildenbrand {
9140c8f4ceSDavid Hildenbrand 	report_prefix_push("r2 null");
9240c8f4ceSDavid Hildenbrand 	expect_pgm_int();
9340c8f4ceSDavid Hildenbrand 	__test_cpacf(opcode, 0, 2, 0, 6);
9440c8f4ceSDavid Hildenbrand 	check_pgm_int_code(PGM_INT_CODE_SPECIFICATION);
9540c8f4ceSDavid Hildenbrand 	report_prefix_pop();
9640c8f4ceSDavid Hildenbrand }
9740c8f4ceSDavid Hildenbrand 
9840c8f4ceSDavid Hildenbrand static inline void __test_cpacf_r3_odd(unsigned int opcode)
9940c8f4ceSDavid Hildenbrand {
10040c8f4ceSDavid Hildenbrand 	report_prefix_push("r3 odd");
10140c8f4ceSDavid Hildenbrand 	expect_pgm_int();
10240c8f4ceSDavid Hildenbrand 	__test_cpacf(opcode, 0, 2, 4, 5);
10340c8f4ceSDavid Hildenbrand 	check_pgm_int_code(PGM_INT_CODE_SPECIFICATION);
10440c8f4ceSDavid Hildenbrand 	report_prefix_pop();
10540c8f4ceSDavid Hildenbrand }
10640c8f4ceSDavid Hildenbrand 
10740c8f4ceSDavid Hildenbrand static inline void __test_cpacf_r3_null(unsigned int opcode)
10840c8f4ceSDavid Hildenbrand {
10940c8f4ceSDavid Hildenbrand 	report_prefix_push("r3 null");
11040c8f4ceSDavid Hildenbrand 	expect_pgm_int();
11140c8f4ceSDavid Hildenbrand 	__test_cpacf(opcode, 0, 2, 4, 0);
11240c8f4ceSDavid Hildenbrand 	check_pgm_int_code(PGM_INT_CODE_SPECIFICATION);
11340c8f4ceSDavid Hildenbrand 	report_prefix_pop();
11440c8f4ceSDavid Hildenbrand }
11540c8f4ceSDavid Hildenbrand 
11640c8f4ceSDavid Hildenbrand static inline void __test_cpacf_mod_bit(unsigned int opcode)
11740c8f4ceSDavid Hildenbrand {
11840c8f4ceSDavid Hildenbrand 	report_prefix_push("mod bit");
11940c8f4ceSDavid Hildenbrand 	expect_pgm_int();
12040c8f4ceSDavid Hildenbrand 	__test_cpacf(opcode, CPACF_DECRYPT, 2, 4, 6);
12140c8f4ceSDavid Hildenbrand 	check_pgm_int_code(PGM_INT_CODE_SPECIFICATION);
12240c8f4ceSDavid Hildenbrand 	report_prefix_pop();
12340c8f4ceSDavid Hildenbrand }
12440c8f4ceSDavid Hildenbrand 
12540c8f4ceSDavid Hildenbrand static inline void __test_cpacf_invalid_func(unsigned int opcode)
12640c8f4ceSDavid Hildenbrand {
12740c8f4ceSDavid Hildenbrand 	report_prefix_push("invalid subfunction");
12840c8f4ceSDavid Hildenbrand 	expect_pgm_int();
12940c8f4ceSDavid Hildenbrand 	/* 127 is unassigned for now. We don't simply use any, as HW
13040c8f4ceSDavid Hildenbrand 	 * might simply mask valid codes in query but they might still work */
13140c8f4ceSDavid Hildenbrand 	if (cpacf_query_func(opcode, 127)) {
13240c8f4ceSDavid Hildenbrand 		report_skip("127 not invalid");
13340c8f4ceSDavid Hildenbrand 	} else {
13440c8f4ceSDavid Hildenbrand 		__test_cpacf(opcode, 127, 2, 4, 6);
13540c8f4ceSDavid Hildenbrand 	}
13640c8f4ceSDavid Hildenbrand 	check_pgm_int_code(PGM_INT_CODE_SPECIFICATION);
13740c8f4ceSDavid Hildenbrand 	report_prefix_pop();
13840c8f4ceSDavid Hildenbrand }
13940c8f4ceSDavid Hildenbrand 
14040c8f4ceSDavid Hildenbrand static inline void __test_cpacf_invalid_parm(unsigned int opcode)
14140c8f4ceSDavid Hildenbrand {
14240c8f4ceSDavid Hildenbrand 	report_prefix_push("invalid parm address");
14340c8f4ceSDavid Hildenbrand 	expect_pgm_int();
14440c8f4ceSDavid Hildenbrand 	__cpacf_query(opcode, (void *) -1);
14540c8f4ceSDavid Hildenbrand 	check_pgm_int_code(PGM_INT_CODE_ADDRESSING);
14640c8f4ceSDavid Hildenbrand 	report_prefix_pop();
14740c8f4ceSDavid Hildenbrand }
14840c8f4ceSDavid Hildenbrand 
14940c8f4ceSDavid Hildenbrand static inline void __test_cpacf_protected_parm(unsigned int opcode)
15040c8f4ceSDavid Hildenbrand {
15140c8f4ceSDavid Hildenbrand 	report_prefix_push("protected parm address");
15240c8f4ceSDavid Hildenbrand 	expect_pgm_int();
15340c8f4ceSDavid Hildenbrand 	low_prot_enable();
15440c8f4ceSDavid Hildenbrand 	__cpacf_query(opcode, (void *) 8);
15540c8f4ceSDavid Hildenbrand 	low_prot_disable();
15640c8f4ceSDavid Hildenbrand 	check_pgm_int_code(PGM_INT_CODE_PROTECTION);
15740c8f4ceSDavid Hildenbrand 	report_prefix_pop();
15840c8f4ceSDavid Hildenbrand }
15940c8f4ceSDavid Hildenbrand 
16040c8f4ceSDavid Hildenbrand static inline void __test_basic_cpacf_opcode(unsigned int opcode)
16140c8f4ceSDavid Hildenbrand {
16240c8f4ceSDavid Hildenbrand 	bool mod_bit_allowed = false;
16340c8f4ceSDavid Hildenbrand 
16440c8f4ceSDavid Hildenbrand 	if (!__cpacf_check_opcode(opcode)) {
16540c8f4ceSDavid Hildenbrand 		report_skip("not available");
16640c8f4ceSDavid Hildenbrand 		return;
16740c8f4ceSDavid Hildenbrand 	}
168*a299895bSThomas Huth 	report(cpacf_query_func(opcode, 0), "query indicated in query");
16940c8f4ceSDavid Hildenbrand 
17040c8f4ceSDavid Hildenbrand 	switch (opcode) {
17140c8f4ceSDavid Hildenbrand 	case CPACF_KMCTR:
17240c8f4ceSDavid Hildenbrand 		__test_cpacf_r3_odd(opcode);
17340c8f4ceSDavid Hildenbrand 		__test_cpacf_r3_null(opcode);
17440c8f4ceSDavid Hildenbrand 		/* FALL THROUGH */
17540c8f4ceSDavid Hildenbrand 	case CPACF_PRNO:
17640c8f4ceSDavid Hildenbrand 	case CPACF_KMF:
17740c8f4ceSDavid Hildenbrand 	case CPACF_KMC:
17840c8f4ceSDavid Hildenbrand 	case CPACF_KMO:
17940c8f4ceSDavid Hildenbrand 	case CPACF_KM:
18040c8f4ceSDavid Hildenbrand 		__test_cpacf_r1_odd(opcode);
18140c8f4ceSDavid Hildenbrand 		__test_cpacf_r1_null(opcode);
18240c8f4ceSDavid Hildenbrand 		mod_bit_allowed = true;
18340c8f4ceSDavid Hildenbrand 		/* FALL THROUGH */
18440c8f4ceSDavid Hildenbrand 	case CPACF_KMAC:
18540c8f4ceSDavid Hildenbrand 	case CPACF_KIMD:
18640c8f4ceSDavid Hildenbrand 	case CPACF_KLMD:
18740c8f4ceSDavid Hildenbrand 		__test_cpacf_r2_odd(opcode);
18840c8f4ceSDavid Hildenbrand 		__test_cpacf_r2_null(opcode);
18940c8f4ceSDavid Hildenbrand 	        break;
19040c8f4ceSDavid Hildenbrand 	}
19140c8f4ceSDavid Hildenbrand 	if (!mod_bit_allowed)
19240c8f4ceSDavid Hildenbrand 		__test_cpacf_mod_bit(opcode);
19340c8f4ceSDavid Hildenbrand 	__test_cpacf_invalid_func(opcode);
19440c8f4ceSDavid Hildenbrand 	__test_cpacf_invalid_parm(opcode);
19540c8f4ceSDavid Hildenbrand 	__test_cpacf_protected_parm(opcode);
19640c8f4ceSDavid Hildenbrand }
19740c8f4ceSDavid Hildenbrand 
19840c8f4ceSDavid Hildenbrand /* COMPUTE MESSAGE AUTHENTICATION CODE */
19940c8f4ceSDavid Hildenbrand static void test_kmac(void)
20040c8f4ceSDavid Hildenbrand {
20140c8f4ceSDavid Hildenbrand 	__test_basic_cpacf_opcode(CPACF_KMAC);
20240c8f4ceSDavid Hildenbrand }
20340c8f4ceSDavid Hildenbrand 
20440c8f4ceSDavid Hildenbrand /* CIPHER MESSAGE */
20540c8f4ceSDavid Hildenbrand static void test_km(void)
20640c8f4ceSDavid Hildenbrand {
20740c8f4ceSDavid Hildenbrand 	__test_basic_cpacf_opcode(CPACF_KM);
20840c8f4ceSDavid Hildenbrand }
20940c8f4ceSDavid Hildenbrand /* CIPHER MESSAGE WITH CHAINING */
21040c8f4ceSDavid Hildenbrand static void test_kmc(void)
21140c8f4ceSDavid Hildenbrand {
21240c8f4ceSDavid Hildenbrand 	__test_basic_cpacf_opcode(CPACF_KMC);
21340c8f4ceSDavid Hildenbrand }
21440c8f4ceSDavid Hildenbrand 
21540c8f4ceSDavid Hildenbrand /* COMPUTE INTERMEDIATE MESSAGE DIGEST */
21640c8f4ceSDavid Hildenbrand static void test_kimd(void)
21740c8f4ceSDavid Hildenbrand {
21840c8f4ceSDavid Hildenbrand 	__test_basic_cpacf_opcode(CPACF_KIMD);
21940c8f4ceSDavid Hildenbrand }
22040c8f4ceSDavid Hildenbrand 
22140c8f4ceSDavid Hildenbrand /* COMPUTE LAST MESSAGE DIGEST */
22240c8f4ceSDavid Hildenbrand static void test_klmd(void)
22340c8f4ceSDavid Hildenbrand {
22440c8f4ceSDavid Hildenbrand 	__test_basic_cpacf_opcode(CPACF_KLMD);
22540c8f4ceSDavid Hildenbrand }
22640c8f4ceSDavid Hildenbrand 
22740c8f4ceSDavid Hildenbrand /* PERFORM CRYPTOGRAPHIC KEY MANAGEMENT OPERATION */
22840c8f4ceSDavid Hildenbrand static void test_pckmo(void)
22940c8f4ceSDavid Hildenbrand {
23040c8f4ceSDavid Hildenbrand 	__test_basic_cpacf_opcode(CPACF_PCKMO);
23140c8f4ceSDavid Hildenbrand }
23240c8f4ceSDavid Hildenbrand 
23340c8f4ceSDavid Hildenbrand /* CIPHER MESSAGE WITH CIPHER FEEDBACK */
23440c8f4ceSDavid Hildenbrand static void test_kmf(void)
23540c8f4ceSDavid Hildenbrand {
23640c8f4ceSDavid Hildenbrand 	__test_basic_cpacf_opcode(CPACF_KMF);
23740c8f4ceSDavid Hildenbrand }
23840c8f4ceSDavid Hildenbrand 
23940c8f4ceSDavid Hildenbrand /* PERFORM CRYPTOGRAPHIC KEY MANAGEMENT OPERATION */
24040c8f4ceSDavid Hildenbrand static void test_kmo(void)
24140c8f4ceSDavid Hildenbrand {
24240c8f4ceSDavid Hildenbrand 	__test_basic_cpacf_opcode(CPACF_KMO);
24340c8f4ceSDavid Hildenbrand }
24440c8f4ceSDavid Hildenbrand 
24540c8f4ceSDavid Hildenbrand /* PERFORM CRYPTOGRAPHIC COMPUTATION */
24640c8f4ceSDavid Hildenbrand static void test_pcc(void)
24740c8f4ceSDavid Hildenbrand {
24840c8f4ceSDavid Hildenbrand 	__test_basic_cpacf_opcode(CPACF_PCC);
24940c8f4ceSDavid Hildenbrand }
25040c8f4ceSDavid Hildenbrand 
25140c8f4ceSDavid Hildenbrand /* CIPHER MESSAGE WITH COUNTER */
25240c8f4ceSDavid Hildenbrand static void test_kmctr(void)
25340c8f4ceSDavid Hildenbrand {
25440c8f4ceSDavid Hildenbrand 	__test_basic_cpacf_opcode(CPACF_KMCTR);
25540c8f4ceSDavid Hildenbrand }
25640c8f4ceSDavid Hildenbrand 
25740c8f4ceSDavid Hildenbrand /* PERFORM RANDOM NUMBER OPERATION (formerly PPNO) */
25840c8f4ceSDavid Hildenbrand static void test_prno(void)
25940c8f4ceSDavid Hildenbrand {
26040c8f4ceSDavid Hildenbrand 	__test_basic_cpacf_opcode(CPACF_PRNO);
26140c8f4ceSDavid Hildenbrand }
26240c8f4ceSDavid Hildenbrand 
2632d0d5f41SDavid Hildenbrand static void test_dxc(void)
2642d0d5f41SDavid Hildenbrand {
2652d0d5f41SDavid Hildenbrand 	/* DXC (0xff) is to be stored in LC and FPC on a trap (CRT) with AFP */
2662d0d5f41SDavid Hildenbrand 	lc->dxc_vxc = 0x12345678;
2672d0d5f41SDavid Hildenbrand 	set_fpc_dxc(0);
2682d0d5f41SDavid Hildenbrand 
26999ee877dSJanosch Frank 	report_prefix_push("afp");
2702d0d5f41SDavid Hildenbrand 	expect_pgm_int();
2712d0d5f41SDavid Hildenbrand 	asm volatile("	.insn	rrf,0xb9600000,%0,%0,8,0\n"
2722d0d5f41SDavid Hildenbrand 		     : : "r"(0) : "memory");
2732d0d5f41SDavid Hildenbrand 	check_pgm_int_code(PGM_INT_CODE_DATA);
2742d0d5f41SDavid Hildenbrand 
275*a299895bSThomas Huth 	report(lc->dxc_vxc == 0xff, "dxc in LC");
276*a299895bSThomas Huth 	report(get_fpc_dxc() == 0xff, "dxc in FPC");
27799ee877dSJanosch Frank 	report_prefix_pop();
2782d0d5f41SDavid Hildenbrand 
2792d0d5f41SDavid Hildenbrand 	/* DXC (0xff) is to be stored in LC only on a trap (CRT) without AFP */
2802d0d5f41SDavid Hildenbrand 	lc->dxc_vxc = 0x12345678;
2812d0d5f41SDavid Hildenbrand 	set_fpc_dxc(0);
2822d0d5f41SDavid Hildenbrand 
28399ee877dSJanosch Frank 	report_prefix_push("no-afp");
2842d0d5f41SDavid Hildenbrand 	expect_pgm_int();
2852d0d5f41SDavid Hildenbrand 	/* temporarily disable AFP */
2862d0d5f41SDavid Hildenbrand 	afp_disable();
2872d0d5f41SDavid Hildenbrand 	asm volatile("	.insn	rrf,0xb9600000,%0,%0,8,0\n"
2882d0d5f41SDavid Hildenbrand 		     : : "r"(0) : "memory");
2892d0d5f41SDavid Hildenbrand 	afp_enable();
2902d0d5f41SDavid Hildenbrand 	check_pgm_int_code(PGM_INT_CODE_DATA);
2912d0d5f41SDavid Hildenbrand 
292*a299895bSThomas Huth 	report(lc->dxc_vxc == 0xff, "dxc in LC");
293*a299895bSThomas Huth 	report(get_fpc_dxc() == 0, "dxc not in FPC");
29499ee877dSJanosch Frank 	report_prefix_pop();
2952d0d5f41SDavid Hildenbrand }
2962d0d5f41SDavid Hildenbrand 
297559eaa1eSDavid Hildenbrand static struct {
298559eaa1eSDavid Hildenbrand 	const char *name;
299559eaa1eSDavid Hildenbrand 	void (*func)(void);
300559eaa1eSDavid Hildenbrand } tests[] = {
301559eaa1eSDavid Hildenbrand 	{ "spm/ipm", test_spm_ipm },
30240c8f4ceSDavid Hildenbrand 	{ "kmac", test_kmac },
30340c8f4ceSDavid Hildenbrand 	{ "km", test_km },
30440c8f4ceSDavid Hildenbrand 	{ "kmc", test_kmc },
30540c8f4ceSDavid Hildenbrand 	{ "kimd", test_kimd },
30640c8f4ceSDavid Hildenbrand 	{ "klmd", test_klmd },
30740c8f4ceSDavid Hildenbrand 	{ "pckmo", test_pckmo },
30840c8f4ceSDavid Hildenbrand 	{ "kmf", test_kmf },
30940c8f4ceSDavid Hildenbrand 	{ "kmo", test_kmo },
31040c8f4ceSDavid Hildenbrand 	{ "pcc", test_pcc },
31140c8f4ceSDavid Hildenbrand 	{ "kmctr", test_kmctr },
31240c8f4ceSDavid Hildenbrand 	{ "prno", test_prno },
3132d0d5f41SDavid Hildenbrand 	{ "dxc", test_dxc },
314559eaa1eSDavid Hildenbrand 	{ NULL, NULL }
315559eaa1eSDavid Hildenbrand };
316559eaa1eSDavid Hildenbrand 
317559eaa1eSDavid Hildenbrand int main(int argc, char**argv)
318559eaa1eSDavid Hildenbrand {
319559eaa1eSDavid Hildenbrand 	int i;
320559eaa1eSDavid Hildenbrand 
321559eaa1eSDavid Hildenbrand 	report_prefix_push("emulator");
322559eaa1eSDavid Hildenbrand 	for (i = 0; tests[i].name; i++) {
323559eaa1eSDavid Hildenbrand 		report_prefix_push(tests[i].name);
324559eaa1eSDavid Hildenbrand 		tests[i].func();
325559eaa1eSDavid Hildenbrand 		report_prefix_pop();
326559eaa1eSDavid Hildenbrand 	}
327559eaa1eSDavid Hildenbrand 	report_prefix_pop();
328559eaa1eSDavid Hildenbrand 
329559eaa1eSDavid Hildenbrand 	return report_summary();
330559eaa1eSDavid Hildenbrand }
331