//
// Copyright (c) Microsoft Corporation.  All rights reserved.
//
//
// Use of this sample source code is subject to the terms of the Microsoft
// license agreement under which you licensed this sample source code. If
// you did not accept the terms of the license agreement, you are not
// authorized to use this sample source code. For the terms of the license,
// please see the license agreement between you and Microsoft or, if applicable,
// see the LICENSE.RTF on your install media or the root of your tools installation.
// THE SAMPLE SOURCE CODE IS PROVIDED "AS IS", WITH NO WARRANTIES.
//
//------------------------------------------------------------------------------
//
//  File: ioctl.c
//
//  This file implements OALIoCtlHalGetCacheInfo function called from
//  OEMIoControl handler for IOCTL_HAL_GETCACHEINFO code.
//
#include <windows.h>
#include <pkfuncs.h>
#include <oal.h>

//------------------------------------------------------------------------------
//
//  Function:  interpretCPUIDCacheInfo
//
//  Updates g_oalCacheInfo with cache information based upon the input value.
//  Expected: the input value is a register produced by the CPUID instruction.
//  Returns TRUE for success, FALSE for failure.
//
static BOOL x86InterpretCPUIDValue(const UINT32 & cpuIDValue);

//------------------------------------------------------------------------------
//
//  Function:  x86InitializeCPUIDTable
//
//  Fills in a static table based on the specification of the CPUID instruction.
//
static void x86InitializeCPUIDTable();

typedef struct
{
    UINT32 descriptor;
    OAL_CACHE_INFO cacheInfo;
} x86CPUIDPair;

#define NUMVALIDCPUIDDESCRIPTORS 59
#define MAXCPUIIDITERATIONS 6

x86CPUIDPair x86CPUIDTable[NUMVALIDCPUIDDESCRIPTORS];

//------------------------------------------------------------------------------
//
//  Function:  x86IoCtlGetCacheInfo
//
//  Implements the IOCTL_HAL_GET_CACHE_INFO. This function fills in a
//  CacheInfo structure.
//
extern "C" BOOL x86IoCtlHalGetCacheInfo(
    UINT32 code, VOID *pInpBuffer, UINT32 inpSize, VOID *pOutBuffer,
    UINT32 outSize, UINT32 *pOutSize
) {
    BOOL rc = FALSE;
    CacheInfo *pInfo = (CacheInfo*)pOutBuffer;

    OALMSG(OAL_FUNC, (L"+x86IoctlGetCacheInfo(...)\r\n"));

    if (pOutSize) {
        *pOutSize = sizeof(CacheInfo);
    }
    if (pOutBuffer == NULL && outSize > 0) {
        NKSetLastError(ERROR_INVALID_PARAMETER);
        OALMSG(OAL_WARN, (
            L"WARN: x86IoctlGetDeviceCacheInfo: Invalid parameter\r\n"
        ));
        goto cleanUp;
    }

    if (pOutBuffer == NULL || outSize < sizeof(CacheInfo)) {
        NKSetLastError(ERROR_INSUFFICIENT_BUFFER);
        OALMSG(OAL_WARN, (
            L"WARN: x86IoctlGetDeviceCacheInfo: Buffer too small\r\n"
        ));
        goto cleanUp;
    }

    UINT32 CPUIDInfo[MAXCPUIIDITERATIONS * 4]; // supports MAXCPUIIDITERATIONS worth of CPUID iterations

    // this loop reads the cache info from the cpu
    UINT32 iterationNumber = 0;
    while(iterationNumber < MAXCPUIIDITERATIONS)
    {
        UINT32 CPUIDTempBuffer[4];

        __try {
            __asm {
            mov eax, 02h
            cpuid                                  ; when eax is 02h we are querying for cache info
            mov CPUIDTempBuffer, eax     ; cpuid fills eax, ebx, ecx, and edx with descriptors of cache
            mov CPUIDTempBuffer+4, ebx
            mov CPUIDTempBuffer+8, ecx
            mov CPUIDTempBuffer+12, edx
            }
        } __except (EXCEPTION_EXECUTE_HANDLER) {
            //There was an error in calling CPUID which means we have no cache info to return.
            NKSetLastError(ERROR_NOT_SUPPORTED);
            OALMSG(OAL_ERROR, (
                L"ERROR: x86IoctlGetDeviceCacheInfo: CPUID instruction failed, no cache info available\r\n"
            ));
            goto cleanUp;
        }

        UINT32 iterationOffset = iterationNumber * sizeof(UINT32);

        CPUIDInfo[iterationOffset] = CPUIDTempBuffer[0];
        CPUIDInfo[iterationOffset+1] = CPUIDTempBuffer[1];
        CPUIDInfo[iterationOffset+2] = CPUIDTempBuffer[2];
        CPUIDInfo[iterationOffset+3] = CPUIDTempBuffer[3];

        iterationNumber++;

        // If the least significant byte of eax is 01, then no further calls are necessary to retrieve
        // cache information.  Otherwise there is more cache info and we need to make another call.
        if((CPUIDInfo[iterationOffset] & 0x000000FF) == 0x01)
        {
            break;
        }
    }

    // set up our table so we can interpret the cpu's return values
    x86InitializeCPUIDTable();

    // update our global cache struct based on the values we read from the cpu
    while(iterationNumber != 0)
    {
        iterationNumber--;
        x86InterpretCPUIDValue(CPUIDInfo[iterationNumber]);
        x86InterpretCPUIDValue(CPUIDInfo[iterationNumber+1]);
        x86InterpretCPUIDValue(CPUIDInfo[iterationNumber+2]);
        x86InterpretCPUIDValue(CPUIDInfo[iterationNumber+3]);
    }

    memset(pInfo, 0, sizeof(CacheInfo));

    // copy from our global cache struct into the slightly smaller struct we're returning
    pInfo->dwL1Flags = g_oalCacheInfo.L1Flags;
    pInfo->dwL1ICacheLineSize = g_oalCacheInfo.L1ILineSize;
    pInfo->dwL1ICacheNumWays = g_oalCacheInfo.L1INumWays;
    pInfo->dwL1ICacheSize = g_oalCacheInfo.L1ISize;
    pInfo->dwL1DCacheLineSize = g_oalCacheInfo.L1DLineSize;
    pInfo->dwL1DCacheNumWays = g_oalCacheInfo.L1DNumWays;
    pInfo->dwL1DCacheSize = g_oalCacheInfo.L1DSize;

    pInfo->dwL2Flags = g_oalCacheInfo.L2Flags;
    pInfo->dwL2ICacheLineSize = g_oalCacheInfo.L2ILineSize;
    pInfo->dwL2ICacheNumWays = g_oalCacheInfo.L2INumWays;
    pInfo->dwL2ICacheSize = g_oalCacheInfo.L2ISize;
    pInfo->dwL2DCacheLineSize = g_oalCacheInfo.L2DLineSize;
    pInfo->dwL2DCacheNumWays = g_oalCacheInfo.L2DNumWays;
    pInfo->dwL2DCacheSize = g_oalCacheInfo.L2DSize;

    rc = TRUE;

cleanUp:
    OALMSG(OAL_FUNC, (L"-x86IoctlGetCacheInfo(rc = %d)\r\n", rc));
    return rc;
}

//------------------------------------------------------------------------------
//
//  Function:  ProcessDescriptorByte
//  Maps a descriptor byte to the corresponding cache values based upon the
//  static table.  Returns TRUE for success, FALSE for failure.
//
static BOOL ProcessDescriptorByte(const byte & descriptor);

static BOOL x86InterpretCPUIDValue(const UINT32 & cpuIDValue)
{
    //check most significant bit of cpuIDValue - if it is 0 then the value contains valid 1-byte descriptors
    if((cpuIDValue & 0x80000000))
    {
        return FALSE;
    }

    // valid descriptor, for each byte, scan the table
    // return true if we've successfully updated the global cache info based on all four bytes
    return (ProcessDescriptorByte(cpuIDValue & 0xFF) &&
            ProcessDescriptorByte(cpuIDValue >> 8 & 0xFF) &&
            ProcessDescriptorByte(cpuIDValue >> 16 & 0xFF) &&
            ProcessDescriptorByte(cpuIDValue >> 24 & 0xFF));
}

static BOOL ProcessDescriptorByte(const BYTE & descriptor)
{
    for(int loopCounter = 0; loopCounter < NUMVALIDCPUIDDESCRIPTORS; loopCounter++)
    {
        if(descriptor == x86CPUIDTable[loopCounter].descriptor)
        {
            // matched a table entry, update the global cache info.
            // because we shouldn't overlapping entries, we can simply
            // logical or the info into our global cache info
            g_oalCacheInfo.L1Flags |= x86CPUIDTable[loopCounter].cacheInfo.L1Flags;
            g_oalCacheInfo.L1ISetsPerWay |= x86CPUIDTable[loopCounter].cacheInfo.L1ISetsPerWay;
            g_oalCacheInfo.L1INumWays |= x86CPUIDTable[loopCounter].cacheInfo.L1INumWays;
            g_oalCacheInfo.L1ILineSize |= x86CPUIDTable[loopCounter].cacheInfo.L1ILineSize;
            g_oalCacheInfo.L1ISize |= x86CPUIDTable[loopCounter].cacheInfo.L1ISize;
            g_oalCacheInfo.L1DSetsPerWay |= x86CPUIDTable[loopCounter].cacheInfo.L1DSetsPerWay;
            g_oalCacheInfo.L1DNumWays |= x86CPUIDTable[loopCounter].cacheInfo.L1DNumWays;
            g_oalCacheInfo.L1DLineSize |= x86CPUIDTable[loopCounter].cacheInfo.L1DLineSize;
            g_oalCacheInfo.L1DSize |= x86CPUIDTable[loopCounter].cacheInfo.L1DSize;

            g_oalCacheInfo.L2Flags |= x86CPUIDTable[loopCounter].cacheInfo.L2Flags;
            g_oalCacheInfo.L2ISetsPerWay |= x86CPUIDTable[loopCounter].cacheInfo.L2ISetsPerWay;
            g_oalCacheInfo.L2INumWays |= x86CPUIDTable[loopCounter].cacheInfo.L2INumWays;
            g_oalCacheInfo.L2ILineSize |= x86CPUIDTable[loopCounter].cacheInfo.L2ILineSize;
            g_oalCacheInfo.L2ISize |= x86CPUIDTable[loopCounter].cacheInfo.L2ISize;
            g_oalCacheInfo.L2DSetsPerWay |= x86CPUIDTable[loopCounter].cacheInfo.L2DSetsPerWay;
            g_oalCacheInfo.L2DNumWays |= x86CPUIDTable[loopCounter].cacheInfo.L2DNumWays;
            g_oalCacheInfo.L2DLineSize |= x86CPUIDTable[loopCounter].cacheInfo.L2DLineSize;
            g_oalCacheInfo.L2DSize |= x86CPUIDTable[loopCounter].cacheInfo.L2DSize;
            return TRUE;
        }
    }
    // got an unexpected byte, either something is very wrong or our table is out of date
    return FALSE;
}

static void x86InitializeCPUIDTable()
{
    // initialize all table entries to 0 so that we can use |= to update the global cache info later
    memset(x86CPUIDTable, 0, (sizeof(x86CPUIDPair)*NUMVALIDCPUIDDESCRIPTORS));

    x86CPUIDTable[0].descriptor = 0x00; // Null descriptor
    x86CPUIDTable[1].descriptor = 0x01; // Instruction TLB: 4 KByte Pages, 4-way set associative, 32 entries
    x86CPUIDTable[2].descriptor = 0x02; // Instruction TLB: 4 MByte Pages, 4-way set associative, 2 entries
    x86CPUIDTable[3].descriptor = 0x03; // Data TLB: 4 KByte Pages, 4-way set associative, 64 entries
    x86CPUIDTable[4].descriptor = 0x04; // Data TLB: 4 MByte Pages, 4-way set associative, 8 entries

    x86CPUIDTable[5].descriptor = 0x06; // 1st-level instruction cache: 8 KBytes, 4-way set associative, 32 byte line size
    x86CPUIDTable[5].cacheInfo.L1ISize = 0x2000;
    x86CPUIDTable[5].cacheInfo.L1INumWays = 0x4;
    x86CPUIDTable[5].cacheInfo.L1ILineSize = 0x20;

    x86CPUIDTable[6].descriptor = 0x08; // 1st-level instruction cache: 16 KBytes, 4-way set associative, 32 byte line size
    x86CPUIDTable[6].cacheInfo.L1ISize = 0x4000;
    x86CPUIDTable[6].cacheInfo.L1INumWays = 0x4;
    x86CPUIDTable[6].cacheInfo.L1ILineSize = 0x20;

    x86CPUIDTable[7].descriptor = 0x0A; // 1st-level data cache: 8 KBytes, 2-way set associative, 32 byte line size
    x86CPUIDTable[7].cacheInfo.L1DSize = 0x2000;
    x86CPUIDTable[7].cacheInfo.L1DNumWays = 0x2;
    x86CPUIDTable[7].cacheInfo.L1DLineSize = 0x20;

    x86CPUIDTable[8].descriptor = 0x0C; // 1st-level data cache: 16 KBytes, 4-way set associative, 32 byte line size
    x86CPUIDTable[8].cacheInfo.L1DSize = 0x4000;
    x86CPUIDTable[8].cacheInfo.L1DNumWays = 0x4;
    x86CPUIDTable[8].cacheInfo.L1DLineSize = 0x20;

    x86CPUIDTable[9].descriptor = 0x22; // 3rd-level cache: 512 KBytes, 4-way set associative, 64 byte line size, 2 lines per sector
    x86CPUIDTable[10].descriptor = 0x23; // 3rd-level cache: 1 MBytes, 8-way set associative, 64 byte line size, 2 lines per sector
    x86CPUIDTable[11].descriptor = 0x25; // 3rd-level cache: 2 MBytes, 8-way set associative, 64 byte line size, 2 lines per sector
    x86CPUIDTable[12].descriptor = 0x29; // 3rd-level cache: 4 MBytes, 8-way set associative, 64 byte line size, 2 lines per sector

    x86CPUIDTable[13].descriptor = 0x2C; // 1st-level data cache: 32 KBytes, 8-way set associative, 64 byte line size
    x86CPUIDTable[13].cacheInfo.L1DSize = 0x8000;
    x86CPUIDTable[13].cacheInfo.L1DNumWays = 0x8;
    x86CPUIDTable[13].cacheInfo.L1DLineSize = 0x40;

    x86CPUIDTable[14].descriptor = 0x30; // 1st-level instruction cache: 32 KBytes, 8-way set associative, 64 byte line size
    x86CPUIDTable[14].cacheInfo.L1ISize = 0x8000;
    x86CPUIDTable[14].cacheInfo.L1INumWays = 0x8;
    x86CPUIDTable[14].cacheInfo.L1ILineSize = 0x40;

    x86CPUIDTable[15].descriptor = 0x40; // No 2nd-level cache or, if processor contains a valid 2nd-level cache, no 3rd-level cache
    x86CPUIDTable[16].descriptor = 0x41; // 2nd-level cache: 128 KBytes, 4-way set associative, 32 byte line size
    x86CPUIDTable[16].cacheInfo.L2DSize = 0x20000;
    x86CPUIDTable[16].cacheInfo.L2DNumWays = 0x4;
    x86CPUIDTable[16].cacheInfo.L2DLineSize = 0x20;

    x86CPUIDTable[17].descriptor = 0x42; // 2nd-level cache: 256 KBytes, 4-way set associative, 32 byte line size
    x86CPUIDTable[17].cacheInfo.L2DSize = 0x40000;
    x86CPUIDTable[17].cacheInfo.L2DNumWays = 0x4;
    x86CPUIDTable[17].cacheInfo.L2DLineSize = 0x20;

    x86CPUIDTable[18].descriptor = 0x43; // 2nd-level cache: 512 KBytes, 4-way set associative, 32 byte line size
    x86CPUIDTable[18].cacheInfo.L2DSize = 0x80000;
    x86CPUIDTable[18].cacheInfo.L2DNumWays = 0x4;
    x86CPUIDTable[18].cacheInfo.L2DLineSize = 0x20;

    x86CPUIDTable[19].descriptor = 0x44; // 2nd-level cache: 1 MByte, 4-way set associative, 32 byte line size
    x86CPUIDTable[19].cacheInfo.L2DSize = 0x100000;
    x86CPUIDTable[19].cacheInfo.L2DNumWays = 0x4;
    x86CPUIDTable[19].cacheInfo.L2DLineSize = 0x20;

    x86CPUIDTable[20].descriptor = 0x45; // 2nd-level cache: 2 MByte, 4-way set associative, 32 byte line size
    x86CPUIDTable[20].cacheInfo.L2DSize = 0x200000;
    x86CPUIDTable[20].cacheInfo.L2DNumWays = 0x4;
    x86CPUIDTable[20].cacheInfo.L2DLineSize = 0x20;

    x86CPUIDTable[21].descriptor = 0x46; // 3rd-level cache: 4 MByte, 4-way set associative, 64 byte line size
    x86CPUIDTable[22].descriptor = 0x47; // 3rd-level cache: 8 MByte, 8-way set associative, 64 byte line size

    x86CPUIDTable[23].descriptor = 0x50; // Instruction TLB: 4 KByte and 2-MByte or 4-MByte pages, 64 entries
    x86CPUIDTable[24].descriptor = 0x51; // Instruction TLB: 4 KByte and 2-MByte or 4-MByte pages, 128 entries
    x86CPUIDTable[25].descriptor = 0x52; // Instruction TLB: 4 KByte and 2-MByte or 4-MByte pages, 256 entries
    x86CPUIDTable[26].descriptor = 0x5B; // Data TLB: 4 KByte and 4 MByte pages, 64 entries
    x86CPUIDTable[27].descriptor = 0x5C; // Data TLB: 4 KByte and 4 MByte pages,128 entries
    x86CPUIDTable[28].descriptor = 0x5D; // Data TLB: 4 KByte and 4 MByte pages,256 entries

    x86CPUIDTable[29].descriptor = 0x60; // 1st-level data cache: 16 KByte, 8-way set associative, 64 byte line size
    x86CPUIDTable[29].cacheInfo.L1DSize = 0x4000;
    x86CPUIDTable[29].cacheInfo.L1DNumWays = 0x8;
    x86CPUIDTable[29].cacheInfo.L1DLineSize = 0x40;

    x86CPUIDTable[30].descriptor = 0x66; // 1st-level data cache: 8 KByte, 4-way set associative, 64 byte line size
    x86CPUIDTable[30].cacheInfo.L1DSize = 0x2000;
    x86CPUIDTable[30].cacheInfo.L1DNumWays = 0x4;
    x86CPUIDTable[30].cacheInfo.L1DLineSize = 0x40;

    x86CPUIDTable[31].descriptor = 0x67; // 1st-level data cache: 16 KByte, 4-way set associative, 64 byte line size
    x86CPUIDTable[31].cacheInfo.L1DSize = 0x4000;
    x86CPUIDTable[31].cacheInfo.L1DNumWays = 0x4;
    x86CPUIDTable[31].cacheInfo.L1DLineSize = 0x40;

    x86CPUIDTable[32].descriptor = 0x68; // 1st-level data cache: 32 KByte, 4-way set associative, 64 byte line size
    x86CPUIDTable[32].cacheInfo.L1DSize = 0x8000;
    x86CPUIDTable[32].cacheInfo.L1DNumWays = 0x4;
    x86CPUIDTable[32].cacheInfo.L1DLineSize = 0x40;

    x86CPUIDTable[33].descriptor = 0x70; // Trace cache: 12 K-micro-op, 8-way set associative
    x86CPUIDTable[34].descriptor = 0x71; // Trace cache: 16 K-micro-op, 8-way set associative
    x86CPUIDTable[35].descriptor = 0x72; // Trace cache: 32 K-micro-op, 8-way set associative

    x86CPUIDTable[36].descriptor = 0x78; // 2nd-level cache: 1 MByte, 4-way set associative, 64byte line size
    x86CPUIDTable[36].cacheInfo.L2DSize = 0x100000;
    x86CPUIDTable[36].cacheInfo.L2DNumWays = 0x4;
    x86CPUIDTable[36].cacheInfo.L2DLineSize = 0x40;

    x86CPUIDTable[37].descriptor = 0x79; // 2nd-level cache: 128 KByte, 8-way set associative, 64 byte line size, 2 lines per sector
    x86CPUIDTable[37].cacheInfo.L2DSize = 0x20000;
    x86CPUIDTable[37].cacheInfo.L2DNumWays = 0x8;
    x86CPUIDTable[37].cacheInfo.L2DLineSize = 0x40;

    x86CPUIDTable[38].descriptor = 0x7A; // 2nd-level cache: 256 KByte, 8-way set associative, 64 byte line size, 2 lines per sector
    x86CPUIDTable[38].cacheInfo.L2DSize = 0x40000;
    x86CPUIDTable[38].cacheInfo.L2DNumWays = 0x8;
    x86CPUIDTable[38].cacheInfo.L2DLineSize = 0x40;

    x86CPUIDTable[39].descriptor = 0x7B; // 2nd-level cache: 512 KByte, 8-way set associative, 64 byte line size, 2 lines per sector
    x86CPUIDTable[39].cacheInfo.L2DSize = 0x80000;
    x86CPUIDTable[39].cacheInfo.L2DNumWays = 0x8;
    x86CPUIDTable[39].cacheInfo.L2DLineSize = 0x40;

    x86CPUIDTable[40].descriptor = 0x7C; // 2nd-level cache: 1 MByte, 8-way set associative, 64 byte line size, 2 lines per sector
    x86CPUIDTable[40].cacheInfo.L2DSize = 0x100000;
    x86CPUIDTable[40].cacheInfo.L2DNumWays = 0x8;
    x86CPUIDTable[40].cacheInfo.L2DLineSize = 0x40;

    x86CPUIDTable[41].descriptor = 0x7D; // 2nd-level cache: 2 MByte, 8-way set associative, 64byte line size
    x86CPUIDTable[41].cacheInfo.L2DSize = 0x200000;
    x86CPUIDTable[41].cacheInfo.L2DNumWays = 0x8;
    x86CPUIDTable[41].cacheInfo.L2DLineSize = 0x40;

    x86CPUIDTable[42].descriptor = 0x7F; // 2nd-level cache: 512 KByte, 2-way set associative, 64-byte line size
    x86CPUIDTable[42].cacheInfo.L2DSize = 0x80000;
    x86CPUIDTable[42].cacheInfo.L2DNumWays = 0x2;
    x86CPUIDTable[42].cacheInfo.L2DLineSize = 0x40;

    x86CPUIDTable[43].descriptor = 0x82; // 2nd-level cache: 256 KByte, 8-way set associative, 32 byte line size
    x86CPUIDTable[43].cacheInfo.L2DSize = 0x40000;
    x86CPUIDTable[43].cacheInfo.L2DNumWays = 0x8;
    x86CPUIDTable[43].cacheInfo.L2DLineSize = 0x20;

    x86CPUIDTable[44].descriptor = 0x83; // 2nd-level cache: 512 KByte, 8-way set associative, 32 byte line size
    x86CPUIDTable[44].cacheInfo.L2DSize = 0x80000;
    x86CPUIDTable[44].cacheInfo.L2DNumWays = 0x8;
    x86CPUIDTable[44].cacheInfo.L2DLineSize = 0x20;

    x86CPUIDTable[45].descriptor = 0x84; // 2nd-level cache: 1 MByte, 8-way set associative, 32 byte line size
    x86CPUIDTable[45].cacheInfo.L2DSize = 0x100000;
    x86CPUIDTable[45].cacheInfo.L2DNumWays = 0x8;
    x86CPUIDTable[45].cacheInfo.L2DLineSize = 0x20;

    x86CPUIDTable[46].descriptor = 0x85; // 2nd-level cache: 2 MByte, 8-way set associative, 32 byte line size
    x86CPUIDTable[46].cacheInfo.L2DSize = 0x200000;
    x86CPUIDTable[46].cacheInfo.L2DNumWays = 0x8;
    x86CPUIDTable[46].cacheInfo.L2DLineSize = 0x20;

    x86CPUIDTable[47].descriptor = 0x86; // 2nd-level cache: 512 KByte, 4-way set associative, 64 byte line size
    x86CPUIDTable[47].cacheInfo.L2DSize = 0x80000;
    x86CPUIDTable[47].cacheInfo.L2DNumWays = 0x4;
    x86CPUIDTable[47].cacheInfo.L2DLineSize = 0x40;

    x86CPUIDTable[48].descriptor = 0x87; // 2nd-level cache: 1 MByte, 8-way set associative, 64 byte line size
    x86CPUIDTable[48].cacheInfo.L2DSize = 0x100000;
    x86CPUIDTable[48].cacheInfo.L2DNumWays = 0x8;
    x86CPUIDTable[48].cacheInfo.L2DLineSize = 0x40;

    x86CPUIDTable[49].descriptor = 0xB0; // Instruction TLB: 4 KByte Pages, 4-way set associative, 128 entries
    x86CPUIDTable[50].descriptor = 0xB3; // Data TLB: 4 KByte Pages, 4-way set associative, 128 entries
    x86CPUIDTable[51].descriptor = 0xF0; // 64-Byte Prefetching
    x86CPUIDTable[52].descriptor = 0xF1; // 128-Byte Prefetching

// 21/04/2010 AddOn: Intel Atom Z530 support
    x86CPUIDTable[53].descriptor = 0x59;	// Data TLB0: 4 KByte pages, fully associative, 16 entries
	x86CPUIDTable[54].descriptor = 0xBA;	// Data TLB1: 4 KByte pages, 4-way associative, 64 entries
	x86CPUIDTable[55].descriptor = 0x4F;	// Instruction TLB: 4 KByte pages, 32 entries
	x86CPUIDTable[56].descriptor = 0xC0;	// Data TLB: 4 KByte and 4 MByte pages, 4-way associative, 8 entries
	
	x86CPUIDTable[57].descriptor = 0x80;	// 2nd-level cache: 512 KByte, 8-way set associative, 64-byte line size
	x86CPUIDTable[57].cacheInfo.L2DSize = 0x80000;
	x86CPUIDTable[57].cacheInfo.L2DNumWays = 0x8;
	x86CPUIDTable[57].cacheInfo.L2DLineSize = 0x40;

	x86CPUIDTable[58].descriptor = 0x0E;	// 1st-level data cache: 24 KBytes, 6-way set associative, 64 byte line size
	x86CPUIDTable[58].cacheInfo.L1DSize = 0x6000;
	x86CPUIDTable[58].cacheInfo.L1DNumWays = 0x6;
	x86CPUIDTable[58].cacheInfo.L1DLineSize = 0x40;
// ==========================================
}

//------------------------------------------------------------------------------
