/*!
    @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;
}
