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