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