; $Id$
;; @file
; CPUM - Patch Helpers.
;
; Copyright (C) 2015 Oracle Corporation
;
; This file is part of VirtualBox Open Source Edition (OSE), as
; available from http://www.virtualbox.org. This file is free software;
; you can redistribute it and/or modify it under the terms of the GNU
; General Public License (GPL) as published by the Free Software
; Foundation, in version 2 as it comes in the "COPYING" file of the
; VirtualBox OSE distribution. VirtualBox OSE is distributed in the
; hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
;
;*******************************************************************************
;* Header Files *
;*******************************************************************************
%include "VBox/asmdefs.mac"
%include "VBox/vmm/cpum.mac"
%include "CPUMInternal.mac"
%include "VBox/vmm/vm.mac"
%include "VMMRC.mac"
;*******************************************************************************
;* External Symbols *
;*******************************************************************************
extern IMPNAME(g_VM)
BEGIN_PATCH_HLP_SEG
;;
; Helper for PATMCpuidReplacement.
;
; We have at most 32 bytes of stack to play with, .
;
; @input eax, ecx(, edx, ebx)
; @output eax, ebx, ecx, ebx
;
; @uses eflags (caller saves them)
;
BEGINPROC_EXPORTED CPUMPatchHlpCpuId
; Save the registers we use for passthru and sub-leaf matching (eax is not used).
push edx
push ecx
push ebx
; Use edi as VM pointer.
push edi
mov edi, IMP_SEG(ss, g_VM)
%define CPUMCPUIDLEAF_SIZE_LOG2 5 ; ASSUMES CPUMCPUIDLEAF_size == 32
;
; Perform a binary search looking for leaf with the EAX value.
;
mov edx, [ss:edi + VM.cpum + CPUM.GuestInfo + CPUMINFO.cCpuIdLeaves]
mov ecx, [ss:edi + VM.cpum + CPUM.GuestInfo + CPUMINFO.paCpuIdLeavesRC]
test edx, edx
jz cpuid_unknown
shl edx, CPUMCPUIDLEAF_SIZE_LOG2
add edx, ecx ; edx = end (exclusive); ecx = start.
cpuid_lookup_leaf:
; Find the middle element
mov ebx, edx
cpuid_lookup_leaf_ebx_loeaded:
sub ebx, ecx
shr ebx, 1 + CPUMCPUIDLEAF_SIZE_LOG2
shl ebx, CPUMCPUIDLEAF_SIZE_LOG2
add ebx, ecx ; ebx = middle element.
; Compare.
cmp eax, [ss:ebx + CPUMCPUIDLEAF.uLeaf]
jae cpuid_lookup_split_up
; The leaf is before ebx.
cpuid_lookup_split_down:
cmp ecx, ebx ; start == middle? if so, we failed.
mov edx, ebx ; end = middle;
jne cpuid_lookup_leaf_ebx_loeaded
jmp cpuid_unknown
; The leaf is at or after ebx.
cpuid_lookup_split_up:
je cpuid_match_eax
lea ecx, [ebx + CPUMCPUIDLEAF_size] ; start = middle + 1
cmp ecx, edx ; middle + 1 == start? if so, we failed.
jne cpuid_lookup_leaf
jmp cpuid_unknown
;
; We've to a matching leaf, does the sub-leaf match too?
;
cpuid_match_eax:
mov ecx, [esp + 4]
and ecx, [ss:ebx + CPUMCPUIDLEAF.fSubLeafMask]
cmp ecx, [ss:ebx + CPUMCPUIDLEAF.uSubLeaf]
je cpuid_fetch
ja cpuid_lookup_subleaf_forwards
;
; Search backwards.
;
cpuid_lookup_subleaf_backwards:
mov edx, [ss:edi + VM.cpum + CPUM.GuestInfo + CPUMINFO.paCpuIdLeavesRC] ; edx = first leaf
cpuid_lookup_subleaf_backwards_loop:
cmp ebx, edx ; Is there a leaf before the current?
jbe cpuid_subleaf_not_found ; If not we're out of luck.
cmp eax, [ss:ebx - CPUMCPUIDLEAF_size + CPUMCPUIDLEAF.uLeaf]
jne cpuid_subleaf_not_found ; If the leaf before us does not have the same leaf number, we failed.
sub ebx, CPUMCPUIDLEAF_size
cmp ecx, [ss:ebx + CPUMCPUIDLEAF.uSubLeaf]
je cpuid_fetch ; If the subleaf matches, we're good!.
jb cpuid_lookup_subleaf_backwards_loop ; Still hope if the subleaf we're seeking is smaller.
jmp cpuid_subleaf_not_found ; Too bad.
;
; Search forward until we've got a matching sub-leaf (or not).
;
cpuid_lookup_subleaf_forwards:
; Calculate the last leaf address.
mov edx, [ss:edi + VM.cpum + CPUM.GuestInfo + CPUMINFO.cCpuIdLeaves]
dec edx
shl edx, CPUMCPUIDLEAF_SIZE_LOG2
add edx, [ss:edi + VM.cpum + CPUM.GuestInfo + CPUMINFO.paCpuIdLeavesRC] ; edx = last leaf (inclusive)
cpuid_subleaf_lookup:
cmp ebx, edx
jae cpuid_subleaf_not_found
cmp eax, [ss:ebx + CPUMCPUIDLEAF_size + CPUMCPUIDLEAF.uLeaf]
jne cpuid_subleaf_not_found
add ebx, CPUMCPUIDLEAF_size
cmp ecx, [ss:ebx + CPUMCPUIDLEAF.uSubLeaf]
ja cpuid_subleaf_lookup
je cpuid_fetch
;
; Out of range sub-leaves aren't quite as easy and pretty as we emulate them
; here, but we do an adequate job.
;
cpuid_subleaf_not_found:
xor ecx, ecx
test dword [ss:ebx + CPUMCPUIDLEAF.fFlags], CPUMCPUIDLEAF_F_INTEL_TOPOLOGY_SUBLEAVES
jz cpuid_load_zeros_except_ecx
mov ecx, [esp + 4]
and ecx, 0ffh
cpuid_load_zeros_except_ecx:
xor edx, edx
xor eax, eax
xor ebx, ebx
jmp cpuid_done
;
; Different CPUs have different ways of dealing with unknown CPUID leaves.
;
cpuid_unknown:
mov ebx, IMP_SEG(ss, g_VM)
mov dword [ss:edi + VM.cpum + CPUM.GuestInfo + CPUMINFO.enmUnknownCpuIdMethod], CPUMUNKNOWNCPUID_PASSTHRU
je cpuid_unknown_passthru
; Load the default cpuid leaf.
cpuid_unknown_def_leaf:
mov edx, [ss:edi + VM.cpum + CPUM.GuestInfo + CPUMINFO.DefCpuId + CPUMCPUID.uEdx]
mov ecx, [ss:edi + VM.cpum + CPUM.GuestInfo + CPUMINFO.DefCpuId + CPUMCPUID.uEcx]
mov eax, [ss:edi + VM.cpum + CPUM.GuestInfo + CPUMINFO.DefCpuId + CPUMCPUID.uEax]
mov ebx, [ss:edi + VM.cpum + CPUM.GuestInfo + CPUMINFO.DefCpuId + CPUMCPUID.uEbx]
jmp cpuid_done
; Pass thru the input values unmodified (eax is still virgin).
cpuid_unknown_passthru:
mov edx, [esp + 8]
mov ecx, [esp + 4]
mov ebx, [esp]
jmp cpuid_done
;
; Normal return.
;
cpuid_fetch:
mov edx, [ss:ebx + CPUMCPUIDLEAF.uEdx]
mov ecx, [ss:ebx + CPUMCPUIDLEAF.uEcx]
mov eax, [ss:ebx + CPUMCPUIDLEAF.uEax]
mov ebx, [ss:ebx + CPUMCPUIDLEAF.uEbx]
cpuid_done:
pop edi
add esp, 12
ret
ENDPROC CPUMPatchHlpCpuId