16842bc34SLaurent Vivier /*
26842bc34SLaurent Vivier * processor control and status function
3b1a6fd7dSThomas Huth *
4b1a6fd7dSThomas Huth * This code is free software; you can redistribute it and/or modify it
5b1a6fd7dSThomas Huth * under the terms of the GNU Library General Public License version 2.
66842bc34SLaurent Vivier */
76842bc34SLaurent Vivier
86842bc34SLaurent Vivier #include <libcflat.h>
96842bc34SLaurent Vivier #include <asm/processor.h>
108b10d4faSNicholas Piggin #include <asm/time.h>
116842bc34SLaurent Vivier #include <asm/ptrace.h>
12f4d8d939SSuraj Jitindar Singh #include <asm/setup.h>
13f4d8d939SSuraj Jitindar Singh #include <asm/barrier.h>
14ba33a96fSNicholas Piggin #include <asm/hcall.h>
15ba33a96fSNicholas Piggin #include <asm/handlers.h>
16d4c8e725SNicholas Piggin #include <asm/mmu.h>
17c76b0d0aSNicholas Piggin #include <asm/smp.h>
186842bc34SLaurent Vivier
196842bc34SLaurent Vivier static struct {
206842bc34SLaurent Vivier void (*func)(struct pt_regs *, void *data);
216842bc34SLaurent Vivier void *data;
220ec01e27SNicholas Piggin } handlers[128];
236842bc34SLaurent Vivier
240ec01e27SNicholas Piggin /*
250ec01e27SNicholas Piggin * Exception handlers span from 0x100 to 0x1000 and can have a granularity
260ec01e27SNicholas Piggin * of 0x20 bytes in some cases. Indexing spans 0-0x1000 with 0x20 increments
270ec01e27SNicholas Piggin * resulting in 128 slots.
280ec01e27SNicholas Piggin */
handle_exception(int trap,void (* func)(struct pt_regs *,void *),void * data)296842bc34SLaurent Vivier void handle_exception(int trap, void (*func)(struct pt_regs *, void *),
306842bc34SLaurent Vivier void * data)
316842bc34SLaurent Vivier {
320ec01e27SNicholas Piggin assert(!(trap & ~0xfe0));
335d571097SNicholas Piggin
340ec01e27SNicholas Piggin trap >>= 5;
356842bc34SLaurent Vivier
365d571097SNicholas Piggin if (func && handlers[trap].func) {
378791cea0SNicholas Piggin printf("exception handler installed twice %#x\n", trap << 5);
385d571097SNicholas Piggin abort();
395d571097SNicholas Piggin }
400ec01e27SNicholas Piggin
416842bc34SLaurent Vivier handlers[trap].func = func;
426842bc34SLaurent Vivier handlers[trap].data = data;
436842bc34SLaurent Vivier }
446842bc34SLaurent Vivier
do_handle_exception(struct pt_regs * regs)456842bc34SLaurent Vivier void do_handle_exception(struct pt_regs *regs)
466842bc34SLaurent Vivier {
476842bc34SLaurent Vivier unsigned char v;
486842bc34SLaurent Vivier
49c76b0d0aSNicholas Piggin __current_cpu = (struct cpu *)mfspr(SPR_SPRG0);
50*93c847c1SNicholas Piggin if (in_usermode())
51*93c847c1SNicholas Piggin current_cpu()->in_user = false;
52c76b0d0aSNicholas Piggin
53d4c8e725SNicholas Piggin /*
54d4c8e725SNicholas Piggin * We run with AIL=0, so interrupts taken with MMU disabled.
55d4c8e725SNicholas Piggin * Enable here.
56d4c8e725SNicholas Piggin */
57d4c8e725SNicholas Piggin assert(!(mfmsr() & (MSR_IR|MSR_DR)));
58d4c8e725SNicholas Piggin if (mmu_enabled())
59d4c8e725SNicholas Piggin mtmsr(mfmsr() | (MSR_IR|MSR_DR));
60d4c8e725SNicholas Piggin
610ec01e27SNicholas Piggin v = regs->trap >> 5;
626842bc34SLaurent Vivier
630ec01e27SNicholas Piggin if (v < 128 && handlers[v].func) {
646842bc34SLaurent Vivier handlers[v].func(regs, handlers[v].data);
65*93c847c1SNicholas Piggin if (regs->msr & MSR_PR)
66*93c847c1SNicholas Piggin current_cpu()->in_user = true;
676842bc34SLaurent Vivier return;
686842bc34SLaurent Vivier }
696842bc34SLaurent Vivier
70c76b0d0aSNicholas Piggin printf("Unhandled CPU%d exception %#lx at NIA:0x%016lx MSR:0x%016lx\n",
71c76b0d0aSNicholas Piggin smp_processor_id(), regs->trap, regs->nip, regs->msr);
72ac6e1abfSNicholas Piggin dump_frame_stack((void *)regs->nip, (void *)regs->gpr[1]);
736842bc34SLaurent Vivier abort();
746842bc34SLaurent Vivier }
75f4d8d939SSuraj Jitindar Singh
get_clock_us(void)768b10d4faSNicholas Piggin uint64_t get_clock_us(void)
778b10d4faSNicholas Piggin {
788b10d4faSNicholas Piggin return get_tb() * 1000000 / tb_hz;
798b10d4faSNicholas Piggin }
808b10d4faSNicholas Piggin
get_clock_ms(void)818b10d4faSNicholas Piggin uint64_t get_clock_ms(void)
828b10d4faSNicholas Piggin {
838b10d4faSNicholas Piggin return get_tb() * 1000 / tb_hz;
848b10d4faSNicholas Piggin }
858b10d4faSNicholas Piggin
delay(uint64_t cycles)86f4d8d939SSuraj Jitindar Singh void delay(uint64_t cycles)
87f4d8d939SSuraj Jitindar Singh {
88f4d8d939SSuraj Jitindar Singh uint64_t start = get_tb();
89f4d8d939SSuraj Jitindar Singh
90f4d8d939SSuraj Jitindar Singh while ((get_tb() - start) < cycles)
91f4d8d939SSuraj Jitindar Singh cpu_relax();
92f4d8d939SSuraj Jitindar Singh }
93f4d8d939SSuraj Jitindar Singh
udelay(uint64_t us)94f4d8d939SSuraj Jitindar Singh void udelay(uint64_t us)
95f4d8d939SSuraj Jitindar Singh {
96f4d8d939SSuraj Jitindar Singh delay((us * tb_hz) / 1000000);
97f4d8d939SSuraj Jitindar Singh }
98ba33a96fSNicholas Piggin
sleep_tb(uint64_t cycles)99ba33a96fSNicholas Piggin void sleep_tb(uint64_t cycles)
100ba33a96fSNicholas Piggin {
101ba33a96fSNicholas Piggin uint64_t start, end, now;
102ba33a96fSNicholas Piggin
103610c5a9cSNicholas Piggin if (!machine_is_pseries()) {
104610c5a9cSNicholas Piggin /*
105610c5a9cSNicholas Piggin * P9/10 Could use 'stop' to sleep here which would be
106610c5a9cSNicholas Piggin * interesting. stop with ESL=0 should be simple enough, ESL=1
107610c5a9cSNicholas Piggin * would require SRESET based wakeup which is more involved.
108610c5a9cSNicholas Piggin */
109610c5a9cSNicholas Piggin delay(cycles);
110610c5a9cSNicholas Piggin return;
111610c5a9cSNicholas Piggin }
112610c5a9cSNicholas Piggin
113ba33a96fSNicholas Piggin start = now = get_tb();
114ba33a96fSNicholas Piggin end = start + cycles;
115ba33a96fSNicholas Piggin
116ba33a96fSNicholas Piggin while (end > now) {
117ba33a96fSNicholas Piggin uint64_t left = end - now;
118ba33a96fSNicholas Piggin
119ba33a96fSNicholas Piggin /* TODO: Could support large decrementer */
120ba33a96fSNicholas Piggin if (left > 0x7fffffff)
121ba33a96fSNicholas Piggin left = 0x7fffffff;
122ba33a96fSNicholas Piggin
123ba33a96fSNicholas Piggin /* DEC won't fire until H_CEDE is called because EE=0 */
124ba33a96fSNicholas Piggin asm volatile ("mtdec %0" : : "r" (left));
125ba33a96fSNicholas Piggin handle_exception(0x900, &dec_handler_oneshot, NULL);
126ba33a96fSNicholas Piggin /*
127ba33a96fSNicholas Piggin * H_CEDE is called with MSR[EE] clear and enables it as part
128ba33a96fSNicholas Piggin * of the hcall, returning with EE enabled. The dec interrupt
129ba33a96fSNicholas Piggin * is then taken immediately and the handler disables EE.
130ba33a96fSNicholas Piggin *
131ba33a96fSNicholas Piggin * If H_CEDE returned for any other interrupt than dec
132ba33a96fSNicholas Piggin * expiring, that is considered an unhandled interrupt and
133ba33a96fSNicholas Piggin * the test case would be stopped.
134ba33a96fSNicholas Piggin */
135ba33a96fSNicholas Piggin if (hcall(H_CEDE) != H_SUCCESS) {
136ba33a96fSNicholas Piggin printf("H_CEDE failed\n");
137ba33a96fSNicholas Piggin abort();
138ba33a96fSNicholas Piggin }
139ba33a96fSNicholas Piggin handle_exception(0x900, NULL, NULL);
140ba33a96fSNicholas Piggin
141ba33a96fSNicholas Piggin now = get_tb();
142ba33a96fSNicholas Piggin }
143ba33a96fSNicholas Piggin }
144ba33a96fSNicholas Piggin
usleep(uint64_t us)145ba33a96fSNicholas Piggin void usleep(uint64_t us)
146ba33a96fSNicholas Piggin {
147ba33a96fSNicholas Piggin sleep_tb((us * tb_hz) / 1000000);
148ba33a96fSNicholas Piggin }
149610c5a9cSNicholas Piggin
rfid_msr(uint64_t msr)150610c5a9cSNicholas Piggin static void rfid_msr(uint64_t msr)
151610c5a9cSNicholas Piggin {
152610c5a9cSNicholas Piggin uint64_t tmp;
153610c5a9cSNicholas Piggin
154610c5a9cSNicholas Piggin asm volatile(
155610c5a9cSNicholas Piggin "mtsrr1 %1 \n\
156610c5a9cSNicholas Piggin bl 0f \n\
157610c5a9cSNicholas Piggin 0: \n\
158610c5a9cSNicholas Piggin mflr %0 \n\
159610c5a9cSNicholas Piggin addi %0,%0,1f-0b \n\
160610c5a9cSNicholas Piggin mtsrr0 %0 \n\
161610c5a9cSNicholas Piggin rfid \n\
162610c5a9cSNicholas Piggin 1: \n"
163610c5a9cSNicholas Piggin : "=r"(tmp) : "r"(msr) : "lr");
164610c5a9cSNicholas Piggin }
165610c5a9cSNicholas Piggin
enable_mcheck(void)166610c5a9cSNicholas Piggin void enable_mcheck(void)
167610c5a9cSNicholas Piggin {
168610c5a9cSNicholas Piggin /* This is a no-op on pseries */
169610c5a9cSNicholas Piggin rfid_msr(mfmsr() | MSR_ME);
170610c5a9cSNicholas Piggin }
171610c5a9cSNicholas Piggin
disable_mcheck(void)172610c5a9cSNicholas Piggin void disable_mcheck(void)
173610c5a9cSNicholas Piggin {
174610c5a9cSNicholas Piggin rfid_msr(mfmsr() & ~MSR_ME);
175610c5a9cSNicholas Piggin }
176*93c847c1SNicholas Piggin
in_usermode(void)177*93c847c1SNicholas Piggin bool in_usermode(void)
178*93c847c1SNicholas Piggin {
179*93c847c1SNicholas Piggin return current_cpu()->in_user;
180*93c847c1SNicholas Piggin }
181*93c847c1SNicholas Piggin
usermode_sc_handler(struct pt_regs * regs,void * data)182*93c847c1SNicholas Piggin static void usermode_sc_handler(struct pt_regs *regs, void *data)
183*93c847c1SNicholas Piggin {
184*93c847c1SNicholas Piggin regs->msr &= ~(MSR_PR|MSR_EE);
185*93c847c1SNicholas Piggin /* Interrupt return handler will keep in_user clear */
186*93c847c1SNicholas Piggin }
187*93c847c1SNicholas Piggin
enter_usermode(void)188*93c847c1SNicholas Piggin void enter_usermode(void)
189*93c847c1SNicholas Piggin {
190*93c847c1SNicholas Piggin assert_msg(!in_usermode(), "enter_usermode called with in_usermode");
191*93c847c1SNicholas Piggin /* mfmsr would fault in usermode anyway */
192*93c847c1SNicholas Piggin assert_msg(!(mfmsr() & MSR_PR), "enter_usermode called from user mode");
193*93c847c1SNicholas Piggin assert_msg(!(mfmsr() & MSR_EE), "enter_usermode called with interrupts enabled");
194*93c847c1SNicholas Piggin assert_msg((mfmsr() & (MSR_IR|MSR_DR)) == (MSR_IR|MSR_DR),
195*93c847c1SNicholas Piggin "enter_usermode called with virtual memory disabled");
196*93c847c1SNicholas Piggin
197*93c847c1SNicholas Piggin handle_exception(0xc00, usermode_sc_handler, NULL);
198*93c847c1SNicholas Piggin rfid_msr(mfmsr() | (MSR_PR|MSR_IR|MSR_DR|MSR_EE));
199*93c847c1SNicholas Piggin current_cpu()->in_user = true;
200*93c847c1SNicholas Piggin }
201*93c847c1SNicholas Piggin
exit_usermode(void)202*93c847c1SNicholas Piggin void exit_usermode(void)
203*93c847c1SNicholas Piggin {
204*93c847c1SNicholas Piggin assert_msg(in_usermode(), "enter_usermode called with !in_usermode");
205*93c847c1SNicholas Piggin asm volatile("sc 0" ::: "memory");
206*93c847c1SNicholas Piggin handle_exception(0xc00, NULL, NULL);
207*93c847c1SNicholas Piggin assert(!in_usermode());
208*93c847c1SNicholas Piggin assert(!(mfmsr() & MSR_PR));
209*93c847c1SNicholas Piggin }
210