/*++

Copyright (c) 1992  Microsoft Corporation

Module Name:

    ia64 pmc

Abstract:

    KD Extension Api

Author:

    Thierry Fevrier (v-thief)

Environment:

    User Mode.

Revision History:

--*/

#include "precomp.h"
#pragma hdrstop
#include "ia64.h"

// 
// EmPmcFields: EM register fields for the Performance Monitor Counter register.
//

EM_REG_FIELD EmGenPmcFields[] = {
        { "plm", "Privilege Level Mask"   , 0x4, 0 },   // 0-3
        { "ev",  "External Visibility"  , 0x1, 4 },   // 4
        { "oi",  "Overflow Interrupt", 0x1, 5 }, // 5
        { "pm",  "Privileged Monitor", 0x1, 6 }, // 6
        { "ig",  "ignored1", 0x1, 7 }, // 7
        { "es",  "Event Selection", 0x7, 8 }, // 8-14
        { "ig",  "ignored2", 0x1, 15 }, // 15
        { "umk", "Unit Mask",    0x4, 16 }, // 16-19
        { "thr", "Event Threshold", 0x3, 20 }, // 20-[21-22]
        { "ig",  "ignored3", 0x1, 23 }, // 23
        { "ism", "Instruction Set Mask", 0x2, 24 }, // 24-25
        { "ig",  "ignored4", 0x26, 26 } // 26-63
};

EM_REG_FIELD EmBtbPmcFields[] = {
        { "plm", "Privilege Level Mask", 0x4, 0 }, // 0-3
        { "ign", "ignored1", 0x2, 4 }, // 4-5
        { "pm" , "Privileged Monitor", 0x1, 6}, // 6
        { "tar", "Target Address Register", 0x1, 7 }, // 7
        { "tm" , "Taken Mask", 0x2, 8 }, // 8-9
        { "ptm", "Predicted Target Address Mask", 0x2, 10 }, // 10-11
        { "ppm", "Predicted Predicate Mask", 0x2, 12 }, // 12-13
        { "bpt", "Branch Prediction Table", 0x1, 14 }, // 14
        { "bac", "Branch Address Calculator", 0x1, 15 }, // 15
        { "ign", "ignored2", 0x30, 16 } // 16-63
};

EM_REG_FIELD EmBtbPmdFields[] = {
        { "b",   "Branch Bit", 0x1, 0 }, // 0
        { "mp",  "Mispredit Bit", 0x1, 1 }, // 1
        { "slt", "Instruction Slot", 0x2, 2}, // 2-3
        { "add", "Address", 0x3c, 4 } // 4-63
};

EM_REG_FIELD EmBtbIndexPmdFields[] = {
        { "bbi", "Branch Buffer Index", 0x3, 0 }, // 0-2
        { "ful", "Full Bit", 0x1, 3 }, // 3
        { "ign", "ignored", 0x3c, 4 } // 4-63
};

typedef
VOID
(*PDISPLAY_PMC)(
    IN const PCHAR         Header,
    IN       ULONG64       PmcValue,
    IN       DISPLAY_MODE  DisplayMode
    ); 

VOID
DisplayPmcIA64( 
    IN const PCHAR         Header,
    IN       ULONG64       PmcValue,
    IN       DISPLAY_MODE  DisplayMode
    )
{

    dprintf("%s0x%I64x\n", ( Header ? Header : " pmc: " ), PmcValue );
    return;

} // DisplayPmcIA64()

VOID
DisplayGenPmcIA64( 
    IN const PCHAR         Header,
    IN       ULONG64       PmcValue,
    IN       DISPLAY_MODE  DisplayMode
    )
{
    dprintf("%s", Header ? Header : "" );
    if ( DisplayMode >= DISPLAY_MED )   {
       DisplayFullEmReg( PmcValue, EmGenPmcFields, DisplayMode );
    }
    else   {
        EM_PMC emPmc;

        emPmc = ULong64ToEM_PMC( PmcValue );
        dprintf(
            "plm ev oi pm es umk thr ism\n       "
            "%1I64x   %1I64x  %1I64x  %1I64x %2I64x  %1I64x   %1I64x   %1I64x\n",
            emPmc.plm,
            emPmc.ev,
            emPmc.oi,
            emPmc.pm,
            emPmc.es,
            emPmc.umask,
            emPmc.threshold,
            emPmc.ism
            );
    }
    return;
} // DisplayGenPmcIA64()

VOID
DisplayBtbPmcIA64( 
    IN const PCHAR         Header,
    IN       ULONG64       PmcValue,
    IN       DISPLAY_MODE  DisplayMode
    )
{
    dprintf("%s", Header ? Header : "" );
    if ( DisplayMode >= DISPLAY_MED )   {
       DisplayFullEmReg( PmcValue, EmBtbPmcFields, DisplayMode );
    }
    else   {
        EM_BTB_PMC emPmc;

        emPmc = ULong64ToEM_BTB_PMC( PmcValue );
        dprintf(
            "plm pm tar tm ptm ppm bpt bac\n       "
            "%1I64x   %1I64x  %1I64x   %1I64x  %1I64x   %1I64x   %1I64x   %1I64x\n",
            emPmc.plm,
            emPmc.pm,
            emPmc.tar,
            emPmc.tm,
            emPmc.ptm,
            emPmc.ppm,
            emPmc.bpt,
            emPmc.bac
            );
    }
    return;
} // DisplayBtbPmcIA64()

VOID
DisplayBtbPmdIA64(
    IN const  PCHAR         Header,      // Header string displayed before pmc.
    IN        ULONG64       PmdValue,    // PMD value.
    IN        DISPLAY_MODE  DisplayMode  // Display mode.
    )
{
    

    dprintf("%s", Header ? Header : "" );
    if ( DisplayMode >= DISPLAY_MED )   {
       DisplayFullEmReg( PmdValue, EmBtbPmdFields, DisplayMode );
    }
    else    {
        EM_BTB_PMD btbPmd;
        ULONG64    brAddress;
    
        btbPmd    = ULong64ToEM_BTB_PMD( PmdValue );
#define ITANIUM_PA_ADDRESSING_MASK     0x80000FFFFFFFFFFFUI64 
#define ITANIUM_VA_ADDRESSING_MASK     0xE007FFFFFFFFFFFFUI64
#define ITANIUM_BTB_PMD_ADDRESS_MASK( _pmdValue) \
                    ( ((_pmdValue) & 0xfffffffffffffff0UI64) & ITANIUM_VA_ADDRESSING_MASK )

        brAddress = ITANIUM_BTB_PMD_ADDRESS_MASK( PmdValue );
        dprintf(
            "%1I64x  %1I64x    %1I64x 0x%016I64x ",
            btbPmd.b,
            btbPmd.mp,
            btbPmd.slot,
            brAddress
            );

#if 0
// Thierry 03/19/2001 - BUGBUG
// Itanium Processor Programmer's guide does not present the 
// PMD[8-15] branch/interrupt mode 2 formats.
// I am disabling this check until I can implement it correctly with a context
// that can distinguish between mode 1 and mode 2 at the time this history was created.
        if ( (btbPmd.b == 0) && (btbPmd.mp == 0)) {
            dprintf("<invalid entry>");
        }
        else 
#endif 
        {
            CCHAR    symbol[256];
            PCHAR    s;
            ULONG64  displacement;

            symbol[0] = '!';
            GetSymbol( brAddress, symbol, &displacement);
            s = (PCHAR)symbol + strlen( (PCHAR)symbol );
            if (s == (PCHAR)symbol ) {
                sprintf( s, (IsPtr64() ? "0x%016I64x" : "0x%08x"), brAddress );
            }
            else {
                if ( displacement ) {
                    sprintf( s, (IsPtr64() ? "+0x%I64x" : "+0x%x"), displacement );
                }
            }
            dprintf( "%s", symbol );
        }
        dprintf("\n");
    }

    return;

} // DisplayBtbPmdIA64()

VOID
DisplayBtbIndexPmdIA64(
    IN const PCHAR         Header,
    IN       ULONG64       PmcValue,
    IN       DISPLAY_MODE  DisplayMode
    )
{
    dprintf("%s", Header ? Header : "" );
    if ( DisplayMode >= DISPLAY_MED )   {
       DisplayFullEmReg( PmcValue, EmBtbIndexPmdFields, DisplayMode );
    }
    else   {
        EM_BTB_INDEX_PMD emPmd;

        emPmd = ULong64ToEM_BTB_INDEX_PMD( PmcValue );
        dprintf(
            "bbi full\n             "
            "%1I64x    %1I64x\n",
            emPmd.bbi,
            emPmd.full
            );
    }
    return;
} // DisplayBtbIndexPmdIA64()

PDISPLAY_PMC
GetPmcDisplayFunction(
    char *Str
    )
{
    LONG pmc;
    char *c;

    c = Str;
    while( isalnum( (int)(*c) ) )   {
        c++;
    }
    *c = '\0';
    
    pmc = atol( Str );
    switch( pmc )   {
        case 4:
        case 5:
        case 6:
        case 7:
            return( (PDISPLAY_PMC)DisplayGenPmcIA64 );

        case 12:
            return( (PDISPLAY_PMC)DisplayBtbPmcIA64 );

        default:
            if ( !strcmp( Str, "gen" ) ) {
                return( (PDISPLAY_PMC)DisplayGenPmcIA64 ); 
            }
            if ( !strcmp( Str, "btb" ) ) {
                return( (PDISPLAY_PMC)DisplayBtbPmcIA64 );
            }
            return ( (PDISPLAY_PMC)DisplayPmcIA64 );
    }
    
} // IsPmcSupported()

DECLARE_API( pmc )

/*++

Routine Description:

    Dumps a IA-64 Processor PMC Register.
    // Thierry 11/2000: The following scheme could also be used for PMD registers.

Arguments:

    args - Supplies the address in hex.

Return Value:

    None

--*/

{
    ULONG64       pmcValue   = 0;
    ULONG         result;
    ULONG         flags      = 0;
    char         *option     = NULL;
    PDISPLAY_PMC  displayPmc = DisplayGenPmcIA64;  // Default display function.
    char         *header;

    if ( TargetMachine != IMAGE_FILE_MACHINE_IA64 )
    {
        dprintf("!pmc not implemented for this architecture.\n");
        return S_OK;
    }

    option = strchr( args, '-' );
    if ( option ) {
        displayPmc = GetPmcDisplayFunction( ++option );
        args += (strlen(option) + 2);
    }
        
    result = sscanf(args,"%I64lx %lx", &pmcValue, &flags);
	if ((result != 1) && (result != 2)) {
        //
        // If user specified "@kpfc*"...
        //

        char kpfcStr[16];
        BOOLEAN valid = FALSE;

        result = sscanf(args,"%s %lx", kpfcStr, &flags);
        if ( (result == 1) || (result == 2) )  {
            if ( option == NULL )   { // If we did not force the display format.
                char *str;

                str = strstr( kpfcStr, "@kpfc" );
                if ( str )      {
                    str += strlen("@kpfc");
                    displayPmc = GetPmcDisplayFunction( str );
                }
            }
        }
        else  {
            dprintf( "USAGE: !pmc [-opt] 0xValue [display_mode:0,1,2]\n"
                     "USAGE: !pmc @kpfc*         [display_mode:0,1,2]\n"
                     "where currently supported option:\n"
                     "\tgen [or 4,5,6,7] - generic PMC registers\n"
                     "\tbtb [or 12]      - branch trace buffer PMC register\n"
                     "\tEx: !pmc -btb @r2 2\n"
                   );
            return E_INVALIDARG;
        }      
        pmcValue = GetExpression(kpfcStr);
    }
    
    header = (flags > DISPLAY_MIN) ? NULL : " pmc: ";
    (*displayPmc)( header, pmcValue, flags );

    return S_OK;

} // !pmc