/* * processor control and status function * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU Library General Public License version 2. */ #include #include #include #include #include #include #include #include #include #include static struct { void (*func)(struct pt_regs *, void *data); void *data; } handlers[128]; /* * Exception handlers span from 0x100 to 0x1000 and can have a granularity * of 0x20 bytes in some cases. Indexing spans 0-0x1000 with 0x20 increments * resulting in 128 slots. */ void handle_exception(int trap, void (*func)(struct pt_regs *, void *), void * data) { assert(!(trap & ~0xfe0)); trap >>= 5; if (func && handlers[trap].func) { printf("exception handler installed twice %#x\n", trap << 5); abort(); } handlers[trap].func = func; handlers[trap].data = data; } void do_handle_exception(struct pt_regs *regs) { unsigned char v; __current_cpu = (struct cpu *)mfspr(SPR_SPRG0); if (in_usermode()) current_cpu()->in_user = false; /* * We run with AIL=0, so interrupts taken with MMU disabled. * Enable here. */ assert(!(mfmsr() & (MSR_IR|MSR_DR))); if (mmu_enabled()) mtmsr(mfmsr() | (MSR_IR|MSR_DR)); v = regs->trap >> 5; if (v < 128 && handlers[v].func) { handlers[v].func(regs, handlers[v].data); if (regs->msr & MSR_PR) current_cpu()->in_user = true; return; } printf("Unhandled CPU%d exception %#lx at NIA:0x%016lx MSR:0x%016lx\n", smp_processor_id(), regs->trap, regs->nip, regs->msr); dump_frame_stack((void *)regs->nip, (void *)regs->gpr[1]); abort(); } uint64_t get_clock_us(void) { return get_tb() * 1000000 / tb_hz; } uint64_t get_clock_ms(void) { return get_tb() * 1000 / tb_hz; } void delay(uint64_t cycles) { uint64_t start = get_tb(); while ((get_tb() - start) < cycles) cpu_relax(); } void udelay(uint64_t us) { delay((us * tb_hz) / 1000000); } void sleep_tb(uint64_t cycles) { uint64_t start, end, now; if (!machine_is_pseries()) { /* * P9/10 Could use 'stop' to sleep here which would be * interesting. stop with ESL=0 should be simple enough, ESL=1 * would require SRESET based wakeup which is more involved. */ delay(cycles); return; } start = now = get_tb(); end = start + cycles; while (end > now) { uint64_t left = end - now; /* TODO: Could support large decrementer */ if (left > 0x7fffffff) left = 0x7fffffff; /* DEC won't fire until H_CEDE is called because EE=0 */ asm volatile ("mtdec %0" : : "r" (left)); handle_exception(0x900, &dec_handler_oneshot, NULL); /* * H_CEDE is called with MSR[EE] clear and enables it as part * of the hcall, returning with EE enabled. The dec interrupt * is then taken immediately and the handler disables EE. * * If H_CEDE returned for any other interrupt than dec * expiring, that is considered an unhandled interrupt and * the test case would be stopped. */ if (hcall(H_CEDE) != H_SUCCESS) { printf("H_CEDE failed\n"); abort(); } handle_exception(0x900, NULL, NULL); now = get_tb(); } } void usleep(uint64_t us) { sleep_tb((us * tb_hz) / 1000000); } static void rfid_msr(uint64_t msr) { uint64_t tmp; asm volatile( "mtsrr1 %1 \n\ bl 0f \n\ 0: \n\ mflr %0 \n\ addi %0,%0,1f-0b \n\ mtsrr0 %0 \n\ rfid \n\ 1: \n" : "=r"(tmp) : "r"(msr) : "lr"); } void enable_mcheck(void) { /* This is a no-op on pseries */ rfid_msr(mfmsr() | MSR_ME); } void disable_mcheck(void) { rfid_msr(mfmsr() & ~MSR_ME); } bool in_usermode(void) { return current_cpu()->in_user; } static void usermode_sc_handler(struct pt_regs *regs, void *data) { regs->msr &= ~(MSR_PR|MSR_EE); /* Interrupt return handler will keep in_user clear */ } void enter_usermode(void) { assert_msg(!in_usermode(), "enter_usermode called with in_usermode"); /* mfmsr would fault in usermode anyway */ assert_msg(!(mfmsr() & MSR_PR), "enter_usermode called from user mode"); assert_msg(!(mfmsr() & MSR_EE), "enter_usermode called with interrupts enabled"); assert_msg((mfmsr() & (MSR_IR|MSR_DR)) == (MSR_IR|MSR_DR), "enter_usermode called with virtual memory disabled"); handle_exception(0xc00, usermode_sc_handler, NULL); rfid_msr(mfmsr() | (MSR_PR|MSR_IR|MSR_DR|MSR_EE)); current_cpu()->in_user = true; } void exit_usermode(void) { assert_msg(in_usermode(), "enter_usermode called with !in_usermode"); asm volatile("sc 0" ::: "memory"); handle_exception(0xc00, NULL, NULL); assert(!in_usermode()); assert(!(mfmsr() & MSR_PR)); }