/*
 *  This file is part of x86info.
 *  (C) 2001-2006 Dave Jones.
 *
 *  Licensed under the terms of the GNU GPL License version 2.
 */

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#if __FreeBSD__ < 500000
#include <sys/types.h>
#include <sys/sysctl.h>
#endif

#include "x86info.h"

int show_bench=0;
int show_bios=0;
int show_bluesmoke=0;
int show_bugs=0;
int show_cacheinfo=0;
int show_connector=0;
int show_eblcr=0;
int show_msr=0;
int show_microcode=0;
int show_mtrr=0;
int show_pm=0;
int show_registers=0;
int show_urls=0;

static int show_mptable=0;
static int show_flags=0;
static int show_MHz=0;

int verbose=0;
int silent = 0;
int used_UP = 0;
int user_is_root = 1;
static int need_root = 0;

unsigned int nrCPUs=1;
static unsigned int nrSMPCPUs;

static void usage (char *programname)
{
	printf ("Usage: %s [<switches>]\n\
-a,   --all\n\
      --bench\n\
	  --bios\n\
      --bugs\n\
-c,   --cache\n\
      --connector\n\
-f,   --flags\n\
-mhz, --mhz\n\
      --microcode\n\
-mp,  --mptable\n\
-m,   --msr\n\
      --mult\n\
      --mtrr\n\
      --pm\n\
-r,   --registers\n\
-s,   --show-bluesmoke\n\
-u,   --urls\n\
-v,   --verbose\n\
\n", programname);
	exit (0);
}

static void parse_command_line (int argc, char **argv)
{
	char **argp, *arg;

	for (argp = argv+1; argp <= argv + argc && (arg = *argp); argp++) {
		if ((!strcmp(arg, "-a") || !strcmp(arg, "--all"))) {
//			show_bench = 1;
			show_bios = 1;
			show_bluesmoke = 1;
			show_bugs = 1;
			show_cacheinfo = 1;
			show_connector = 1;
			show_eblcr =1;
			show_flags = 1;
			show_microcode = 1;
			show_mptable =1;
			show_msr = 1;
			show_MHz = 1;
			show_mtrr = 1;
			show_pm = 1;
			show_registers = 1;
			show_urls = 1;
			need_root = 1;
		}

//		if (!strcmp(arg, "--bench"))
//			show_bench = 1;

		if (!strcmp(arg, "--bios")) {
			need_root = 1;
			show_bios = 1;
		}

		if (!strcmp(arg, "--bugs"))
			show_bugs = 1;

		if ((!strcmp(arg, "-c") || !strcmp(arg, "--cache")))
			show_cacheinfo = 1;

		if (!strcmp(arg, "--connector"))
			show_connector = 1;

		if ((!strcmp(arg, "-f") || !strcmp(arg, "--flags")))
			show_flags = 1;

		if ((!strcmp(arg, "-m") || !strcmp(arg, "--msr"))) {
			need_root = 1;
			show_msr = 1;
		}

		if (!strcmp(arg, "--microcode")) {
			need_root = 1;
			show_microcode = 1;
		}

		if ((!strcmp(arg, "-mhz") || !strcmp(arg, "--mhz")))
			show_MHz = 1;

		if ((!strcmp(arg, "-mp") || !strcmp(arg, "--mptable"))) {
			need_root = 1;
			show_mptable = 1;
		}

		if (!strcmp(arg, "--mtrr")) {
			need_root = 1;
			show_mtrr = 1;
		}

		if (!strcmp(arg, "--mult")) {
			need_root = 1;
			show_eblcr = 1;
		}

		if (!strcmp(arg, "--pm"))
			show_pm = 1;

		if ((!strcmp(arg, "-r") || !strcmp(arg, "--registers")))
			show_registers = 1;

		if ((!strcmp(arg, "-s") || !strcmp(arg, "--show-bluesmoke"))) {
			need_root = 1;
			show_bluesmoke = 1;
		}

		if ((!strcmp(arg, "-u") || !strcmp(arg, "--urls")))
			show_urls = 1;

		if ((!strcmp(arg, "-v") || !strcmp(arg, "--verbose")))
			verbose = 1;

		if ((!strcmp(arg, "?") || !strcmp(arg, "--help")))
			usage(argv[0]);	
	}
}


static void separator(void)
{
	int j;

	for (j=0; j<74; j++)
		printf ("-");
	printf ("\n");
}


int main (int argc, char **argv)
{
	unsigned int i;
	struct cpudata *cpu, *head=NULL, *tmp;
#if __FreeBSD__ < 500000
	size_t nr = sizeof(nrCPUs);
#endif

	parse_command_line(argc, argv);
	if (!silent) {
		printf ("x86info v1.20.  Dave Jones 2001-2006\n");
		printf ("Feedback to <davej@redhat.com>.\n\n");
	}

	if ((HaveCPUID())==0) {
		printf ("No CPUID instruction available.\n");
		printf ("No further information available for this CPU.\n");
		return 0;
	}

	if (getuid()!=0)
		user_is_root=0;

	if (need_root && !user_is_root)
		printf ("Need to be root to use specified options.\n");

#if __FreeBSD__ > 500000
	nrCPUs = sysconf (_SC_NPROCESSORS_CONF);
#else
	sysctlbyname("hw.ncpu", &nrCPUs, &nr, NULL, 0);
#endif

	if (!silent) {
		printf ("Found %u CPU", nrCPUs);
		if (nrCPUs > 1)
			printf ("s");

		/* Check mptable if present. This way we get number of CPUs
		   on SMP systems that have booted UP kernels. */
		if (user_is_root) {
			nrSMPCPUs = enumerate_cpus();
			if (nrSMPCPUs > nrCPUs)
				printf (", but found %ud CPUs in MPTable.", nrSMPCPUs);
		}
		printf ("\n");
	}

	/*
	 * can't have less than 1 CPU, or more than
	 * 65535 (some arbitrary large number)
	 */
	if ((nrCPUs < 1) || (nrCPUs > 65535)) {
		printf("CPU count is bogus: defaulting to 1 CPU.\n");
		nrCPUs = 1;
	}

	if (show_mptable && user_is_root)
		(void)issmp(1);		// FIXME: issmp should become 'show_mptable'

	separator();

	/* Iterate over the linked list. */

	for (i=0; i<nrCPUs; i++) {
		cpu = malloc (sizeof (struct cpudata));
		if (!cpu) {
			printf ("Out of memory\n");
			return -1;
		}

		memset (cpu, 0, sizeof(struct cpudata));

		if (!head) {
			head = cpu;
		} else {
			cpu->next = head;
			head = cpu;
		}

		cpu->number = i;

		if (!silent && nrCPUs!=1)
			printf ("CPU #%u\n", i+1);

		estimate_MHz(cpu);
		identify (cpu);
		show_info (cpu);

		/*
		 * Doing this per-cpu is a problem, as we can't
		 * schedule userspace code per-cpu.
		 * Although running nrCPUs * threads would probably work.
		 *
		 * Could also experiment with the new scheduler binding syscalls.
		 */
		if (show_MHz) {
			if (cpu->MHz < 1000)
				printf("%uMHz", cpu->MHz);
			else {
				int a = (cpu->MHz / 1000);
				int b = ((cpu->MHz % 1000)/100);
				int c = (a*1000)+(b*100);

				printf("%u.%u%uGHz", a, b, (cpu->MHz - c)/10);
			}
			printf (" processor (estimate).\n\n");
		}
		if (show_bench)
			show_benchmarks();

		if (nrCPUs>1)
			separator();

		if (cpu->next)
			cpu = cpu->next;
	}

	/* Tear down the linked list. */
	cpu = head;
	for (i=0; i<nrCPUs; i++) {
		if (cpu->datasheet_url)
			free(cpu->datasheet_url);
		if (cpu->errata_url)
			free(cpu->errata_url);
		tmp = cpu->next;
		free(cpu);
		cpu = tmp;
	}

	if (nrCPUs > 1 && used_UP==1 && (!silent)) {
		printf ("WARNING: Detected SMP, but unable to access cpuid driver.\n");
		printf ("Used Uniprocessor CPU routines. Results inaccurate.\n");
	}

	return (0);
}


syntax highlighted by Code2HTML, v. 0.9.1