/* AMD-specific CPUID data
 * $Id: amd.c,v 1.3 2009/04/06 05:40:58 karn Exp karn $
 * Copyright 2009, Phil Karn, KA9Q
 * Available under the terms of the GNU General Public License
 */

#include <stdio.h>
#include "cpuid.h"

struct featureflag AMD_standard_ecx[] = {
  {"SSE3","SSE3 instructions"},
  {NULL, NULL},
  {NULL, NULL},
  {"MONITOR","MONITOR/MWAIT"},
  {NULL, NULL},
  {NULL, NULL},
  {NULL, NULL},
  {NULL, NULL},
  {NULL, NULL},
  {"SSSE3","Supplemental SSE3 instructions"},
  {NULL,NULL},
  {NULL,NULL},
  {NULL,NULL},
  {"CMPXCHG16B","CMPXCHG16B instruction"},
  {NULL,NULL},
  {NULL,NULL},
  {NULL,NULL},
  {NULL,NULL},
  {NULL,NULL},
  {"SSE4.1","SSE4.1 instructions"},
  {NULL,NULL},
  {NULL,NULL},
  {NULL,NULL},
  {"POPCNT","Population count instruction"},
  {NULL,NULL},
  {NULL,NULL},
  {NULL,NULL},
  {NULL,NULL},
  {NULL,NULL},
  {NULL,NULL},
  {NULL,NULL},
  {"VM","Virtual Machine Hypervisor present"},
};

struct featureflag AMD_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},
  {"SYSENTER","SYSENTER and SYSEXIT Instructions"},
  {"MTRR","Memory Type Range Registers"},
  {"PGE","PTE Global Flag"},
  {"MCA","Machine Check Architecture"},
  {"CMOV","Conditional Move and Compare Instructions"},
  {"PAT","Page Attribute Table"},
  {"PSE36","36-bit Page Size extension"},
  {NULL,NULL},
  {"CLFSH","Instruction"},
  {NULL,NULL},
  {NULL,NULL},
  {NULL,NULL},
  {"MMX","MMX instruction set"},
  {"FXSR","Fast FP/MMX Streaming SIMD extensions save/restore"},
  {"SSE","Streaming SIMD extensions instruction set"},
  {"SSE2","SSE2 extensions"},
  {NULL,NULL},
  {"HTT","Hyperthreading"},
  {NULL,NULL},
  {NULL,NULL},
  {NULL,NULL}
};

struct featureflag AMD_extended_ecx[] = {
  {"LahfSahf","LAHF and SAHF instructions in 64-bit mode"},
  {"CmpLeg","core multi-processing legacy mode"},
  {"SVM","Secure virtual machine"},
  {"ExtApicSpac","extended APIC space"},
  {"AltMovCr8","LOCK MOV CR0 means MOV CR8"},
  {"ABM","Advanced bit manipulation, LZCNT instructions"},
  {"SSE4a","EZXZTRQ, INSERTQ, MOVNTSS and MOVNTSD instructions"},
  {"MisAlignSse","Misaligned SSE mode instructions"},
  {"3DNowPrefetch","PREFETCH and PREFETCHW instructions"},
  {"OSVW","OS visible workaround"},
  {"IBS","instruction based sampling"},
  {"SSE5","SSE5 instructions"},
  {"SKINIT","SKINIT and STGI are supported independent of the value of MSRC000_0080{SVME]"},
  {"WDT","Watchdog timer support"},
  {NULL,NULL},
  {NULL,NULL},
  {NULL,NULL},
  {NULL,NULL},
  {NULL,NULL},
  {NULL,NULL},
  {NULL,NULL},
  {NULL,NULL},
  {NULL,NULL},
  {NULL,NULL},
  {NULL,NULL},
  {NULL,NULL},
  {NULL,NULL},
  {NULL,NULL},
  {NULL,NULL},
  {NULL,NULL},
  {NULL,NULL},
  {NULL,NULL},
};
struct featureflag AMD_extended_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},
  {"SYSENTER","SYSENTER and SYSEXIT Instructions"},
  {"MTRR","Memory Type Range Registers"},
  {"PGE","PTE Global Flag"},
  {"MCA","Machine Check Architecture"},
  {"CMOV","Conditional Move and Compare Instructions"},
  {"PAT","Page Attribute Table"},
  {"PSE36","36-bit Page Size extension"},
  {NULL,NULL},
  {NULL,NULL},
  {"NX","No-execute page protection"},
  {NULL,NULL},
  {"MMXExt","AMD extensions to MMX instructions"},
  {"MMX","MMX instructions"},
  {"FXSR","Fast FP/MMX Streaming SIMD extensions save/restore"},
  {"FFXSR","FXSAVE and FXRSTOR instruction optimizations"},
  {"Page1GB","1 GB large page support"},
  {"RDTSCP","RDTSCP instruction"},
  {NULL,NULL},
  {"LM","Long mode"},
  {"3DNowExt","Extended 3DNow! instructions"},
  {"3DNow","3DNow! instructions"}
};


/* AMD-specific information */
void doamd(){
  unsigned int i;
  unsigned int regs[4];
  int stepping,model,family,reserved;
  int maxi;

  printf("AMD-specific functions\n");

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

  /* Do standard stuff */
  if(maxi >= 1){
    regs[0] = 1;
    cpuid(regs);
    stepping = regs[0] & 0xf;
    model = (regs[0] >> 4) & 0xf;
    family = (regs[0] >> 8) & 0xf;
    reserved = regs[0] >> 12;

    printf("Family: %d Model: %d [",family,model);
    switch(family){
    case 4:
      printf("486");
      break;
    case 5:
      printf("586/K6");
      break;
    }
    printf("]\n\n");

    printf("Feature flags in register EDX = %08x:\n",regs[3]);
    for(i=0;i<32;i++){
      if(regs[3] & (1<<i)){
	if(AMD_standard_edx[i].abbrev != NULL)
	  printf("%2d %-12s %s\n",i,AMD_standard_edx[i].abbrev,AMD_standard_edx[i].full);
	else
	  printf("%2d %-12s %s\n",i,"","Reserved");
      }
    }
    putchar('\n');
    
    /* Flags in ecx register */
    printf("Feature flags in register ECX = %08x:\n",regs[2]);
    for(i=0;i<32;i++){
      if(regs[2] & (1<<i)){
	if(AMD_standard_ecx[i].abbrev != NULL)	
	  printf("%2d %-12s %s\n",i,AMD_standard_ecx[i].abbrev,AMD_standard_ecx[i].full);
	else
	  printf("%2d %-12s %s\n",i,"","Reserved");
      }
    }
    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 supported\n");
    if(regs[2] & (1<<1))
      printf(" 1 IBE MWAIT can use ECX bit 0 to allow interrupts to cause exit from monitor event 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');
  }
  /* End of base leaves */


  /* Check for extended info */
  regs[0] = 0x80000000;
  cpuid(regs);
  if(regs[0] >= 0x80000000){
    maxi = regs[0] - 0x80000000;
  } else {
    maxi = 0;
  }

  if(maxi >= 1){
    printf("Extended leaf 1, feature flags:\n");
    regs[0] = 0x80000001;
    cpuid(regs);
    stepping = regs[0] & 0xf;
    model = (regs[0] >> 4) & 0xf;
    family = (regs[0] >> 8) & 0xf;
    reserved = regs[0] >> 12;

    printf("Family: %d Model: %d [",family,model);
    switch(family){
    case 4:
      printf("486");
      break;
    case 5:
      printf("586");
      break;
    case 6:
      printf("K6");
      break;
    }
    printf("]\n");

    printf("Extended feature flags in register EDX = %08x:\n",regs[3]);
    decode_feature_flags(AMD_extended_edx,regs[3]);

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

  if(maxi >= 4){
    int i;

    /* Processor identification string */
    printf("Extended leaves 2-4, Processor name string: ");
    for(i=0x80000002;i<=0x80000004;i++){
      int j;

      regs[0] = i;
      cpuid(regs);
      for(j=0;j<4;j++)
	putreg(regs[j]);
    }
    putchar('\n');
    putchar('\n');
  }

  if(maxi >= 5){
    /* L1 cache and TLB info */
    regs[0] = 0x80000005;
    cpuid(regs);
    printf("Data TLB, 2/4 MB pages: associativity: %u entries: %u\n",
	   regs[0] >> 24,(regs[0] >> 16) & 0xff);
    printf("Instruction TLB, 2/4 MB pages: associativity: %u entries: %u\n",
	   (regs[0] >> 8) & 0xff ,regs[0] & 0xff);
    printf("Data TLB, 4KB pages: associativity: %u entries: %u\n",
	   regs[1] >> 24,(regs[1] >> 16) & 0xff);
    printf("Instruction TLB, 4KB pages: associativity: %u entries: %u\n",
	   (regs[1] >> 8) & 0xff,regs[1] & 0xff);
    printf("L1 Data cache size: %u KB associativity: %u lines per tag: %u line size: %u\n",
	   regs[2] >> 24,(regs[2]>>16) & 0xff,(regs[2] >> 8)&0xff,regs[2]&0xff);
    printf("L1 Instruction cache size: %u KB associativity: %u lines per tag: %u line size: %u\n",
	   regs[3] >> 24,(regs[3]>>16) & 0xff,(regs[3] >> 8)&0xff,regs[3]&0xff);
    putchar('\n');
  }
  
  /* check K6-III (and later?) on-chip L2 cache size */
  if (maxi >= 6) {
    regs[0] = 0x80000006;
    cpuid(regs);
    printf("L2 Data TLB, 2/4 MB pages: associativity: %u entries: %u\n",
	   regs[0] >> 24,(regs[0] >> 16) & 0xff);
    printf("L2 Instruction TLB, 2/4 MB pages: associativity: %u entries: %u\n",
	   (regs[0] >> 8) & 0xff ,regs[0] & 0xff);
    printf("L2 Data TLB, 4KB pages: associativity: %u entries: %u\n",
	   regs[1] >> 24,(regs[1] >> 16) & 0xff);
    printf("L2 Instruction TLB, 4KB pages: associativity: %u entries: %u\n",
	   (regs[1] >> 8) & 0xff,regs[1] & 0xff);
    printf("L2 Cache: size %u KB associativity: %u lines per tag %u line size %u\n",
	   regs[2] >> 24,(regs[2]>>16) & 0xff,(regs[2] >> 8)&0xff,regs[2]&0xff);
    printf("L3 cache: size %u KB associativity: %u lines per tag %u line size %u\n",
	   regs[3] >> 24,(regs[3]>>16) & 0xff,(regs[3] >> 8)&0xff,regs[3]&0xff);
    putchar('\n');
  }
  if(maxi >= 7){
    regs[0] = 0x80000007;
    cpuid(regs);
    if(regs[3]){
      printf("Extended leaf 7, Advanced Power Management Information:\n");
      if(regs[3] & (1<<0))
	printf(" 0 TS - Temperature sensor\n");
      if(regs[3] & (1<<1))
	printf(" 1 FID - Frequency ID control\n");
      if(regs[3] & (1<<2))
	printf(" 2 VID - Voltage ID control\n");
      if(regs[3] & (1<<3))
	printf(" 3 TTP - THERMTRIP\n");
      if(regs[3] & (1<<4))
	printf(" 4 TM - Hardware thermal control\n");
      if(regs[3] & (1<<5))
	printf(" 5 STC - Software thermal control\n");
      if(regs[3] & (1<<6))
	printf(" 6 100 MHz Steps - 100 MHz multiplier control\n");
      if(regs[3] & (1<<7))
	printf(" 7 HwPState - Hardware P-State control\n");
      if(regs[3] & (1<<8))
	printf(" 8 Invariant TSC available\n");
      putchar('\n');
    }
  }

  if(maxi >= 8){
    int nc,mnc;

    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);
    printf("Maximum guest physical byte address size in bits: %u\n",(regs[0] >> 16) & 0xff);
    nc = regs[2] & 0xff;
    printf("Number of CPU cores: %u\n",nc+1);
    mnc = (regs[2] >> 12) & 0xf;
    if(nc == 0)
      printf("ApicIdCoreIdSize %u\n",nc+1);
    else
      printf("ApicIdCoreIdSize %u\n",1<<mnc);
    putchar('\n');
  }
  if(maxi >= 0xa){
    regs[0] = 0x8000000a;
    cpuid(regs);
    printf("Extended leaf 10, SVM revision and feature info:\n");
    printf("SvmRev: %u\n",regs[0] & 0xff);
    printf("Number of address space identifiers; %u\n",regs[1]);
    if(regs[3] & (1<<0))
      printf(" 0 NP - nested paging\n");
    if(regs[3] & (1<<1))
      printf(" 1 LBR virtualization\n");
    if(regs[3] & (1<<2))
      printf(" 2 SVM Lock\n");
    if(regs[3] & (1<<3))
      printf(" 3 NRIP save\n");
    if(regs[3] & (1<<9))
      printf(" 9 Ssse3Sse5Dis - SSSE3 and SSE5 opcode set disable\n");
    putchar('\n');
  }
  if(maxi >= 0x19){
    regs[0] = 0x80000019;
    cpuid(regs);
    printf("Extended leaf 0x19, TLB 1GB page identifiers\n");
    printf("L1 Instruction TLB entries for 1 GB pages: %u\n",regs[0] & 0xfff);
    printf("L1 Instruction TLB associativity for 1 GB pages: %u\n",(regs[0] >> 12) & 0xf);
    printf("L1 Data TLB entries for 1 GB pages; %u\n",(regs[0] >> 16) & 0xfff);
    printf("L1 Data TLB associativity for 1 GB pages: %u\n",(regs[0] >> 28) & 0xf);

    printf("L2 Instruction TLB entries for 1 GB pages: %u\n",regs[1] & 0xfff);
    printf("L2 Instruction TLB associativity for 1 GB pages: %u\n",(regs[1] >> 12) & 0xf);
    printf("L2 Data TLB entries for 1 GB pages; %u\n",(regs[1] >> 16) & 0xfff);
    printf("L2 Data TLB associativity for 1 GB pages: %u\n",(regs[1] >> 28) & 0xf);
    putchar('\n');
  }
  if(maxi >= 0x1a){
    regs[0] = 0x8000001a;
    cpuid(regs);
    printf("Extended leaf 0x1a, performance optimization identifiers\n");
    if(regs[0] & (1<<0))
      printf(" 0 FP128 - 128-bit SSE full width internal excution\n");
    if(regs[0] & (1<<1))
      printf(" 1 MOVU - MOVU SSE is more efficient than MOVL/MOVH; MOVUPS is more efficient than MOVLPS/HPS;\
MOVUPD is more efficient than MOVLPD/HPD\n");
    putchar('\n');
  }

}
