/******************************************************************************
*   Module:     Scanner.c                                                     *
*   Revision:   1.00                                                          *
*   Date:       4/5/2000                                                      *
*   Author:     Goran Devic                                                   *
*******************************************************************************

    Module Description:

        This module contains the Scan-Before-Execute code.

    Meta-page contains clue-bytes mapped to the equivalent offsets within
    a page to the corresponding code page.

    The format of a meta-byte is:
    Ŀ
     7  6  5  4  3  2  1  0
    
              |   |   |   |   |   +--+  Instruction len in bytes [1,15]
              |   |   |   |   +------|
              |   |   |   +----------|
              |   |   +--------------+
              |   |
              |   +------------------+  Instruction behaviour:
              +----------------------+     0 - normal instruction
                                           1 - cross-page: do single step
                                           2 - terminating
                                           3 - illegal instruction

    Whole byte=0 means opcode not yet scanned.

*******************************************************************************
*   Changes:                                                                  *
*   DATE     DESCRIPTION OF CHANGES                               AUTHOR      *
* --------   ---------------------------------------------------  ----------- *
* 4/5/2000   Original                                             Goran Devic *
* --------   ---------------------------------------------------  ----------- *
*******************************************************************************
*   Include Files                                                             *
******************************************************************************/
#include <strmini.h>                // Include driver level C functions
#include <ntddk.h>                  // Include ddk function header file
#include "Vm.h"                     // Include root header file
#include "Scanner.h"                // Include scanner defines
#include "DisassemblerInterface.h"

/******************************************************************************
*   Global Variables                                                          *
******************************************************************************/
TDisassembler Dis;
TDisassembler *pDis = &Dis;

/******************************************************************************
*   Local Defines, Variables and Macros                                       *
******************************************************************************/
static DWORD dwMetaPageBase;
static DWORD dwCSIPPageBase;
static BYTE  szDisasm[256];

/******************************************************************************
*   Functions                                                                 *
******************************************************************************/
extern int ScanOneInstruction();
static void ScanOneLeg( BYTE *pCSIP );

/******************************************************************************
*   DWORD GetDisFlags(void)                                                   *
*******************************************************************************
*   Returns the flags containing the data/address mode of operation.
*
*   Returns:
*       DIS_DATA32 | DIS_ADDRESS32
******************************************************************************/
DWORD GetDisFlags(void)
{
    // Determine the current code and data bit-ness: it is 16 bit unless
    // the mode is PM and cs selector is 32-bit

    if( !(pMonitor->guestTSS.eflags & VM_MASK)
        && pMonitor->GDT[ pMonitor->guestTSS.cs>>3 ].size32 )
        return(DIS_ADDRESS32 | DIS_DATA32);

    return(0);                  // 16 bit code and data
}

//
// Helper function: Disassemble single line that CS:EIP points at
//
void DisassembleCSIP()
{
    Dis.bpEffTarget = (BYTE *)(pMonitor->guestTSS.cs << 4) + pMonitor->guestTSS.eip;
    Dis.bpTarget    = Mem.MemVM.linear + Dis.bpEffTarget;
    Dis.szDisasm    = szDisasm;
    Dis.dwFlags     = GetDisFlags();
    Disassembler( &Dis );

    DbgPrint(("%04X:%04X   ", pMonitor->guestTSS.cs, pMonitor->guestTSS.eip));
    Dis.nScanEnum = Dis.bInstrLen;
    for( ; Dis.bInstrLen>0 ;Dis.bInstrLen-- )
        DbgPrint((" %02X", *(BYTE*)Dis.bpTarget++ ));
    for( ;Dis.nScanEnum<6 ;Dis.nScanEnum++ )
        DbgPrint(("   "));
    DbgPrint(("  %s\n", Dis.szDisasm ));
}

/******************************************************************************
*   int ScanCodePage( BYTE *pCSIP, BYTE *pMetaPage, DWORD Mode )              *
*******************************************************************************

    This function scans the currently-executing code page starting at the
    given linear location.

    This scanner will scan until a terminating condition is reached:
        1) current instruction crossed a page boundary
        2) terminal instruction is reached (one that needs virtualization)
        3) current instruction is already scanned
        4) invalid opcode

    As additional performance improvement, certain decoded instructions will
    cause the following to happen:
        Short displacement jump on condition 0x70-0x7F
            (always a byte displacement)
         -> scan recursively both paths until a terminal condition
        Long displacement jump on condition 0x0F & 0x80-0x8F
            (word or doubleword displacement)
         -> scan recursively both paths until a terminal condition
        LOOPxx instructions 0xE0-0xE3
            (always a byte displacement)
         -> scan recursively both paths until a terminal condition
        CALL relative offset 0xE8
            (word or doubleword displacement)
         -> scan recursively both paths until a terminal condition
        JMP relative offset 0xE9
            (word or doubleword displacement)
         -> start scanning from the new offset until a terminal condition
        JMP relative offset 0xEB
            (always a byte displacement)
         -> start scanning from the new offset until a terminal condition

*******************************************************************************
*   Where:
*       pCSIP is the starting address to scan
*       pMetaPage is the base pointer to a meta page
*       dwMode may contain:
*               bit 0 - when set, 32 bit data
*               bit 1 - when set, 32 bit code
*   Returns:
*       0
******************************************************************************/
BYTE *baseCSIP;
WORD xxcs = 0;
DWORD xxeip = 0xFFFFFFFF;
DWORD delta;

int ScanCodePage( BYTE *pCSIP, BYTE *pMetaPage, DWORD Mode )
{
    ASSERT(((int)pMetaPage & 0xFFF) == 0);

    dwMetaPageBase = (int)pMetaPage;
    dwCSIPPageBase = (int)pCSIP & ~0xFFF;

#if DBG
    if( fSingleStep )
    {
        // Disassemble single line that CS:EIP points at

        Dis.bpTarget    = pCSIP;
        Dis.bpEffTarget = (BYTE *)(pMonitor->guestTSS.cs << 4) + pMonitor->guestTSS.eip;
        Dis.szDisasm    = szDisasm;
        Dis.dwFlags     = GetDisFlags();
        Disassembler( &Dis );

        DbgPrint(("%04X:%04X   ", pMonitor->guestTSS.cs, pMonitor->guestTSS.eip));
        DbgPrint(("  %s\n", Dis.szDisasm ));
    }
    baseCSIP = pCSIP;
#endif

    ScanOneLeg( pCSIP );

    return( 0 );
}

static int stopdisat=0;

static void ScanOneLeg( BYTE *pCSIP )
{
    BYTE * pMetaPage = (BYTE *) (dwMetaPageBase + ((DWORD)pCSIP & 0xFFF));
    int nLen;

    while(1)
    {
        // Make sure the instruction is not already scanned

        if( *pMetaPage != 0 )
        {
            return;
        }

        if( stopdisat==(int)pCSIP ) Breakpoint;
        delta = (DWORD) (pCSIP - baseCSIP);
        if( (xxcs==pMonitor->guestTSS.cs) && (xxeip==pMonitor->guestTSS.eip+delta) )
            _asm int 3;
        if( 0 && (pMonitor->guestTSS.cs==0xC000 || pMonitor->guestTSS.cs==0xF000) ) goto skipit;
        if( !fSingleStep )
        {
            // Disassemble instruction and print it to a debug out device
            if( !fSilent )
            {
                Dis.bpTarget    = pCSIP;
                Dis.bpEffTarget = (BYTE*) CSIPtoVmLinear();
                Dis.szDisasm    = szDisasm;
                Dis.dwFlags     = GetDisFlags();
                Disassembler( &Dis );

                DbgPrint(("%04X:%04X   ", pMonitor->guestTSS.cs, pMonitor->guestTSS.eip + delta));
                Dis.nScanEnum = Dis.bInstrLen;
                for( ; Dis.bInstrLen>0 ;Dis.bInstrLen-- )
                    DbgPrint((" %02X", *(BYTE*)Dis.bpTarget++ ));
                for( ;Dis.nScanEnum<6 ;Dis.nScanEnum++ )
                    DbgPrint(("   "));
                DbgPrint(("  %s\n", Dis.szDisasm ));
            }
        }
        // Use a cut version of disassembler to get the instr. stats
        // We either scan pages for 16 bit real/v86 mode, or 16/32 bit PM

        Dis.dwFlags = GetDisFlags();
        Dis.bpTarget = pCSIP;

        nLen = ScanOneInstruction();

        ASSERT(nLen < 16);

        // Is the instruction legal ?
        // "If not, I will make it legal." -- Senator Palpatine, The Phantom Menace
        if( nLen==0 )
        {
            // Illegal instruction, set the undefined instrution meta-byte

            ASSERT(0);      // For now scream if found illigal instruction !  Good error catcher!

            *pMetaPage = (META_UNDEF << 4) + nLen;
            *pCSIP     = INT3;

            return;
        }

        if( ((DWORD)(pCSIP + nLen) & ~0xFFF) != dwCSIPPageBase )
        {
            // Current instruction spans out of this page...
            if( Dis.nScanEnum==SCAN_TERMINATING )
            {
                // Instruction was terminating as well

                *pMetaPage = (META_TERMINATING << 4) + nLen;
                *pCSIP     = INT3;

                return;
            }
            else
            {
                // Single step over it to bring us to a new page
                *pMetaPage = (META_SINGLE_STEP << 4) + nLen;
                *pCSIP     = INT3;

                return;
            }
        }

        // If the instruction can be optimized, try to do it
        switch( Dis.nScanEnum )
        {
            case SCAN_NATIVE:  // Normal instruction
                    // Set the instruction meta byte to execute it natively

                    *pMetaPage = (BYTE) nLen;

                break;

            case SCAN_COND_JUMP:    // See if we can evaluate both paths
                    if( ((DWORD)(pCSIP + nLen + Dis.nDisplacement) & ~0xFFF)==dwCSIPPageBase )
                    {
                        // OK for a conditional jump or a call..

                        *pMetaPage = (BYTE) nLen;

                        // ..it is on the same page; call a branch recursively...

                        ScanOneLeg( pCSIP + nLen + Dis.nDisplacement );

                        // ...And proceed with the next instruction
                    }
                    else
                    {   // Conditional jump is out-of-page, so we need to stop on it
                        // Single-step will give us a chance to follow it

                        *pMetaPage = (META_SINGLE_STEP << 4) + nLen;
                        *pCSIP     = INT3;

                        return;
                    }
                break;

            case SCAN_JUMP:     // Simple jump to a new offset.. Check the new address
                    if( ((DWORD)(pCSIP + nLen + Dis.nDisplacement) & ~0xFFF)==dwCSIPPageBase )
                    {
                        // OK to jump... we are on the same page
                        // Set the instruction meta byte to execute it natively
                        *pMetaPage = (BYTE) nLen;

                        // Move on to the next instruction (nLen will be added below)
                        pCSIP     += Dis.nDisplacement;
                        pMetaPage += Dis.nDisplacement;

                        break;
                    }
                    else
                    {   // Nope! This jump is out-of-page
                        // Single-step will give us a chance to follow it
                        *pMetaPage = (META_SINGLE_STEP << 4) + nLen;
                        *pCSIP     = INT3;

                        return;
                    }

            case SCAN_TERMINATING:
                    // Nothing we can do here.. it's a terminating instruction
                    *pMetaPage = (META_TERMINATING << 4) + nLen;
                    *pCSIP     = INT3;

                    return;

            case SCAN_TERM_PMODE:
                    // These instructions are terminating only in protected mode
                    if( CPU.CPU_mode == CPU_KERNEL_MODE )
                    {
                        *pMetaPage = (META_TERMINATING << 4) + nLen;
                        *pCSIP     = INT3;

                        return;
                    }
                    // In V86 and 'Real' mode it is executed natively
                    *pMetaPage = (BYTE) nLen;
                break;

            case SCAN_SINGLE_STEP:  // Single step over this instruction
                    // Single-step will give us a chance to follow it wherever it goes
                    *pMetaPage = (META_SINGLE_STEP << 4) + nLen;
                    *pCSIP     = INT3;
                    return;
            default:    // We should not be here
                    ASSERT(0);
                break;
        }

        pCSIP     += nLen;
        pMetaPage += nLen;
    }
}