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