| #include <arch/arch.h> |
| #include <arch/x86.h> |
| #include <arch/mmu.h> |
| #include <cpu_feat.h> |
| #include <arch/uaccess.h> |
| |
| static unsigned int x86_cstate; |
| |
| /* This atomically enables interrupts and halts. |
| * |
| * Note that sti does not take effect until after the *next* instruction */ |
| void cpu_halt(void) |
| { |
| if (cpu_has_feat(CPU_FEAT_X86_MWAIT)) { |
| /* TODO: since we're monitoring anyway, x86 could use monitor/mwait for |
| * KMSGs, instead of relying on IPIs. (Maybe only for ROUTINE). */ |
| asm volatile("monitor" : : "a"(KERNBASE), "c"(0), "d"(0)); |
| asm volatile("sti; mwait" : : "c"(0x0), "a"(x86_cstate) : "memory"); |
| } else { |
| asm volatile("sti; hlt" : : : "memory"); |
| } |
| } |
| |
| void set_pstate(unsigned int pstate) |
| { |
| uint64_t perf_ctl; |
| |
| /* This MSR was introduced in 0f_03 (family/model), so checking cpuid should |
| * suffice. Though my Qemu says it is a later generation and still fails to |
| * support it (patches pending, I hear). */ |
| if (read_msr_safe(MSR_IA32_PERF_CTL, &perf_ctl)) |
| return; |
| /* The p-state ratio is actually at 15:8, AFAIK, for both PERF_CTL and |
| * PERF_STATUS. Not sure what the lower byte represents. It's probably |
| * processor specific. */ |
| perf_ctl &= ~0xff00ULL; |
| perf_ctl |= pstate << 8; |
| write_msr_safe(MSR_IA32_PERF_CTL, perf_ctl); |
| } |
| |
| void set_fastest_pstate(void) |
| { |
| uint64_t turbo_ratio_limit; |
| |
| /* Support for TURBO_RATIO_LIMIT varies from processor to processor. In |
| * lieu of a full per-model driver, we can just take a peak. */ |
| if (read_msr_safe(MSR_TURBO_RATIO_LIMIT, &turbo_ratio_limit)) |
| return; |
| /* The lowest byte is the max turbo ratio achievable by one active core. */ |
| set_pstate(turbo_ratio_limit & 0xff); |
| } |
| |
| /* This returns the desired pstate, which might be less than desired if other |
| * cores are active. */ |
| unsigned int get_pstate(void) |
| { |
| uint64_t perf_ctl; |
| |
| if (read_msr_safe(MSR_IA32_PERF_CTL, &perf_ctl)) |
| return 0; |
| return (perf_ctl & 0xff00) >> 8; |
| } |
| |
| void set_cstate(unsigned int cstate) |
| { |
| /* No real need to lock for an assignment. Any core can set this, and other |
| * cores will notice the next time they halt. */ |
| x86_cstate = cstate; |
| } |
| |
| unsigned int get_cstate(void) |
| { |
| /* We won't be able to use anything deeper than C1 without MWAIT */ |
| if (!cpu_has_feat(CPU_FEAT_X86_MWAIT)) |
| return X86_MWAIT_C1; |
| return x86_cstate; |
| } |