/* Intel-specific CPUID data
 * $Id: intel.c,v 1.4 2009/04/06 05:42:46 karn Exp karn $
 * Copyright 2009, Phil Karn, KA9Q
 * Available under the terms of the GNU General Public License
 */
#include <stdio.h>
#if 0
#include <smmintrin.h>
#endif
#include "cpuid.h"

struct featureflag Intel_standard_ecx[] = {
  {"SSE3","SSE3 instructions"},
  {NULL, NULL},
  {"DTES64","64-bit DS area"},
  {"MONITOR","MONITOR/MWAIT"},
  {"DS-CPL","CPL Qualified Debug Store"},
  {"VMX","Virtual Machine extensions"},
  {"SMX","Safer Mode extensions"},
  {"EST","Enhanced Intel SpeedStep technology"},
  {"TM2","Thermal Monitor 2"},
  {"SSSE3","Supplemental SSE3 instructions"},
  {"CNXT-ID","L1 Context ID"},
  {NULL,NULL},
  {NULL,NULL},
  {"CMPXCHG16B","CMPXCHG16B instruction"},
  {"xTPR","Update Control"},
  {"PDCM","Perfmon and Debug Capability"},
  {NULL,NULL},
  {NULL,NULL},
  {"DCA","Prefetch data from memory mapped device"},
  {"SSE4.1","SSE4.1 instructions"},
  {"SSE4.2","SSE4.2 instructions"},
  {"x2APIC",""},
  {"MOVBE","MOVBE instruction"},
  {"POPCNT","Population count instruction"},
  {NULL,NULL},
  {NULL,NULL},
  {"XSAVE","XSAVE/XRSTOR processor extended states"},
  {"OSXSAVE","XSETBV/SGETBV instructions"},
  {NULL,NULL},
  {NULL,NULL},
  {NULL,NULL},
  {"VM","Virtual Machine Hypervisor present"},
};

struct featureflag Intel_standard_edx[] = {
  {"FPU","Floating Point Unit"},
  {"VME","Virtual 8086 Mode Enhancements"},
  {"DE","Debugging extensions"},
  {"PSE","Page Size extensions"},
  {"TSC","Time Stamp Counter"},
  {"MSR","Model Specific Registers"},
  {"PAE","Physical Address Extension"},
  {"MCE","Machine Check Exception"},
  {"CX8","COMPXCHG8B instruction"},
  {"APIC","On-chip Advanced Programmable Interrupt Controller present and enabled"},
  {NULL,NULL},
  {"SEP","Fast System Call"},
  {"MTRR","Memory Type Range Registers"},
  {"PGE","PTE Global Flag"},
  {"MCA","Machine Check Architecture"},
  {"CMOV","Conditional Move and Compare instructions"},
  {"FGPAT","Page Attribute Table"},
  {"PSE-36","36-bit Page Size Extension"},
  {"PN","Processor Serial Number present and enabled"},
  {"CLFSH","Flush Cache Line instruction"},
  {NULL,NULL},
  {"DS","Debug store"},
  {"ACPI","Thermal Monitor and Clock Ctrl"},
  {"MMX","MMX instructions"},
  {"FXSR","Fast FP/MMX Streaming SIMD extensions save/restore"},
  {"SSE","SSE instructions"},
  {"SSE2","SSE2 instructions"},
  {"SS","Self Snoop"},
  {"HT","Hyperthreading"},
  {"TM","Thermal monitor"},
  {NULL,NULL},
  {"PBE","Pending Break Enable"}
};

char *intel_tlb[] = {
  /* 00 */
  NULL,
  "Instruction TLB: 4KB pages, 4-way, 32 entries",
  "Instruction TLB: 4MB pages, fully associative, 2 entries",
  "Data TLB: 4KB pages, 4-way, 64 entries",
  "Data TLB: 4MB pages, 4-way, 8 entries",
  "Data TLB1: 4 MB pages, 4-way, 32 entries",
  "Instruction cache: 8KB, 4-way, 32 byte lines",
  NULL,
  "Instruction cache: 16KB, 4-way, 32 byte lines",
  "L1 instruction cache: 32 KB, 4-way, 64 byte lines",
  "Data cache: 8KB, 2-way, 32 byte lines",
  "Instruction TLB: 4 MB pages, 4-way, 4 entries",
  "Data cache: 16KB, 2-way or 4-way, 32 byte lines",
  "L1 data cache: 16 KB, 4-way, 64 byte lines",
  "L1 data cache: 24 KB, 6-way, 64 byte lines",
  NULL,

  /* 10 */
  NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,

  /* 20 */
  NULL,
  "L2 cache: 256 KB, 8-way, 64 byte lines",
  "L3 cache: 512 KB, 4-way, 64 byte lines, 2 lines/sector",
  "L3 cache: 1 MB, 8-way, 64 byte lines, 2 lines/sector",
  NULL,
  "L3 cache: 2 MB, 8-way, 64 byte lines, 2 lines/sector",
  NULL,NULL,NULL,
  "L3 cache: 4 MB, 8-way, 64 byte lines, 2 lines/sector",
  NULL,NULL,
  "L1 data cache: 32 KB, 8-way, 64 byte lines",
  NULL,NULL,NULL,

  /* 30 */
  "L1 instruction cache: 32 KB, 8-way, 64 byte lines",
  NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,

  /* 40 */
  "No L2 cache; or if L2 cache exists, no L3 cache",
  "L2 unified cache: 128KB, 4-way, 32 byte lines",
  "L2 unified cache: 256KB, 4-way, 32 byte lines",
  "L2 unified cache: 512KB, 4-way, 32 byte lines",
  "L2 unified cache: 1MB, 4-way, 32 byte lines",
  "L2 unified cache: 2MB, 4-way, 32 byte lines",
  "L3 cache: 4MB, 4-way, 64 byte lines",
  "L3 cache: 8MB, 8-way, 64 byte lines",
  "L2 cache: 3 MB, 12-way, 64 byte lines",
  "L2/L3 cache: 4 MB, 16-way, 64 byte lines (L3: Xeon MP, family 0FH, Model 06H)",
  "L3 cache: 6 MB, 12-way, 64 byte lines",
  "L3 cache: 8 MB, 16-way, 64 byte lines",
  "L3 cache: 12 MB, 12-way, 64 byte lines",
  "L3 cache: 16 MB, 16-way, 64 byte lines",
  "L2 cache: 6 MB, 24-way, 64 byte lines",
  "Instruction TLB: 4KB pages, 32 entries",

  /* 50 */
  "Instruction TLB: 4KB and 2MB or 4MB pages, 64 entries",
  "Instruction TLB: 4KB and 2MB or 4MB pages, 128 entries",
  "Instruction TLB: 4KB and 2MB or 4MB pages, 256 entries",
  NULL,NULL,
  "Instruction TLB: 2MB or 4 MB pages, fully associative, 7 entries",
  "Data TLB0: 4 MB pages, 4-way, 16 entries",
  "Data TLB0: 4 KB pages, 4-way, 16 entries",
  NULL,
  "Data TLB0: 4 KB pages, fully associative, 16 entries",
  "Data TLB0: 2 MB or 4 MB pages, 4-way, 32 entries",
  "Data TLB: 4 KB and 4 MB pages, 64 entries",
  "Data TLB: 4 KB and 4 MB pages, 128 entries",
  "Data TLB: 4 KB and 4 MB pages, 256 entries",
  NULL,NULL,

  /* 60 */
  "L1 data cache: 16 KB, 8-way, 64 byte lines",
  NULL,NULL,NULL,NULL,NULL,
  "L1 data cache: 8 KB, 4-way, 64 byte lines",
  "L1 data cache: 16 KB, 4-way, 64 byte lines",
  "L1 data cache: 32 KB, 4-way, 64 byte lines",
  NULL,NULL,NULL,NULL,NULL,NULL,NULL,

  /* 70 */
  "Trace cache: 12 k uop, 8-way",
  "Trace cache: 16 k uop, 8-way",
  "Trace cache: 32 k uop, 8-way",
  NULL,NULL,NULL,NULL,NULL,
  "L2 cache: 1 MB, 4-way, 64 byte lines",
  "L2 cache: 128 KB, 8-way, 64 byte lines, 2 lines per sector",
  "L2 cache: 256 KB, 8-way, 64 byte lines, 2 lines per sector",
  "L2 cache: 512KB, 8-way, 64 byte lines, 2 lines per sector",
  "L2 cache: 1 MB, 8-way, 64-byte lines, 2 lines/sector",
  "L2 cache: 2 MB, 8-way, 64-byte lines",
  NULL,
  "L2 cache: 512KB, 2-way, 64-byte lines",

  /* 80 */
  "L2 cache: 512KB, 8-way, 64-byte lines",
  NULL,
  "L2 cache: 256 KB, 8-way, 32 byte lines",
  "L2 cache: 512 KB, 8-way, 32 byte lines",
  "L2 cache: 1 MB, 8-way, 32 byte lines",
  "L2 cache: 2 MB, 8-way, 32 byte lines",
  "L2 cache: 512KB, 4-way, 64 byte lines",
  "L2 cache: 1 MB, 8-way, 64 byte lines",
  NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,

  /* 90 */
  NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,

  /* A0 */
  NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,

  /* B0 */  
  "Instruction TLB: 4KB pages, 4-way, 128 entries",
  "Instruction TLB: 2M pages, 4-way, 8 entries, or 4M pages, 4-way, 4 entries",
  "Instruction TLB: 4 KB pages, 4-way, 64 entries",
  "Data TLB: 4 KB pages, 4-way, 128 entries",
  "Data TLB1: 4 KB pages, 4-way, 256 entries",
  NULL,NULL,NULL,NULL,NULL,
  "Data TLB1: 4 KB pages, 4-way, 64 entries",
  NULL,NULL,NULL,NULL,

  /* C0 */
  NULL,
  "Data TLB: 4 KB and 4 MB pages, 4-way, 8 entries",
  NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,
  "STLB: shared L2 TLB: 4KB pages, 4-way, 64 entries",
  NULL,NULL,NULL,NULL,

  /* D0 */
  NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,

  /* E0 */
  NULL,NULL,NULL,NULL,NULL,NULL,
  "L3 cache: 8 MB, 16-way, 64 byte lines",
  NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,

  /* F0 */
  NULL,NULL,
  "Instruction prefetch: 64 bytes",
  "Instruction prefetch: 128 bytes",
  NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,
};


/* Intel-specific information */
void dointel(){
  int i,ntlb;
  unsigned int regs[4];
  int psn = 0;
  int maxi;

  printf("Intel-specific information:\n");

  regs[0] = 0;
  cpuid(regs);
  maxi = regs[0];

  if(maxi >= 1){
    int i;
    int stepping,model,family,type,extended_model,extended_family;

    /* Family/model/type etc */
    printf("CPUID Leaf 1; standard feature flags:\n");
    regs[0] = 1;
    cpuid(regs);
    
    stepping = regs[0] & 0xf;
    model = (regs[0] >> 4) & 0xf;
    family = (regs[0] >> 8) & 0xf;
    type = (regs[0] >> 12) & 0x3;
    extended_model = (regs[0] >> 16) & 0xf;
    extended_family = (regs[0] >>20) & 0xff;

    if(family == 0x6 || family == 0xf)
      model = (extended_model << 4) + model;


    if(family == 0xf){
      family = extended_family + family;
    }

    printf("EAX = %08x; ",regs[0]);
    printf("Family: 0x%x Model: 0x%x Type: %d [",family,model,type);
    switch(family){
    case 3:
      printf("i386 ");
      switch(model){
      }
      break;
    case 4:
      printf("i486 ");
      switch(model){
      case 0:
      case 1:
	printf("DX");
	break;
      case 2:
	printf("SX");
	break;
      case 3:
	printf("487/DX2");
	break;
      case 4:
	printf("SL");
	break;
      case 5:
	printf("SX2");
	break;
      case 7:
	printf("write-back enhanced DX2");
	break;
      case 8:
	printf("DX4");
	break;
      }
      break;
    case 5:
      printf("Pentium ");
      switch(model){
      case 1:
	printf("60/66");
	break;
      case 2:
	printf("75-200");
	break;
      case 3:
	printf("for 486 system");
	break;
      case 4:
	printf("MMX");
	break;
      }
      break;
    case 6:
      printf("Pentium Pro;");
      switch(model){
      case 1:
	printf(" Pentium Pro;");
	break;
      case 3:
	printf(" Pentium II Model 3;");
	break;
      case 5:
	printf(" Pentium II Model 5/Xeon/Celeron;");
	break;
      case 6:
	printf(" Celeron;");
	break;
      case 7:
	printf(" Pentium III/Pentium III Xeon;");
	break;
      }
      break;
    }
    switch(type){
    case 0:
      printf(" Original OEM");
      break;
    case 1:
      printf(" Overdrive");
      break;
    case 2:
      printf(" Dual-capable");
      break;
    case 3:
      printf(" Reserved");
      break;
    }
    printf("] Stepping: %d\n",stepping);

    printf("Brand index %u: ",regs[1] & 0xff);
    switch(regs[1] & 0xff){
    case 0:
      printf("feature not supported");
      break;
    case 0xa:
    case 1:
    case 0x14:
      printf("Intel Celeron");
      break;
    case 2:
      printf("Intel Pentium III");
      break;
    case 3:
      printf("Intel Pentium III Xeon");
      break;
    case 4:
      printf("Intel Pentium III");
      break;
    case 6:
      printf("Mobile Intel Pentium III-M");
      break;
    case 7:
    case 0xf:
      printf("Mobile Intel Celeron");
      break;
    case 8:
    case 9:
      printf("Intel Pentium 4");
      break;
    case 0xb:
      printf("Intel Xeon");
      break;
    case 0xc:
      printf("Intel Xeon MP");
      break;
    case 0xe:
      printf("Mobile Intel Pentium 4-M");
      break;
    case 0x11:
    case 0x15:
      printf("Mobile Intel");
      break;
    case 0x12:
      printf("Intel Celeron M");
      break;
    case 0x13:
    case 0x17:
      printf("Mobile Intel Celeron");
      break;
    case 0x16:
      printf("Intel Pentium M");
      break;
    }
    putchar('\n');
    printf("CLFLUSH line size: %u bytes\n",8*((regs[1] >> 8) & 0xff));
    printf("Max addressable IDs: %u\n",(regs[1] >> 16) & 0xff);
    printf("Initial APIC ID: %u\n",(regs[1] >> 24) & 0xff);


    if(regs[3] & (1<<18)){
      psn = 1; /* Pentium-III processor serial number */
    }

    if(regs[3]){
      printf("Feature flags in register EDX = %08x:\n",regs[3]);
      decode_feature_flags(Intel_standard_edx,regs[3]);
    }

    /* Flags in ecx register */
    if(regs[2]){
      printf("Feature flags in register ECX = %08x:\n",regs[2]);
      decode_feature_flags(Intel_standard_ecx,regs[2]);
    }
  }
  putchar('\n');

  if(maxi >= 2){
    int n,i;
    char *messages[1000];
    int l2present = 0;
    int msg40 = 0;

    /* Decode TLB and cache info */
    printf("CPUID leaf 2; Cache & TLB descriptors:\n");
    n = 0;  i = 0;
    do {
      int reg;

      regs[0] = 2;
      cpuid(regs);
      ntlb = regs[0] & 0xff;
      for(reg=0;reg<4;reg++){
	int byte;
	if(regs[reg] & 0x80000000)
	  continue; /* Skip register if high bit set */

	for(byte = (reg == 0)? 1 : 0  ;  byte<4;  byte++){ /* Skip low byte of reg[0], that's ntlb */
	  char *msg;
	  int desc;

	  desc = (regs[reg] >> (8*byte)) & 0xff;

	  if(desc == 0)
	    continue;
	  if(desc == 0x40){
	    msg40++;
	    continue;
	  }
	  if((msg = intel_tlb[desc]) == NULL){
	      printf("Unknown cache/TLB descriptor %d (0x%x)",desc);
	      continue;
	  }
	  if(strncmp(msg,"L2",2) == 0)
	    l2present++;
	  messages[n++] = msg;
	}
      }
    } while (ntlb > 1);
    qsort(messages,n,sizeof(char *),scompare);
    for(i=0;i<n;i++){
      printf("%s\n",messages[i]);
    }
    /* Descriptor 0x40 says "No L2 cache; or if L2 cache present, no L3 cache"
     * so for hack value, figure out which is the case
     */
    if(msg40){
      if(l2present)
	printf("No L3 cache\n");
      else
	printf("No L2 cache\n");
    }
  }
  if(maxi >= 3 && psn){
    /* Pentium III CPU serial number */
    unsigned int signature;
    regs[0] = 1;
    cpuid(regs);
    signature = regs[0];

    regs[0] = 3;
    cpuid(regs);
    printf("CPUID leaf 3; Processor serial: ");
    printf("%04x",signature >> 16);
    printf("-%04x",signature & 0xffff);
    printf("-%04x",regs[3] >> 16);
    printf("-%04x",regs[3] & 0xffff);
    printf("-%04x",regs[2] >> 16);
    printf("-%04x\n",regs[2] & 0xffff);
  }
  putchar('\n');

  /* Leaf 4: deterministic cache parameters leaf */
  if(maxi >= 4){
    int level;

    printf("CPUID Leaf 4; Deterministic cache parameters\n");
    printf("Type              Size LogIDs  Linesize  Phys line parts  Associativity   Sets\n");
    for(level=0;level < 32;level++){
      int size,line_size,sets,assoc;

      regs[0] = 4; regs[2] = level;
      cpuid(regs);
      if((regs[0] & 0x1f) == 0)
	break;
      line_size = (regs[1] & 0xfff) + 1; /* Line size */
      sets = regs[2] + 1;
      assoc = (regs[1] >> 22) + 1; /* ways of associativity */
      size = line_size * sets * assoc;
      printf("L%1u",(regs[0] >> 5) & 7);
      switch(regs[0] & 0x1f){
      case 1:
	printf(" Data       ");
	break;
      case 2:
	printf(" Instruction");
	break;
      case 3:
	printf(" Unified    ");
	break;
      }
      /* Format size */
      if((size  & 0xfffff) == 0){
	/* Multiple of a megabyte */
	printf("%5d MB",size >> 20);
      } else if((size & 0x3ff) == 0){
	/* Multiple of a kilobyte */
	printf("%5d KB",size >> 10);
      } else {
	printf("%8d",size);
      }
      printf("%7u",((regs[0] >> 14) & 0xfff) + 1 ); /* Logical IDs */

      printf("%10d",line_size);
      printf("%17u",((regs[1] >> 12) & 0x3ff) + 1); /* Physical line partitions */
      if(regs[0] & (1<<9))
	printf("%15s","Full");
      else
	printf("%11d-way",assoc);
      printf("%7d",sets); /* Sets */
      printf("%s",regs[0] & (1<<8) ? " Self init;" : "");
      printf("%s",regs[3] & (1<<0) ? " WB inval;" : "");
      printf("%s",regs[1] & (1<<1) ? " Inclusive;" : "");
      putchar('\n');
    }
    regs[0] = 4;
    cpuid(regs);
    printf("Max addressable IDs for CPU cores in physical package: %u\n",(regs[0] >> 26) + 1); /* Physical IDs */
    putchar('\n');
  }
  if(maxi >= 5){
    printf("CPUID Leaf 5; Monitor/MWAIT\n");
    regs[0] = 5;
    cpuid(regs);
    printf("Smallest monitor line-size: %u bytes\n",regs[0] & 0xffff);
    printf("Largest monitor line-size: %u bytes\n",regs[1] & 0xffff);
    printf("ECX flags = %08x:\n",regs[2]);
    if(regs[2] & (1<<0))
      printf(" 0 EMX Enumeration of Monitor-Mwait extensions beyond EAX & EBX supported\n");
    if(regs[2] & (1<<1))
      printf(" 1 IBE Supports treating interrupts as break-event for MWAIT even with ints disabled\n");
    printf("sub C-states supported using MWait:");
    printf(" C0=%d  C1=%d  C2=%d  C3=%d  C4=%d\n",
	   regs[3] & 0xf,(regs[3] >> 4)  & 0xf,(regs[3] >> 8)  & 0xf,
	   (regs[3] >> 12) & 0xf,(regs[3] >> 16) & 0xf);
    putchar('\n');
  }
  if(maxi >= 6){
    regs[0] = 6;
    cpuid(regs);
    printf("Leaf 6; thermal and power management\n");
    printf("EAX flags = %08x:\n",regs[0]);

    if(regs[0] & (1<<0)){
      printf(" 0 Digital temperature sensor supported\n");
    }
    if(regs[0] & (1<<1)){
      printf(" 1 Intel Turbo Boost Technology available\n");
    }
    printf("Number of interrupt thresholds in digital thermal sensor: %u\n",regs[1] & 0xf);
    printf("ECX flags = %08x:\n",regs[2]);
    if(regs[2] & (1<<1)){
      printf(" 1 Hardware Coordination Feedback Capability\n");
    }
    putchar('\n');
  }
  if(maxi >= 9){
    regs[0] = 9;
    cpuid(regs);

    printf("Leaf 9; Direct Cache Access Information Leaf\n");
    printf("IA32_PLATFORM_DCA_CAP MSR: %08u\n",regs[0]);
    putchar('\n');
  }
  if(maxi >= 10){
    regs[0] = 10;
    cpuid(regs);

    printf("Leaf 10; Architectural Performance Monitoring Leaf\n");
    printf("Version ID of architectural performance monitoring: %u\n",regs[0] & 0xff);
    printf("Number of general-purpose performance monitoring counters per logical processor: %u\n", (regs[0] >> 8) & 0xff);
    printf("Bit width of general purpose performance monitoring counter: %u\n",
	   (regs[0] >> 16) & 0xff);
    printf("Length of EBX bit vector to enumerate architectural performance monitoring events: %u\n",
	   (regs[0] >> 24) & 0xff);
    printf("EBX flags = %08x:\n",regs[1]);
    if(regs[1] & (1<<0))
      printf(" 0 Core cycle event not available\n");
    if(regs[1] & (1<<1))
      printf(" 1 Instruction retired event not available\n");
    if(regs[1] & (1<<2))
      printf(" 2 Reference cycles event not available\n");
    if(regs[1] & (1<<3))
      printf(" 3 Last-level cache reference event not available\n");
    if(regs[1] & (1<<4))
      printf(" 4 Last level cache misses event not available\n");
    if(regs[1] & (1<<5))
      printf(" 5 Branch instruction retired event not available\n");
    if(regs[1] & (1<<6))
      printf(" 6 Branch mispredict retired event not available\n");

    printf("Fixed-function performance counters: %u\n",regs[3] & 0x1f);
    printf("Fixed-function performance counters width: %u bits\n",
	   (regs[3] >> 5) & 0xff);
    putchar('\n');
  }
  if(maxi >= 11){
    regs[0] = 11;
    cpuid(regs);
    /* Incomplete -- should enumerate once I figure this out */
    if(regs[1] & 0xffff){
      printf("Leaf 11; Extended topology enumeration leaf\n");
      printf("Bits to shift right on x2APIC ID to get unique topology ID of the next level type: %u\n",
	     regs[0] & 0x1f);
      printf("Number of logical processors at this level type: %u\n",regs[1] & 0xffff);
      printf("Level number: %u\n",regs[2] & 0xff);
      printf("Level type: %u\n",(regs[2] >> 8) & 0xff);
      printf("x2APIC ID the current logical processor: %u\n",regs[3]);
      putchar('\n');
    }
  }
  if(maxi >= 13){
    regs[0] = 13;
    cpuid(regs);
    printf("XFEATURE_ENABLED_MASK low 32 bits: %08x\n",regs[0]);
    printf("Maximum size in bytes required by enabled features in XFEATURE_ENABLED_MASK (XCR0): %u\n",
	   regs[1]);
    printf("Maximum size in bytes of the XSAVE/XRSTOR save area: %u\n",regs[2]);
    printf("XFEATURE_ENABLED_MASK high 32 bits: %08u\n",regs[3]);
    /* Incomplete - should enumerate ECX > 1 */
  }


  /* Check for extended information */
  regs[0] = 0x80000000;
  cpuid(regs);
  if(regs[0] >= 0x80000000){
    maxi = regs[0] - 0x80000000;
  } else {
    maxi = 0;
    printf("No extended CPUID information\n");
  }
  if(maxi >= 1){
    regs[0] = 0x80000001;
    cpuid(regs);
    if(regs[2] || regs[3]){
      printf("Extended leaf 1, feature flags:\n");
      if(regs[2]){
	printf("Register ECX = %08x:\n",regs[2]);
	if(regs[2] & (1 << 0))
	  printf(" 0 LAHF      LAHF/SAHF available in 64-bit mode\n");
      }
      if(regs[3]){
	printf("Register EDX = %08x:\n",regs[3]);
	
	if(regs[3] & (1 << 11))
	  printf("11 SYSCALL    SYSCALL/SYSRET available in 64-bit mode\n");
	if(regs[3] & (1 << 20))
	  printf("20 XD Bit     Execute Disable (NX) Bit available\n");
	if(regs[3] & (1 << 29))
	  printf("29 64-bit architecture available\n");
      }
      putchar('\n');
    }
  }
  if(maxi >= 4){ /* leaves 2, 3 and 4 all required for brand string feature */
    printf("Extended leaves 2-4, Processor Brand String: '");
    /* Show it literally with leading/trailing spaces */
    for(i=0x80000002;i<=0x80000004;i++){
      int j;
      regs[0] = i;
      cpuid(regs);
      for(j=0;j<4;j++)
	putreg(regs[j]);
    }
    printf("'\n\n");
  }
  if(maxi >= 6){
    int a;
    
    printf("Extended leaf 6, L2 cache information:\n");
    regs[0] = 0x80000006;
    cpuid(regs);
    printf("Cache line size: %u\n",regs[2] & 0xff);
    printf("L2 Associativity: ");
    a = (regs[2] >> 12) & 0xf;
    switch(a){
    case 0:
      printf("disabled");
      break;
    case 1:
      printf("direct mapped");
      break;
    case 2:
      printf("2-way");
      break;
    case 4:
      printf("4-way");
      break;
    case 6:
      printf("8-way");
      break;
    case 8:
      printf("16-way");
      break;
    case 15:
      printf("Fully associative");
      break;
    default:
      printf(" unknown code %d",a);
      break;
    }
    putchar('\n');
    printf("Cache size: %u K\n",(regs[2] >> 16) & 0xffff);
    putchar('\n');
  }
  if(maxi >= 7){
    regs[0] = 0x80000007;
    cpuid(regs);
    if(regs[3]){
      printf("Extended leaf 7; Advanced Power Management:\n");
      if(regs[3] & (1<<8))
	printf(" 8 Invariant TSC available\n");
      putchar('\n');
    }
  }
  if(maxi >= 8){
    printf("Extended leaf 8; address sizes:\n");
    regs[0] = 0x80000008;
    cpuid(regs);
    printf("Physical address bits: %u\n",regs[0] & 0xff);
    printf("Virtual address bits: %u\n",(regs[0] >> 8) & 0xff);
    putchar('\n');
  }
#if 0
  /* Determine if denormals are zero - unfinished */
  {
    int i,j;
    union {
      char area[512];
      __m128i foo;
    } save;
    
    if(((long)(save.area) & 0xf) == 0){
      regs[0] = 1;
      cpuid(regs);
      if((regs[3] & (1<<24)) && (regs[3] & (3<<25))){
	/* Has FXSAVE/FXRSTOR and either SSE or SSE2 */
	asm("fxsave (%0);" : : "r"(save.area));
	printf("FXSAVE area (incomplete):\n");
	for(i=0;i<128;i+=4){
	  for(j=3;j>=0;j--)
	    printf("%08x ",save.area[i+j]);
	  putchar('\n');
	}
      }
    }
  }
#endif
}
      
/* Rewrite this to extract generic cache data in the same format regardless of how we get it
 * Cache level (1/2/3)
 * Data/Instruction/Unified
 * Total Size
 * Line size
 * associativity
 */


do_intel_cache(){
  unsigned int regs[4];
  int maxi;

  regs[0] = 0;
  cpuid(regs);
  maxi = regs[0] & 0xffff;
  if(maxi >= 4){
    int level;

    for(level=0;level < 32;level++){
	int size,line_size,sets,assoc;
	
	regs[0] = 4; regs[2] = level;
	cpuid(regs);
	if((regs[0] & 0x1f) == 0)
	  break;
	line_size = (regs[1] & 0xfff) + 1; /* Line size */
	sets = regs[2] + 1;
	assoc = (regs[1] >> 22) + 1; /* ways of associativity */
	size = line_size * sets * assoc;
	printf("L%1u",(regs[0] >> 5) & 7);
	switch(regs[0] & 0x1f){
	case 1:
	  printf(" Data       ");
	  break;
	case 2:
	  printf(" Instruction");
	  break;
	case 3:
	  printf(" Unified    ");
	  break;
	}
	/* Format size */
	if((size & 0x3fffffff) == 0){
	  /* Multiple of a gigabyte (!) */
	  printf("%d GB",size >> 30);
	} else if((size  & 0xfffff) == 0){
	  /* Multiple of a megabyte */
	  printf("%5d MB",size >> 20);
	} else if((size & 0x3ff) == 0){
	  /* Multiple of a kilobyte */
	  printf("%5d KB",size >> 10);
	} else {
	  printf("%8d",size);
	}
	printf("%7u",((regs[0] >> 14) & 0xfff) + 1 ); /* Logical IDs */
	
	printf("%10d",line_size);
	printf("%17u",((regs[1] >> 12) & 0x3ff) + 1); /* Physical line partitions */
	if(regs[0] & (1<<9))
	  printf("%15s","Full");
	else
	  printf("%11d-way",assoc);
	printf("%7d",sets); /* Sets */
	printf("%s",regs[0] & (1<<8) ? " Self init;" : "");
	printf("%s",regs[3] & (1<<0) ? " WB inval;" : "");
	printf("%s",regs[1] & (1<<1) ? " Inclusive;" : "");
	putchar('\n');
    }
  } else if(maxi >= 2){
    int reg,ntlb,msg40 = 0,l2present=0;
    char *messages[256];
    int i,n=0;
    do {
      regs[0] = 2;
      cpuid(regs);
      ntlb = regs[0] & 0xff;
      for(reg=0;reg<4;reg++){
	int byte;
	if(regs[reg] & 0x80000000)
	  continue; /* Skip register if high bit set */
	
	for(byte = (reg == 0)? 1 : 0  ;  byte<4;  byte++){ /* Skip low byte of reg[0], that's ntlb */
	  char *msg;
	  int desc;
	  
	  desc = (regs[reg] >> (8*byte)) & 0xff;
	  
	  if(desc == 0)
	    continue;
	  if(desc == 0x40){
	    msg40++;
	    continue;
	  }
	  msg = intel_tlb[desc];
	  if(strncmp(msg,"L2",2) == 0)
	    l2present++;
	  messages[n++] = msg;
	}
      }
    } while (ntlb > 1);
    qsort(messages,n,sizeof(char *),scompare);
    for(i=0;i<n;i++){
      printf("%s\n",messages[i]);
    }
    /* Descriptor 0x40 says "No L2 cache; or if L2 cache present, no L3 cache"
     * so for hack value, figure out which is the case
     */
    if(msg40){
      if(l2present)
	printf("No L3 cache\n");
      else
	printf("No L2 cache\n");
    }
  } else {
    printf("maxi = %d\n",maxi);
      printf("Cache information unavailable\n");
  }
}

