xref: /kvm-unit-tests/lib/powerpc/processor.c (revision 386ed5c2f9e98a2605817748c44c0cefa60dc88e)
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 #include <asm/smp.h>
17 
18 static struct {
19 	void (*func)(struct pt_regs *, void *data);
20 	void *data;
21 } handlers[128];
22 
23 /*
24  * Exception handlers span from 0x100 to 0x1000 and can have a granularity
25  * of 0x20 bytes in some cases. Indexing spans 0-0x1000 with 0x20 increments
26  * resulting in 128 slots.
27  */
28 void handle_exception(int trap, void (*func)(struct pt_regs *, void *),
29 		      void * data)
30 {
31 	assert(!(trap & ~0xfe0));
32 
33 	trap >>= 5;
34 
35 	if (func && handlers[trap].func) {
36 		printf("exception handler installed twice %#x\n", trap << 5);
37 		abort();
38 	}
39 
40 	handlers[trap].func = func;
41 	handlers[trap].data = data;
42 }
43 
44 void do_handle_exception(struct pt_regs *regs)
45 {
46 	unsigned char v;
47 
48 	__current_cpu = (struct cpu *)mfspr(SPR_SPRG0);
49 
50 	v = regs->trap >> 5;
51 
52 	if (v < 128 && handlers[v].func) {
53 		handlers[v].func(regs, handlers[v].data);
54 		return;
55 	}
56 
57 	printf("Unhandled CPU%d exception %#lx at NIA:0x%016lx MSR:0x%016lx\n",
58 		smp_processor_id(), regs->trap, regs->nip, regs->msr);
59 	dump_frame_stack((void *)regs->nip, (void *)regs->gpr[1]);
60 	abort();
61 }
62 
63 uint64_t get_clock_us(void)
64 {
65 	return get_tb() * 1000000 / tb_hz;
66 }
67 
68 uint64_t get_clock_ms(void)
69 {
70 	return get_tb() * 1000 / tb_hz;
71 }
72 
73 void delay(uint64_t cycles)
74 {
75 	uint64_t start = get_tb();
76 
77 	while ((get_tb() - start) < cycles)
78 		cpu_relax();
79 }
80 
81 void udelay(uint64_t us)
82 {
83 	delay((us * tb_hz) / 1000000);
84 }
85 
86 void sleep_tb(uint64_t cycles)
87 {
88 	uint64_t start, end, now;
89 
90 	if (!machine_is_pseries()) {
91 		/*
92 		 * P9/10 Could use 'stop' to sleep here which would be
93 		 * interesting.  stop with ESL=0 should be simple enough, ESL=1
94 		 * would require SRESET based wakeup which is more involved.
95 		 */
96 		delay(cycles);
97 		return;
98 	}
99 
100 	start = now = get_tb();
101 	end = start + cycles;
102 
103 	while (end > now) {
104 		uint64_t left = end - now;
105 
106 		/* TODO: Could support large decrementer */
107 		if (left > 0x7fffffff)
108 			left = 0x7fffffff;
109 
110 		/* DEC won't fire until H_CEDE is called because EE=0 */
111 		asm volatile ("mtdec %0" : : "r" (left));
112 		handle_exception(0x900, &dec_handler_oneshot, NULL);
113 		/*
114 		 * H_CEDE is called with MSR[EE] clear and enables it as part
115 		 * of the hcall, returning with EE enabled. The dec interrupt
116 		 * is then taken immediately and the handler disables EE.
117 		 *
118 		 * If H_CEDE returned for any other interrupt than dec
119 		 * expiring, that is considered an unhandled interrupt and
120 		 * the test case would be stopped.
121 		 */
122 		if (hcall(H_CEDE) != H_SUCCESS) {
123 			printf("H_CEDE failed\n");
124 			abort();
125 		}
126 		handle_exception(0x900, NULL, NULL);
127 
128 		now = get_tb();
129 	}
130 }
131 
132 void usleep(uint64_t us)
133 {
134 	sleep_tb((us * tb_hz) / 1000000);
135 }
136 
137 static void rfid_msr(uint64_t msr)
138 {
139 	uint64_t tmp;
140 
141 	asm volatile(
142 		"mtsrr1	%1		\n\
143 		 bl	0f		\n\
144 		 0:			\n\
145 		 mflr	%0		\n\
146 		 addi	%0,%0,1f-0b	\n\
147 		 mtsrr0	%0		\n\
148 		 rfid			\n\
149 		 1:			\n"
150 		: "=r"(tmp) : "r"(msr) : "lr");
151 }
152 
153 void enable_mcheck(void)
154 {
155 	/* This is a no-op on pseries */
156 	rfid_msr(mfmsr() | MSR_ME);
157 }
158 
159 void disable_mcheck(void)
160 {
161 	rfid_msr(mfmsr() & ~MSR_ME);
162 }
163