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