#include <arch_cpu.h>
#include "hyperv_cpu.h"
#include "VMBusPrivate.h"
status_t
vmbus_detect_hyperv()
{
CALLED();
cpu_ent* cpu = get_cpu_struct();
if ((cpu->arch.feature[FEATURE_EXT] & IA32_FEATURE_EXT_HYPERVISOR) == 0) {
TRACE("No hypervisor detected\n");
return B_ERROR;
}
cpuid_info cpuInfo;
get_cpuid(&cpuInfo, IA32_CPUID_LEAF_HYPERVISOR, 0);
if (cpuInfo.regs.eax < IA32_CPUID_LEAF_HV_IMP_LIMITS) {
TRACE("Not running on Hyper-V\n");
return B_ERROR;
}
get_cpuid(&cpuInfo, IA32_CPUID_LEAF_HV_INT_ID, 0);
if (cpuInfo.regs.eax != HV_CPUID_INTERFACE_ID) {
TRACE("Not running on Hyper-V\n");
return B_ERROR;
}
get_cpuid(&cpuInfo, IA32_CPUID_LEAF_HV_FEAT_ID, 0);
TRACE("Hyper-V features: %08" B_PRIX32 ":%08" B_PRIX32 ":%08" B_PRIX32 ":%08" B_PRIX32 "\n",
cpuInfo.regs.eax, cpuInfo.regs.ebx, cpuInfo.regs.ecx, cpuInfo.regs.edx);
if ((cpuInfo.regs.eax & HV_CPUID_EAX_REQUIRED_FEATURES) == 0
|| (cpuInfo.regs.ebx & HV_CPUID_EBX_REQUIRED_FEATURES) == 0) {
TRACE("Missing required Hyper-V features\n");
return B_ERROR;
}
#ifdef TRACE_HYPERV
get_cpuid(&cpuInfo, IA32_CPUID_LEAF_HV_SYS_ID, 0);
TRACE("Hyper-V version: %d.%d.%d [SP%d]\n", cpuInfo.regs.ebx >> 16, cpuInfo.regs.ebx & 0xFFFF,
cpuInfo.regs.eax, cpuInfo.regs.ecx);
#endif
return B_OK;
}
status_t
VMBus::_EnableHypercalls()
{
x86_write_msr(IA32_MSR_HV_GUEST_OS_ID, IA32_MSR_HV_GUEST_OS_ID_FREEBSD);
uint64 msr = x86_read_msr(IA32_MSR_HV_HYPERCALL);
msr = ((fHyperCallPhys >> HV_PAGE_SHIFT) << IA32_MSR_HV_HYPERCALL_PAGE_SHIFT)
| (msr & IA32_MSR_HV_HYPERCALL_RSVD_MASK) | IA32_MSR_HV_HYPERCALL_ENABLE;
x86_write_msr(IA32_MSR_HV_HYPERCALL, msr);
msr = x86_read_msr(IA32_MSR_HV_HYPERCALL);
if ((msr & IA32_MSR_HV_HYPERCALL_ENABLE) == 0)
return B_ERROR;
TRACE("Hypercalls enabled at %p\n", fHypercallPage);
return B_OK;
}
void
VMBus::_DisableHypercalls()
{
uint64 msr = x86_read_msr(IA32_MSR_HV_HYPERCALL);
msr &= IA32_MSR_HV_HYPERCALL_RSVD_MASK;
x86_write_msr(IA32_MSR_HV_HYPERCALL, msr);
TRACE("Hypercalls disabled\n");
}
uint16
VMBus::_HypercallPostMessage(phys_addr_t physAddr)
{
uint64 status;
#if defined(__i386__)
__asm __volatile("call *%5"
: "=A" (status)
: "d" (0), "a" (HYPERCALL_POST_MESSAGE), "b" (0), "c" (static_cast<uint32>(physAddr)),
"m" (fHypercallPage));
#elif defined(__x86_64__)
__asm __volatile("call *%3"
: "=a" (status)
: "c" (HYPERCALL_POST_MESSAGE), "d" (physAddr), "m" (fHypercallPage));
#endif
return (uint16)(status & 0xFFFF);
}
uint16
VMBus::_HypercallSignalEvent(uint32 connId)
{
uint64 status;
#if defined(__i386__)
__asm __volatile("call *%5"
: "=A" (status)
: "d" (0), "a" (HYPERCALL_SIGNAL_EVENT), "b" (0), "c" (connId), "m" (fHypercallPage));
#elif defined(__x86_64__)
__asm __volatile("call *%3"
: "=a" (status)
: "c" (HYPERCALL_SIGNAL_EVENT), "d" (connId), "m" (fHypercallPage));
#endif
return (uint16)(status & 0xFFFF);
}
void
VMBus::_EnableInterruptCPU(int32 cpu)
{
phys_addr_t messagesPhys = fCPUMessagesPhys + (sizeof(*fCPUMessages) * cpu);
phys_addr_t eventFlagsPhys = fCPUEventFlagsPhys + (sizeof(*fCPUEventFlags) * cpu);
uint64 msr = x86_read_msr(IA32_MSR_HV_SIMP);
msr = ((messagesPhys >> HV_PAGE_SHIFT) << IA32_MSR_HV_SIMP_PAGE_SHIFT)
| (msr & IA32_MSR_HV_SIMP_RSVD_MASK) | IA32_MSR_HV_SIMP_ENABLE;
x86_write_msr(IA32_MSR_HV_SIMP, msr);
TRACE("cpu%u: simp new msr 0x%llX\n", cpu, (unsigned long long)msr);
msr = x86_read_msr(IA32_MSR_HV_SIEFP);
msr = ((eventFlagsPhys >> HV_PAGE_SHIFT) << IA32_MSR_HV_SIEFP_PAGE_SHIFT)
| (msr & IA32_MSR_HV_SIEFP_RSVD_MASK) | IA32_MSR_HV_SIEFP_ENABLE;
x86_write_msr(IA32_MSR_HV_SIEFP, msr);
TRACE("cpu%u: siefp new msr 0x%llX\n", cpu, (unsigned long long)msr);
msr = x86_read_msr(IA32_MSR_HV_SINT0 + VMBUS_SINT_MESSAGE);
msr = fInterruptVector | (msr & IA32_MSR_HV_SINT_RSVD_MASK);
x86_write_msr(IA32_MSR_HV_SINT0 + VMBUS_SINT_MESSAGE, msr);
TRACE("cpu%u: sint%u new msr 0x%llX\n", VMBUS_SINT_MESSAGE, cpu, (unsigned long long)msr);
msr = x86_read_msr(IA32_MSR_HV_SINT0 + VMBUS_SINT_TIMER);
msr = fInterruptVector | (msr & IA32_MSR_HV_SINT_RSVD_MASK);
x86_write_msr(IA32_MSR_HV_SINT0 + VMBUS_SINT_TIMER, msr);
TRACE("cpu%u: sint%u new msr 0x%llX\n", VMBUS_SINT_TIMER, cpu, (unsigned long long)msr);
msr = x86_read_msr(IA32_MSR_HV_SCONTROL);
msr = (msr & IA32_MSR_HV_SCONTROL_RSVD_MASK) | IA32_MSR_HV_SCONTROL_ENABLE;
x86_write_msr(IA32_MSR_HV_SCONTROL, msr);
TRACE("cpu%u: scontrol new msr 0x%llX\n", cpu, (unsigned long long)msr);
}
void
VMBus::_DisableInterruptCPU(int32 cpu)
{
uint64 msr = x86_read_msr(IA32_MSR_HV_SCONTROL);
msr &= IA32_MSR_HV_SCONTROL_RSVD_MASK;
x86_write_msr(IA32_MSR_HV_SCONTROL, msr);
TRACE("cpu%u: scontrol new msr 0x%llX\n", cpu, (unsigned long long)msr);
msr = x86_read_msr(IA32_MSR_HV_SIMP);
msr &= IA32_MSR_HV_SIMP_RSVD_MASK;
x86_write_msr(IA32_MSR_HV_SIMP, msr);
TRACE("cpu%u: simp new msr 0x%llX\n", cpu, (unsigned long long)msr);
msr = x86_read_msr(IA32_MSR_HV_SIEFP);
msr &= IA32_MSR_HV_SIEFP_RSVD_MASK;
x86_write_msr(IA32_MSR_HV_SIEFP, msr);
TRACE("cpu%u: siefp new msr 0x%llX\n", cpu, (unsigned long long)msr);
msr = x86_read_msr(IA32_MSR_HV_SINT0 + VMBUS_SINT_MESSAGE);
msr = IA32_MSR_HV_SINT_MASKED | (msr & IA32_MSR_HV_SINT_RSVD_MASK);
x86_write_msr(IA32_MSR_HV_SINT0 + VMBUS_SINT_MESSAGE, msr);
TRACE("cpu%u: sint%u new msr 0x%llX\n", VMBUS_SINT_MESSAGE, cpu, (unsigned long long)msr);
msr = x86_read_msr(IA32_MSR_HV_SINT0 + VMBUS_SINT_TIMER);
msr = IA32_MSR_HV_SINT_MASKED | (msr & IA32_MSR_HV_SINT_RSVD_MASK);
x86_write_msr(IA32_MSR_HV_SINT0 + VMBUS_SINT_TIMER, msr);
TRACE("cpu%u: sint%u new msr 0x%llX\n", VMBUS_SINT_TIMER, cpu, (unsigned long long)msr);
}
void
VMBus::_SignalEom(void*, int)
{
x86_write_msr(IA32_MSR_HV_EOM, 0);
}