/* $Id: misc.c,v 1.11 2003/03/10 22:07:58 mikpe Exp $
 * Miscellaneous perfctr operations.
 *
 * Copyright (C) 1999-2003  Mikael Pettersson
 */

#include <errno.h>
#include <sys/ioctl.h>
#include <stdio.h>
#include "libperfctr.h"

int _perfctr_abi_check_fd(int fd, unsigned int user_abi_version)
{
    unsigned int driver_abi_version;
    
    if( ioctl(fd, PERFCTR_ABI, &driver_abi_version) < 0 ) {
	perror("perfctr_abi_check");
	return -1;
    }
    if( (driver_abi_version ^ user_abi_version) & 0xFF00FF00 ) {
	fprintf(stderr, "Error: perfctr ABI major version mismatch: "
		"driver ABI 0x%08X, user ABI 0x%08X\n",
		driver_abi_version, user_abi_version);
	errno = EPROTO;
	return -1;
    }
    return 0;
}

int perfctr_info(int fd, struct perfctr_info *info)
{
    return ioctl(fd, PERFCTR_INFO, info);
}

unsigned perfctr_info_nrctrs(const struct perfctr_info *info)
{
    switch( info->cpu_type ) {
      case PERFCTR_X86_GENERIC:
	return 0;
      case PERFCTR_X86_VIA_C3:
	return 1;
      case PERFCTR_X86_AMD_K7:
      case PERFCTR_X86_AMD_K8:
	return 4;
      case PERFCTR_X86_INTEL_P4:
      case PERFCTR_X86_INTEL_P4M2:
	return 18;
      default:
	return 2;
    }
}

const char *perfctr_info_cpu_name(const struct perfctr_info *info)
{
    switch( info->cpu_type ) {
      case PERFCTR_X86_GENERIC:
	return "Generic x86 with TSC";
      case PERFCTR_X86_INTEL_P5:
        return "Intel Pentium";
      case PERFCTR_X86_INTEL_P5MMX:
        return "Intel Pentium MMX";
      case PERFCTR_X86_INTEL_P6:
        return "Intel Pentium Pro";
      case PERFCTR_X86_INTEL_PII:
        return "Intel Pentium II";
      case PERFCTR_X86_INTEL_PIII:
        return "Intel Pentium III";
      case PERFCTR_X86_CYRIX_MII:
        return "Cyrix 6x86MX/MII/III";
      case PERFCTR_X86_WINCHIP_C6:
	return "WinChip C6";
      case PERFCTR_X86_WINCHIP_2:
	return "WinChip 2/3";
      case PERFCTR_X86_AMD_K7:
	return "AMD K7";
      case PERFCTR_X86_VIA_C3:
	return "VIA C3";
      case PERFCTR_X86_INTEL_P4:
	return "Intel Pentium 4";
      case PERFCTR_X86_INTEL_P4M2:
	return "Intel Pentium 4 Model 2";
      case PERFCTR_X86_AMD_K8:
	return "AMD K8";
      default:
        return "?";
    }
}

static void print_cpus(unsigned long cpus)
{
    unsigned int nrcpus, nr;
    unsigned long mask;

    printf("0x%lX ([", cpus);
    nrcpus = 0;
    for(nr = 0, mask = 1; cpus != 0; nr += 1, mask <<= 1) {
	if( cpus & mask ) {
	    cpus &= ~mask;
	    if( nrcpus )
		printf(",");
	    ++nrcpus;
	    printf("%u", nr);
	}
    }
    printf("], total: %u)\n", nrcpus);
}

void perfctr_info_print(const struct perfctr_info *info)
{
    static const char * const features[] = { "rdpmc", "rdtsc", "pcint" };
    int fi, comma;

    printf("abi_version\t\t0x%08X\n", info->abi_version);
    printf("driver_version\t\t%s\n", info->driver_version);
    printf("cpus\t\t\t"); print_cpus(info->cpus);
    printf("cpus_forbidden\t\t"); print_cpus(info->cpus_forbidden);
    printf("cpu_type\t\t%u (%s)\n", info->cpu_type, perfctr_info_cpu_name(info));
    printf("cpu_features\t\t%#x (", info->cpu_features);
    for(comma = 0, fi = 0; fi < sizeof features / sizeof features[0]; ++fi) {
	unsigned fmask = 1 << fi;
	if( info->cpu_features & fmask ) {
	    if( comma )
		printf(",");
	    printf("%s", features[fi]);
	    comma = 1;
	}
    }
    printf(")\n");
    printf("cpu_khz\t\t\t%lu\n", info->cpu_khz);
    printf("cpu_nrctrs\t\t%u\n", perfctr_info_nrctrs(info));
}

void perfctr_cpu_control_print(const struct perfctr_cpu_control *control)
{
    unsigned int i, nractrs, nrictrs, nrctrs;

    nractrs = control->nractrs;
    nrictrs = control->nrictrs;
    nrctrs = control->nractrs + nrictrs;

    printf("tsc_on\t\t\t%u\n", control->tsc_on);
    printf("nractrs\t\t\t%u\n", nractrs);
    if( nrictrs )
	printf("nrictrs\t\t\t%u\n", nrictrs);
    for(i = 0; i < nrctrs; ++i) {
        if( control->pmc_map[i] >= 18 ) /* for P4 'fast rdpmc' cases */
            printf("pmc_map[%u]\t\t0x%08X\n", i, control->pmc_map[i]);
        else
            printf("pmc_map[%u]\t\t%u\n", i, control->pmc_map[i]);
        printf("evntsel[%u]\t\t0x%08X\n", i, control->evntsel[i]);
        if( control->p4.escr[i] )
            printf("escr[%u]\t\t\t0x%08X\n", i, control->p4.escr[i]);
	if( i >= nractrs )
	    printf("ireset[%u]\t\t%d\n", i, control->ireset[i]);
    }
    if( control->p4.pebs_enable )
	printf("pebs_enable\t\t0x%08X\n", control->p4.pebs_enable);
    if( control->p4.pebs_matrix_vert )
	printf("pebs_matrix_vert\t0x%08X\n", control->p4.pebs_matrix_vert);
}
