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