xref: /kvm-unit-tests/lib/powerpc/processor.c (revision 8791cea03f49c31f981792302b3f449714f818cc)
1 /*
2  * processor control and status function
3  *
4  * This code is free software; you can redistribute it and/or modify it
5  * under the terms of the GNU Library General Public License version 2.
6  */
7 
8 #include <libcflat.h>
9 #include <asm/processor.h>
10 #include <asm/ptrace.h>
11 #include <asm/setup.h>
12 #include <asm/barrier.h>
13 #include <asm/hcall.h>
14 #include <asm/handlers.h>
15 
16 static struct {
17 	void (*func)(struct pt_regs *, void *data);
18 	void *data;
19 } handlers[128];
20 
21 /*
22  * Exception handlers span from 0x100 to 0x1000 and can have a granularity
23  * of 0x20 bytes in some cases. Indexing spans 0-0x1000 with 0x20 increments
24  * resulting in 128 slots.
25  */
26 void handle_exception(int trap, void (*func)(struct pt_regs *, void *),
27 		      void * data)
28 {
29 	assert(!(trap & ~0xfe0));
30 
31 	trap >>= 5;
32 
33 	if (func && handlers[trap].func) {
34 		printf("exception handler installed twice %#x\n", trap << 5);
35 		abort();
36 	}
37 
38 	handlers[trap].func = func;
39 	handlers[trap].data = data;
40 }
41 
42 void do_handle_exception(struct pt_regs *regs)
43 {
44 	unsigned char v;
45 
46 	v = regs->trap >> 5;
47 
48 	if (v < 128 && handlers[v].func) {
49 		handlers[v].func(regs, handlers[v].data);
50 		return;
51 	}
52 
53 	printf("unhandled cpu exception %#lx at NIA:0x%016lx MSR:0x%016lx\n", regs->trap, regs->nip, regs->msr);
54 	abort();
55 }
56 
57 void delay(uint64_t cycles)
58 {
59 	uint64_t start = get_tb();
60 
61 	while ((get_tb() - start) < cycles)
62 		cpu_relax();
63 }
64 
65 void udelay(uint64_t us)
66 {
67 	delay((us * tb_hz) / 1000000);
68 }
69 
70 void sleep_tb(uint64_t cycles)
71 {
72 	uint64_t start, end, now;
73 
74 	start = now = get_tb();
75 	end = start + cycles;
76 
77 	while (end > now) {
78 		uint64_t left = end - now;
79 
80 		/* TODO: Could support large decrementer */
81 		if (left > 0x7fffffff)
82 			left = 0x7fffffff;
83 
84 		/* DEC won't fire until H_CEDE is called because EE=0 */
85 		asm volatile ("mtdec %0" : : "r" (left));
86 		handle_exception(0x900, &dec_handler_oneshot, NULL);
87 		/*
88 		 * H_CEDE is called with MSR[EE] clear and enables it as part
89 		 * of the hcall, returning with EE enabled. The dec interrupt
90 		 * is then taken immediately and the handler disables EE.
91 		 *
92 		 * If H_CEDE returned for any other interrupt than dec
93 		 * expiring, that is considered an unhandled interrupt and
94 		 * the test case would be stopped.
95 		 */
96 		if (hcall(H_CEDE) != H_SUCCESS) {
97 			printf("H_CEDE failed\n");
98 			abort();
99 		}
100 		handle_exception(0x900, NULL, NULL);
101 
102 		now = get_tb();
103 	}
104 }
105 
106 void usleep(uint64_t us)
107 {
108 	sleep_tb((us * tb_hz) / 1000000);
109 }
110