/* AMD (formerly NSC) Geode-specific CPUID data
 * $Id: geode.c,v 1.3 2009/04/06 08:51:47 karn Exp $
 * Copyright 2009, Phil Karn, KA9Q
 * Available under the terms of the GNU General Public License
 */
#include <stdio.h>
#include "cpuid.h"

struct featureflag Geode_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"},
  {NULL,NULL},
  {NULL,NULL},
  {NULL,NULL},
  {NULL,NULL},
  {NULL,NULL},
  {NULL,NULL},
  {"MMX","MMX instruction set"},
  {"FXSR","Fast FP/MMX Streaming SIMD Extensions save/restore"},
  {NULL,NULL},
  {NULL,NULL},
  {NULL,NULL},
  {NULL,NULL},
  {NULL,NULL},
  {NULL,NULL},
  {NULL,NULL},
};

char *geode_tlb[] = {
  /* 00 */
  NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,

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

  /* 20 */
  NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,

  /* 30 */
  NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,

  /* 40 */
  NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,

  /* 50 */
  NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,

  /* 60 */
  NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,

  /* 70 */
  "TLB: 4KB pages, 4-way, 32 entries, 4-way",
  NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,

  /* 80 */
  "L1 cache: 16 KB, 4-way, 16 bytes/line",
  NULL,NULL,NULL,NULL,NULL,NULL,NULL,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 */  
  NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,

  /* C0 */
  NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,

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

  /* E0 */
  NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,

  /* F0 */
  NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,
};


/* Geode-specific information 
 *  Hacked from Intel routine, and there are differences that need to be fixed
 */
void dogeode(){
  int ntlb;
  unsigned int regs[4];
  int maxi;

  printf("Geode-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;

    printf("EAX = %08x; ",regs[0]);
    printf("Family: 0x%x Model: 0x%x Type: %d [",family,model,type);

    printf("Brand index %u: ",regs[1] & 0xff);
    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]){
      printf("Feature flags in register EDX = %08x:\n",regs[3]);
      decode_feature_flags(Geode_standard_edx,regs[3]);
    }
  }

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

    /* 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;
      if(ntlb != 0)
	ntlb = ntlb - 1;
      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 = (regs[reg] >> (8*byte)) & 0xff;

	  switch(desc){
	  case 0:
	    continue;
	  default:
	    if((msg = geode_tlb[desc]) == NULL)
	      asprintf(&msg,"Unknown cache/TLB descriptor %d (0x%x)",desc);
	    messages[n++] = msg;
	    break;
	  }
	}
      }
    } while (ntlb != 0);
    qsort(messages,n,sizeof(char *),(int (*)(const void *,const void *))scompare);
    for(i=0;i<n;i++){
      printf("%s\n",messages[i]);
    }
  }
}


