Files
MiniVisorPkg/Sources/Utils.c
2020-02-22 13:54:50 -08:00

223 lines
6.2 KiB
C

/*!
@file Utils.c
@brief Utility functions that could be used by both on root and non-root
operations.
@author Satoshi Tanda
@copyright Copyright (c) 2020 - , Satoshi Tanda. All rights reserved.
*/
#include "Utils.h"
#include "Asm.h"
_Use_decl_annotations_
UINT64
ComputeAddressFromIndexes (
UINT32 Pml4Index,
UINT32 PdptIndex,
UINT32 PdIndex,
UINT32 PtIndex
)
{
ADDRESS_TRANSLATION_HELPER helper;
helper.AsUInt64 = 0;
helper.AsIndex.Pml4 = Pml4Index;
helper.AsIndex.Pdpt = PdptIndex;
helper.AsIndex.Pd = PdIndex;
helper.AsIndex.Pt = PtIndex;
return helper.AsUInt64;
}
UINT32
GetSegmentAccessRight (
_In_ UINT16 SegmentSelector
)
{
SEGMENT_SELECTOR segmentSelector;
UINT32 nativeAccessRight;
VMX_SEGMENT_ACCESS_RIGHTS accessRight;
segmentSelector.Flags = SegmentSelector;
//
// "In general, a segment register is unusable if it has been loaded with a
// null selector."
// See: 24.4.1 Guest Register State
//
if ((segmentSelector.Table == 0) &&
(segmentSelector.Index == 0))
{
accessRight.Flags = 0;
accessRight.Unusable = TRUE;
goto Exit;
}
//
// Convert the native access right to the format for VMX. Those two formats
// are almost identical except that first 8 bits of the native format does
// not exist in the VMX format, and that few fields are undefined in the
// native format but reserved to be zero in the VMX format.
//
nativeAccessRight = AsmLoadAccessRightsByte(SegmentSelector);
MV_ASSERT(nativeAccessRight);
accessRight.Flags = (nativeAccessRight >> 8);
accessRight.Reserved1 = 0;
accessRight.Reserved2 = 0;
accessRight.Unusable = FALSE;
Exit:
return accessRight.Flags;
}
/*!
@brief Returns the segment descriptor corresponds to the SegmentSelector.
@param[in] DescriptorTableBase - An address of the base of the descriptor
table.
@param[in] SegmentSelector - A segment selector value.
@return The segment descriptor corresponds to the SegmentSelector.
*/
static
SEGMENT_DESCRIPTOR_32*
GetSegmentDescriptor (
_In_ UINT64 DescriptorTableBase,
_In_ UINT16 SegmentSelector
)
{
SEGMENT_SELECTOR segmentSelector;
SEGMENT_DESCRIPTOR_32* segmentDescriptors;
//
// "Selects one of 8192 descriptors in the GDT or LDT. The processor multiplies
// the index value by 8 (the number of bytes in a segment descriptor) and
// adds the result to the base address of the GDT or LDT (from the GDTR or
// LDTR register, respectively)."
// See: 3.4.2 Segment Selectors
//
segmentSelector.Flags = SegmentSelector;
segmentDescriptors = (SEGMENT_DESCRIPTOR_32*)DescriptorTableBase;
return &segmentDescriptors[segmentSelector.Index];
}
/*!
@brief Returns the base address of SegmentDescriptor.
@param[in] SegmentDescriptor - The segment descriptor from which retrieve
the base address.
@return The base address of SegmentDescriptor.
*/
static
UINT64
GetSegmentBaseByDescriptor (
_In_ CONST SEGMENT_DESCRIPTOR_32* SegmentDescriptor
)
{
UINT64 segmentBase;
UINT64 baseHigh, baseMiddle, baseLow;
baseHigh = ((UINT64)SegmentDescriptor->BaseAddressHigh) << (6 * 4);
baseMiddle = ((UINT64)SegmentDescriptor->BaseAddressMiddle) << (4 * 4);
baseLow = SegmentDescriptor->BaseAddressLow;
segmentBase = (baseHigh | baseMiddle | baseLow) & MAXUINT32;
//
// Few system descriptors are expanded to 16 bytes on x64. For practical
// reasons, we only detect TSS descriptors (that is the System field is
// cleared, and the Type field has either one of specific values).
//
// See: 3.5.2 Segment Descriptor Tables in IA-32e Mode
//
if ((SegmentDescriptor->System == 0) &&
((SegmentDescriptor->Type == SEGMENT_DESCRIPTOR_TYPE_TSS_AVAILABLE) ||
(SegmentDescriptor->Type == SEGMENT_DESCRIPTOR_TYPE_TSS_BUSY)))
{
CONST SEGMENT_DESCRIPTOR_64* descriptor64;
descriptor64 = (CONST SEGMENT_DESCRIPTOR_64*)SegmentDescriptor;
segmentBase |= ((UINT64)descriptor64->BaseAddressUpper << 32);
}
return segmentBase;
}
UINT64
GetSegmentBase (
_In_ UINT64 DescriptorTableBase,
_In_ UINT16 SegmentSelector
)
{
UINT64 segmentBase;
SEGMENT_SELECTOR segmentSelector;
segmentSelector.Flags = SegmentSelector;
if ((segmentSelector.Table == 0) &&
(segmentSelector.Index == 0))
{
//
// The null segment selectors technically does not point to a valid
// segment descriptor, hence no valid base address either. We return
// 0 for convenience, however.
//
// "The first entry of the GDT is not used by the processor. A segment
// selector that points to this entry of the GDT (that is, a segment
// selector with an index of 0 and the TI flag set to 0) is used as a
// "null segment selector."".
// 3.4.2 Segment Selectors
//
segmentBase = 0;
goto Exit;
}
//
// For practical reasons, we do not support LDT. This will not be an issue
// as we are running as a SYSTEM which will not use LDT.
//
// "Specifies the descriptor table to use: clearing this flag selects the GDT;
// setting this flag selects the current LDT."
// See: 3.4.2 Segment Selectors
//
MV_ASSERT(segmentSelector.Table == 0);
segmentBase = GetSegmentBaseByDescriptor(GetSegmentDescriptor(DescriptorTableBase,
SegmentSelector));
Exit:
return segmentBase;
}
_Use_decl_annotations_
CR0
AdjustCr0 (
CR0 Cr0
)
{
CR0 newCr0, fixed0Cr0, fixed1Cr0;
newCr0 = Cr0;
fixed0Cr0.Flags = __readmsr(IA32_VMX_CR0_FIXED0);
fixed1Cr0.Flags = __readmsr(IA32_VMX_CR0_FIXED1);
newCr0.Flags &= fixed1Cr0.Flags;
newCr0.Flags |= fixed0Cr0.Flags;
return newCr0;
}
_Use_decl_annotations_
CR4
AdjustCr4 (
CR4 Cr4
)
{
CR4 newCr4, fixed0Cr4, fixed1Cr4;
newCr4 = Cr4;
fixed0Cr4.Flags = __readmsr(IA32_VMX_CR4_FIXED0);
fixed1Cr4.Flags = __readmsr(IA32_VMX_CR4_FIXED1);
newCr4.Flags &= fixed1Cr4.Flags;
newCr4.Flags |= fixed0Cr4.Flags;
return newCr4;
}