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", 55 regs->trap, regs->nip, regs->msr); 56 dump_frame_stack((void *)regs->nip, (void *)regs->gpr[1]); 57 abort(); 58 } 59 60 uint64_t get_clock_us(void) 61 { 62 return get_tb() * 1000000 / tb_hz; 63 } 64 65 uint64_t get_clock_ms(void) 66 { 67 return get_tb() * 1000 / tb_hz; 68 } 69 70 void delay(uint64_t cycles) 71 { 72 uint64_t start = get_tb(); 73 74 while ((get_tb() - start) < cycles) 75 cpu_relax(); 76 } 77 78 void udelay(uint64_t us) 79 { 80 delay((us * tb_hz) / 1000000); 81 } 82 83 void sleep_tb(uint64_t cycles) 84 { 85 uint64_t start, end, now; 86 87 if (!machine_is_pseries()) { 88 /* 89 * P9/10 Could use 'stop' to sleep here which would be 90 * interesting. stop with ESL=0 should be simple enough, ESL=1 91 * would require SRESET based wakeup which is more involved. 92 */ 93 delay(cycles); 94 return; 95 } 96 97 start = now = get_tb(); 98 end = start + cycles; 99 100 while (end > now) { 101 uint64_t left = end - now; 102 103 /* TODO: Could support large decrementer */ 104 if (left > 0x7fffffff) 105 left = 0x7fffffff; 106 107 /* DEC won't fire until H_CEDE is called because EE=0 */ 108 asm volatile ("mtdec %0" : : "r" (left)); 109 handle_exception(0x900, &dec_handler_oneshot, NULL); 110 /* 111 * H_CEDE is called with MSR[EE] clear and enables it as part 112 * of the hcall, returning with EE enabled. The dec interrupt 113 * is then taken immediately and the handler disables EE. 114 * 115 * If H_CEDE returned for any other interrupt than dec 116 * expiring, that is considered an unhandled interrupt and 117 * the test case would be stopped. 118 */ 119 if (hcall(H_CEDE) != H_SUCCESS) { 120 printf("H_CEDE failed\n"); 121 abort(); 122 } 123 handle_exception(0x900, NULL, NULL); 124 125 now = get_tb(); 126 } 127 } 128 129 void usleep(uint64_t us) 130 { 131 sleep_tb((us * tb_hz) / 1000000); 132 } 133 134 static void rfid_msr(uint64_t msr) 135 { 136 uint64_t tmp; 137 138 asm volatile( 139 "mtsrr1 %1 \n\ 140 bl 0f \n\ 141 0: \n\ 142 mflr %0 \n\ 143 addi %0,%0,1f-0b \n\ 144 mtsrr0 %0 \n\ 145 rfid \n\ 146 1: \n" 147 : "=r"(tmp) : "r"(msr) : "lr"); 148 } 149 150 void enable_mcheck(void) 151 { 152 /* This is a no-op on pseries */ 153 rfid_msr(mfmsr() | MSR_ME); 154 } 155 156 void disable_mcheck(void) 157 { 158 rfid_msr(mfmsr() & ~MSR_ME); 159 } 160