blob: 101de50a4d314d2aa9eaf1c6ce96fb02e7cb34a8 [file] [log] [blame]
#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. It returns with IRQs off.
*
* 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");
}
disable_irq();
}
/* Atomically enables interrupts and halts. It will wake if notif_pending was
* set (racy), and if we have mwait, it will wake if notif_pending gets set.
* It returns with IRQs off. */
void cpu_halt_notif_pending(struct preempt_data *vcpd)
{
if (cpu_has_feat(CPU_FEAT_X86_MWAIT))
asm volatile("monitor" :
: "a"(&vcpd->notif_pending), "c"(0), "d"(0));
if (vcpd->notif_pending)
return;
/* Note we don't use the ecx=1 setting - we actually want to sti so that
* we handle the IRQ and not just wake from it. */
if (cpu_has_feat(CPU_FEAT_X86_MWAIT))
asm volatile("sti; mwait" :
: "c"(0x0), "a"(x86_cstate) : "memory");
else
asm volatile("sti; hlt" : : : "memory");
disable_irq();
}
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;
}
unsigned int get_actual_pstate(void)
{
uint64_t perf_status;
if (read_msr_safe(MSR_IA32_PERF_STATUS, &perf_status))
return 0;
return (perf_status & 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;
}