/* $Id: detect_cpu.c 1530 2006-01-12 20:07:40Z mipsator $ */

/*
 * Copyright (c) 2004-2005 Damien Couderc
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 *    - Redistributions of source code must retain the above copyright
 *      notice, this list of conditions and the following disclaimer.
 *    - Redistributions in binary form must reproduce the above
 *      copyright notice, this list of conditions and the following
 *      disclaimer in the documentation and/or other materials provided
 *      with the distribution.
 *    - Neither the name of the copyright holder(s) nor the names of its
 *      contributors may be used to endorse or promote products derived
 *      from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
 * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
 * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 *
 */


#include <stdlib.h>
/* include it first as if it was <sys/types.h> - this will avoid errors */
#include "compat/pmk_sys_types.h"
#include <sys/utsname.h>
#include <errno.h>

#include "compat/pmk_string.h"
#include "compat/pmk_stdio.h"
#include "common.h"
#include "detect_cpu.h"
#include "detect_cpu_asm.h"


/***************
 * common code *
 ***********************************************************************/


/* config tools data file keyword */
prskw	kw_pmkcpu[] = {
	{"LIST_ARCH_EQUIV",			LIST_ARCH_EQUIV,		PRS_KW_CELL,	PRS_TOK_NULL,	NULL},
	{"LIST_X86_CPU_VENDOR",		LIST_X86_CPU_VENDOR,	PRS_KW_CELL,	PRS_TOK_NULL,	NULL},
	{"LIST_X86_CPU_MODEL",		LIST_X86_CPU_MODEL,		PRS_KW_CELL,	PRS_TOK_NULL,	NULL},
	{"LIST_X86_CPU_CLASS",		LIST_X86_CPU_CLASS,		PRS_KW_CELL,	PRS_TOK_NULL,	NULL},
	{"LIST_ALPHA_CPU_CLASS",	LIST_ALPHA_CPU_CLASS,	PRS_KW_CELL,	PRS_TOK_NULL,	NULL},
	{"LIST_IA64_CPU_CLASS",		LIST_IA64_CPU_CLASS,	PRS_KW_CELL,	PRS_TOK_NULL,	NULL}
};
size_t	nbkwc = sizeof(kw_pmkcpu) / sizeof(prskw);


arch_cell	arch_tab[] = {
	{"x86_32",		PMK_ARCH_X86_32},
	{"x86_64",		PMK_ARCH_X86_64},
	{"sparc32",		PMK_ARCH_SPARC},
	{"sparc64",		PMK_ARCH_SPARC64},
	{"ia_64",		PMK_ARCH_IA_64},
	{"ppc",			PMK_ARCH_PPC},
	{"alpha",		PMK_ARCH_ALPHA},
	{"m68k",		PMK_ARCH_M68K},
	{"parisc_64",	PMK_ARCH_PARISC},
	{"vax",			PMK_ARCH_VAX}
};
size_t	nbarch = sizeof(arch_tab) / sizeof(arch_cell);


/**************
 * seek_key() *
 ***********************************************************************
 DESCR
	seek parse data key

 IN
	pdata : parse data structure
	token : key token to find

 OUT
	key data or NULL
 ***********************************************************************/

void *seek_key(prsdata *pdata, int token) {
	prscell		*pcell;

	pcell = pdata->tree->first;
	while (pcell != NULL) {
		if (pcell->token == token) {
			return(pcell->data);
		}

		pcell = pcell->next;
	}

	return(NULL);
}


/********************
 * parse_cpu_data() *
 ***********************************************************************
 DESCR
	parse cpu data file

 IN
	fname : data file name

 OUT
	prsdata structure
 ***********************************************************************/

prsdata *parse_cpu_data(char *fname) {
	FILE		*fp;
	bool		 rval;
	prsdata		*pdata;

	/* initialize parsing structure */
	pdata = prsdata_init();
	if (pdata == NULL) {
		errorf("cannot intialize parsing data structure.");
		return(NULL);
	}

	fp = fopen(fname, "r");
	if (fp == NULL) {
		prsdata_destroy(pdata);
		errorf("cannot open '%s' : %s.",
				fname, strerror(errno));
		return(NULL);
	}

	/* parse data file and fill prsdata strucutre */
	rval = parse_pmkfile(fp, pdata, kw_pmkcpu, nbkwc);

	fclose(fp);

	if (rval == false) {
		/* parsing failed, pdata is useless, cleaning */
		prsdata_destroy(pdata);
		return(NULL);
	}

	return(pdata);
}


/********************
 * check_cpu_arch() *
 ***********************************************************************
 DESCR
	check the cpu architecture

 IN
	uname_m : uname machine string

 OUT
	cpu architecture string
 ***********************************************************************/

char *check_cpu_arch(char *uname_m, prsdata *pdata) {
	char		*pstr = NULL;
	htable		*pht;
	void		*ptmp;

	pht = (htable *) seek_key(pdata, LIST_ARCH_EQUIV);
	if (pht != NULL) {
		ptmp = hash_get(pht, uname_m); /* no check needed */
		if (ptmp != NULL)
			pstr = po_get_str(ptmp); /* no check needed */
	}

	if (pstr == NULL)
		pstr = PMK_ARCH_STR_UNKNOWN;

	return(strdup(pstr)); /* no check needed, NULL will be automatically returned */
}


/*********************
 * arch_name_to_id() *
 ***********************************************************************
 DESCR
	convert arch name to id

 IN
	arch_name : architecture string

 OUT
	architecture identifier
 ***********************************************************************/

unsigned char arch_name_to_id(char *arch_name) {
	unsigned int	i;
	unsigned char	id = PMK_ARCH_UNKNOWN;

	for(i = 0 ; i < nbarch ; i++) {
		/* compare arch name with record key */
		if (strncmp(arch_name, arch_tab[i].name, strlen(arch_name)) == 0) {
			/* they are same, get the id */
			id = arch_tab[i].id;
			break;
		}
	}

	return(id);
}


/******************
 * arch_wrapper() *
 ***********************************************************************
 DESCR
	architecture wrapper

 IN
	pdata : parsing data structure
	arch_name : architecture name

 OUT
	hash table with values or NULL
 ***********************************************************************/

htable *arch_wrapper(prsdata *pdata, char *arch_name) {
	htable			*pht;
	unsigned char	 arch_id;

#if defined(ARCH_X86_32) || defined(ARCH_X86_64)
	x86_cpu_cell	*pcell;
#endif /* ARCH_X86_32 || ARCH_X86_64 */

	arch_id = arch_name_to_id(arch_name);

	pht = hash_init(ARCH_NB_MAX);
	if (pht == NULL) {
		printf("DEBUG pht init failed !\n");
		return(NULL);
	}

	switch (arch_id) {
		case PMK_ARCH_X86_32 :
		case PMK_ARCH_X86_64 :
#if defined(ARCH_X86_32) || defined(ARCH_X86_64)
			pcell = x86_cpu_cell_init();
			if (pcell == NULL)
				return(NULL);

			if (x86_get_cpuid_data(pcell) == false) {
				errorf("unable to get x86 cpuid data.");
				return(NULL);
			}

			pcell->stdvendor = x86_get_std_cpu_vendor(pdata, pcell->vendor);
			if (x86_set_cpu_data(pdata, pcell, pht) == false) {
				errorf("failed to record cpu data.");
				return(NULL);
			}

			x86_cpu_cell_destroy(pcell);
#else /* ARCH_X86_32 || ARCH_X86_64 */
			errorf("architecture mismatch for '%s'.", arch_name);
			return(NULL);
#endif /* ARCH_X86_32 || ARCH_X86_64 */
			break;

		case PMK_ARCH_ALPHA :
#if defined(ARCH_ALPHA)
			if (alpha_set_cpu_data(pdata, pht) == false) {
				errorf("failed to record cpu data.");
				return(NULL);
			}
#else /* ARCH_ALPHA */
			errorf("architecture mismatch for '%s'.", arch_name);
			return(NULL);
#endif /* ARCH_ALPHA */
			break;

		case PMK_ARCH_IA_64 :
#if defined(ARCH_IA64)
			if (ia64_get_cpuid_data(pdata, pht) == false) {
				errorf("failed to record cpu data.");
				return(NULL);
			}
#else /* ARCH_IA64 */
			errorf("architecture mismatch for '%s'.", arch_name);
			return(NULL);
#endif /* ARCH_IA64 */

			break;
	}

	return(pht);
}


/****************
 * x86 specific *
 ***********************************************************************/

#if defined(ARCH_X86_32) || defined(ARCH_X86_64)

x86_cpu_feature	x86_cpu_feat_reg1[] = {
	{X86_CPU_MASK_FEAT_FPU,		"FPU"},
	{X86_CPU_MASK_FEAT_VME,		"VME"},
	{X86_CPU_MASK_FEAT_DE,		"DE"},
	{X86_CPU_MASK_FEAT_PSE,		"PSE"},
	{X86_CPU_MASK_FEAT_TSC,		"TSC"},
	{X86_CPU_MASK_FEAT_MSR,		"MSR"},
	{X86_CPU_MASK_FEAT_PAE,		"PAE"},
	{X86_CPU_MASK_FEAT_MCE,		"MCE"},
	{X86_CPU_MASK_FEAT_CX8,		"CX8"},
	{X86_CPU_MASK_FEAT_APIC,	"APIC"},
	{X86_CPU_MASK_FEAT_SEP,		"SEP"},
	{X86_CPU_MASK_FEAT_MTRR,	"MTRR"},
	{X86_CPU_MASK_FEAT_PGE,		"PGE"},
	{X86_CPU_MASK_FEAT_MCA,		"MCA"},
	{X86_CPU_MASK_FEAT_CMOV,	"CMOV"},
	{X86_CPU_MASK_FEAT_PAT,		"PAT"},
	{X86_CPU_MASK_FEAT_PSE36,	"PSE36"},
	{X86_CPU_MASK_FEAT_PSN,		"PSN"},
	{X86_CPU_MASK_FEAT_CLFL,	"CLFL"},
	{X86_CPU_MASK_FEAT_DTES,	"DTES"},
	{X86_CPU_MASK_FEAT_ACPI,	"ACPI"},
	{X86_CPU_MASK_FEAT_MMX,		"MMX"},
	{X86_CPU_MASK_FEAT_FXR,		"FXR"},
	{X86_CPU_MASK_FEAT_SSE,		"SSE"},
	{X86_CPU_MASK_FEAT_SSE2,	"SSE2"},
	{X86_CPU_MASK_FEAT_SS,		"SS"},
	{X86_CPU_MASK_FEAT_HTT,		"HTT"},
	{X86_CPU_MASK_FEAT_TM1,		"TM1"},
	{X86_CPU_MASK_FEAT_IA64,	"IA64"},
	{X86_CPU_MASK_FEAT_PBE,		"PBE"}
};
size_t nb_feat_reg1 = sizeof(x86_cpu_feat_reg1) / sizeof(x86_cpu_feature);

x86_cpu_feature	x86_cpu_feat_reg2[] = {
	{X86_CPU_MASK_FEAT_FPU,		"FPU"},
	{X86_CPU_MASK_FEAT_MON,		"MON"},
	{X86_CPU_MASK_FEAT_DSCPL,	"DSCPL"},
	{X86_CPU_MASK_FEAT_EST,		"EST"},
	{X86_CPU_MASK_FEAT_TM2,		"TM2"},
	{X86_CPU_MASK_FEAT_CID,		"CID"},
	{X86_CPU_MASK_FEAT_CX16,	"CX16"},
	{X86_CPU_MASK_FEAT_ETPRD,	"ETPRD"}
};
size_t nb_feat_reg2 = sizeof(x86_cpu_feat_reg2) / sizeof(x86_cpu_feature);

x86_cpu_feature	x86_cpu_feat_extreg[] = {
	{X86_CPU_MASK_FEAT_LM,		"LM"},
	{X86_CPU_MASK_FEAT_EXT3DN,	"EXT3DN"},
	{X86_CPU_MASK_FEAT_3DNOW,	"3DNOW"}
};
size_t nb_feat_extreg = sizeof(x86_cpu_feat_extreg) / sizeof(x86_cpu_feature);


/****
 functions
****/

/***********************
 * x86_cpu_cell_init() *
 ***********************************************************************
 DESCR
	initialise x86 cpu cell

 IN
	NONE

 OUT
	cpu cell or NULL
 ***********************************************************************/

x86_cpu_cell *x86_cpu_cell_init(void) {
	x86_cpu_cell	*pcell;

	pcell = (x86_cpu_cell *) malloc(sizeof(x86_cpu_cell));
	if (pcell == NULL)
		return(NULL);

	pcell->vendor = NULL;
	pcell->stdvendor = NULL;
	pcell->cpuname = NULL;
	pcell->features = NULL;
	pcell->family = 0;
	pcell->model = 0;
	pcell->extfam = 0;
	pcell->extmod = 0;

	return(pcell);
}


/**************************
 * x86_cpu_cell_destroy() *
 ***********************************************************************
 DESCR
	free x86 cpu cell structure

 IN
	pcell :	structure to free

 OUT
	NONE
 ***********************************************************************/

void x86_cpu_cell_destroy(x86_cpu_cell *pcell) {
	if (pcell->vendor != NULL)
		free(pcell->vendor);
	if (pcell->stdvendor != NULL)
		free(pcell->stdvendor);
	if (pcell->cpuname != NULL)
		free(pcell->cpuname);
	free(pcell);
}


/****************************
 * x86_get_std_cpu_vendor() *
 ***********************************************************************
 DESCR
	get (pmk) vendor identifier based on data from cpuid

 IN
	pdata :	parsing structure

 OUT
	cpu vendor string
 ***********************************************************************/

char *x86_get_std_cpu_vendor(prsdata *pdata, char *civendor) {
	char		*vendor = NULL;
	htable		*pht;
	prscell		*pcell;

	/* look for vendor list */
	pcell = pdata->tree->first;
	while ((pcell != NULL) && (vendor == NULL)) {
		if (pcell->token == LIST_X86_CPU_VENDOR) {
			/* got list ! */
			pht = pcell->data;
			/* look for standard vendor name */
			vendor = po_get_str(hash_get(pht, civendor));
		}
		pcell = pcell->next;
	}

	/* in case we don't find anything */
	if (vendor == NULL)
		vendor = PMK_ARCH_STR_UNKNOWN;

	return(strdup(vendor)); /* no check needed, NULL will be automatically returned */
}


/************************
 * x86_get_cpuid_data() *
 ***********************************************************************
 DESCR
	gather x86 cpuid data

 IN
	cell :	x86 cpu cell

 OUT
	boolean
 ***********************************************************************/

bool x86_get_cpuid_data(x86_cpu_cell *cell) {
	char			 feat_str[TMP_BUF_LEN] = "";
	unsigned int	 i;
	uint32_t		 buffer[13],
					 extlevel;

	if (x86_check_cpuid_flag() == 0) {
		/* no cpuid flag => 386 or old 486 */
		cell->cpuid = false;
		cell->family = 3; /* fake family */
		return(true);
	}

	cell->cpuid = true;

	/* get the cpuid vendor */
	x86_exec_cpuid(0);

	cell->level = x86_cpu_reg_eax;
	buffer[0] = x86_cpu_reg_ebx;
	buffer[1] = x86_cpu_reg_edx;
	buffer[2] = x86_cpu_reg_ecx;
	buffer[3] = 0;	/* terminate string */

	cell->vendor = strdup((char *) buffer);
	if (cell->vendor == NULL) {
		return(false);
	}

	/* get the cpu type */
	x86_exec_cpuid(1);

	cell->family = (unsigned int) ((x86_cpu_reg_eax & X86_CPU_MASK_FAMILY) >> 8);
	if (cell->family == 15) {
		/* extended family */
		cell->extfam = (unsigned int) ((x86_cpu_reg_eax & X86_CPU_MASK_EXTFAM) >> 20);
	}

	cell->model = (unsigned int) ((x86_cpu_reg_eax & X86_CPU_MASK_MODEL) >> 4);

	/* processing feature register 1 */
	for (i = 0 ; i < nb_feat_reg1 ; i++) {
		if ((x86_cpu_reg_edx & x86_cpu_feat_reg1[i].mask) != 0) {
			strlcat(feat_str, x86_cpu_feat_reg1[i].descr, sizeof(feat_str)); /* no check */
			if (strlcat_b(feat_str, " ", sizeof(feat_str)) == false) {
				return(false);
			}
		}
	}
	/* processing feature register 2 */
	for (i = 0 ; i < nb_feat_reg2 ; i++) {
		if ((x86_cpu_reg_edx & x86_cpu_feat_reg2[i].mask) != 0) {
			strlcat(feat_str, x86_cpu_feat_reg2[i].descr, sizeof(feat_str)); /* no check */
			if (strlcat_b(feat_str, " ", sizeof(feat_str)) == false) {
				return(false);
			}
		}
	}

	/* check extended cpu level */
	x86_exec_cpuid(0x80000000);
	extlevel = x86_cpu_reg_eax;

	if (extlevel >= 0x80000001) {
		/* get extended cpu features */
		x86_exec_cpuid(0x80000001);

		/* processing extended feature register */
		for (i = 0 ; i < nb_feat_extreg ; i++) {
			if ((x86_cpu_reg_edx & x86_cpu_feat_extreg[i].mask) != 0) {
				strlcat(feat_str, x86_cpu_feat_extreg[i].descr, sizeof(feat_str)); /* no check */
				if (strlcat_b(feat_str, " ", sizeof(feat_str)) == false) {
					return(false);
				}
			}
		}
	}

	/* save feature string */
	cell->features = strdup(feat_str);

	if (extlevel >= 0x80000002) {
		/* get the cpu name */
		x86_exec_cpuid(0x80000002);
		buffer[0] = x86_cpu_reg_eax;
		buffer[1] = x86_cpu_reg_ebx;
		buffer[2] = x86_cpu_reg_ecx;
		buffer[3] = x86_cpu_reg_edx;

		x86_exec_cpuid(0x80000003);
		buffer[4] = x86_cpu_reg_eax;
		buffer[5] = x86_cpu_reg_ebx;
		buffer[6] = x86_cpu_reg_ecx;
		buffer[7] = x86_cpu_reg_edx;

		x86_exec_cpuid(0x80000004);
		buffer[8] = x86_cpu_reg_eax;
		buffer[9] = x86_cpu_reg_ebx;
		buffer[10] = x86_cpu_reg_ecx;
		buffer[11] = x86_cpu_reg_edx;

		buffer[12] = 0;	/* terminate string */
		cell->cpuname = strdup((char *) buffer);
		if (cell->cpuname == NULL) {
			return(false);
		}
	} else {
		cell->cpuname = NULL;
	}

	return(true);
}


/**********************
 * x86_set_cpu_data() *
 ***********************************************************************
 DESCR
	record cpu data in hash table

 IN
	pdata :	parsing data
	pcell :	x86 cpu data cell
	pht :	storage hash table

 OUT
	boolean
 ***********************************************************************/

bool x86_set_cpu_data(prsdata *pdata, x86_cpu_cell *pcell, htable *pht) {
	char	 buffer[TMP_BUF_LEN],
			*pstr;
	htable	*phtbis;

	if (snprintf_b(buffer, sizeof(buffer), "%u", pcell->family) == false)
		return(false);

	if (hash_update_dup(pht, PMKCONF_HW_X86_CPU_FAMILY, buffer) == HASH_ADD_FAIL) {
		return(false);
	}

	if (snprintf_b(buffer, sizeof(buffer), "%u", pcell->model) == false)
		return(false);

	if (hash_update_dup(pht, PMKCONF_HW_X86_CPU_MODEL, buffer) == HASH_ADD_FAIL) {
		return(false);
	}

	if (snprintf_b(buffer, sizeof(buffer), "%u", pcell->extfam) == false)
		return(false);

	if (hash_update_dup(pht, PMKCONF_HW_X86_CPU_EXTFAM, buffer) == HASH_ADD_FAIL) {
		return(false);
	}

	if (snprintf_b(buffer, sizeof(buffer), "%u", pcell->extmod) == false)
		return(false);

	if (hash_update_dup(pht, PMKCONF_HW_X86_CPU_EXTMOD, buffer) == HASH_ADD_FAIL) {
		return(false);
	}

	if (pcell->cpuname != NULL) {
		if (hash_update_dup(pht, PMKCONF_HW_X86_CPU_NAME,
				pcell->cpuname) == HASH_ADD_FAIL) {
			return(false);
		}
	} /* else put unknown ? */

	if (pcell->vendor != NULL) {
		if (hash_update_dup(pht, PMKCONF_HW_X86_CPU_VENDOR,
				pcell->vendor) == HASH_ADD_FAIL) {
			return(false);
		}
	} /* else put unknown ? */

	if (pcell->stdvendor != NULL) {
		if (hash_update_dup(pht, PMKCONF_HW_X86_CPU_STD_VENDOR,
				pcell->stdvendor) == HASH_ADD_FAIL) {
			return(false);
		}
	} /* else put unknown ? */

	if (pcell->features != NULL) {
		if (hash_update_dup(pht, PMKCONF_HW_X86_CPU_FEATURES,
				pcell->features) == HASH_ADD_FAIL) {
			return(false);
		}
	} /* else put empty string ? */

	phtbis = (htable *) seek_key(pdata, LIST_X86_CPU_CLASS);
	if (phtbis != NULL) {
		if (pcell->family < 15) {
			snprintf_b(buffer, sizeof(buffer), /* no check needed */
					X86_CPU_CLASS_FAMILY_FMT,
					pcell->stdvendor, pcell->family);
			pstr = po_get_str(hash_get(phtbis, buffer)); /* no check needed */
		} else {
			snprintf_b(buffer, sizeof(buffer), /* no check needed */
					X86_CPU_CLASS_EXTFAM_FMT,
					pcell->stdvendor, pcell->extfam);
			pstr = po_get_str(hash_get(phtbis, buffer)); /* no check needed */
		}

		if (pstr == NULL) {
			/* not found, get default */
			pstr = po_get_str(hash_get(phtbis, "DEFAULT"));
			if (pstr == NULL) {
				errorf("failed to get default value for cpu family (%s).", buffer);
				return(false);
			}
		}

		if (hash_update_dup(pht, PMKCONF_HW_X86_CPU_CLASS,
						pstr) == HASH_ADD_FAIL) {
			return(false);
		}
	} else {
		errorf("failed to find '%s' key\n", LIST_X86_CPU_CLASS);
		return(false);
	}

	return(true);
}

#endif /* ARCH_X86_32 || ARCH_X86_64 */


/******************
 * alpha specific *
 ***********************************************************************/

#if defined(ARCH_ALPHA)

alpha_cpu_feature	alpha_cpu_feat[] = {
	{ALPHA_CPU_MASK_FEAT_BWX,	"BWX"},
	{ALPHA_CPU_MASK_FEAT_FIX,	"FIX"},
	{ALPHA_CPU_MASK_FEAT_CIX,	"CIX"},
	{ALPHA_CPU_MASK_FEAT_MVI,	"MVI"},
	{ALPHA_CPU_MASK_FEAT_PAT,	"PAT"},
	{ALPHA_CPU_MASK_FEAT_PMI,	"PMI"},
};
int nb_feat = sizeof(alpha_cpu_feat) / sizeof(alpha_cpu_feature);


/************************
 * alpha_set_cpu_data() *
 ***********************************************************************
 DESCR
	set alpha cpu data

 IN
	pdata:	parsing data structure
	pht:	data storage hash table

 OUT
	boolean
 ***********************************************************************/

bool alpha_set_cpu_data(prsdata *pdata, htable *pht) {
	char			 buffer[16],
					 feat_str[TMP_BUF_LEN] = "",
					*pstr;
	htable			*phtbis;
	unsigned int	 i;
	uint64_t		 implver,
					 amask;

/*debugf("alpha_set_cpu_data() : end");*/
	phtbis = (htable *) seek_key(pdata, LIST_ALPHA_CPU_CLASS);
	if (phtbis != NULL) {
		/* get cpu class */
		implver = alpha_exec_implver();
/*debugf("alpha_set_cpu_data() : implver = '%u'", implver);*/

		if (snprintf_b(buffer, sizeof(buffer), ALPHA_CPU_CLASS_FMT, implver) == false) {
			return(false);
		}
/*debugf("alpha_set_cpu_data() : buffer = '%s'", buffer);*/
		pstr = po_get_str(hash_get(phtbis, buffer));
		if (pstr == NULL) {
			pstr = ALPHA_CPU_UNKNOWN;
		}

		if (hash_update_dup(pht, PMKCONF_HW_ALPHA_CPU_CLASS, pstr) == HASH_ADD_FAIL) {
			return(false);
		}

		/* get cpu features */
		amask = alpha_exec_amask();

		/* processing feature mask */
		for (i = 0 ; i < nb_feat ; i++) {
			if ((amask & alpha_cpu_feat[i].mask) != 0) {
				strlcat(feat_str, alpha_cpu_feat[i].descr, sizeof(feat_str)); /* no check */
				if (strlcat_b(feat_str, " ", sizeof(feat_str)) == false) {
					return(false);
				}
			}
		}

		/* save feature string */
		if (hash_update_dup(pht, PMKCONF_HW_ALPHA_CPU_FEATURES, feat_str) == HASH_ADD_FAIL) {
			return(false);
		}

/*debugf("alpha_set_cpu_data() : amask = %x", amask);*/
	}


/*debugf("alpha_set_cpu_data() : end");*/
	return(true);
}

#endif /* ARCH_ALPHA */


/*****************
 * ia64 specific *
 ***********************************************************************/

#if defined(ARCH_IA64)

ia64_cpu_feature ia64_cpu_feat[] = {
	{IA64_CPU_MASK_FEAT_LB,	"LB"},
	{IA64_CPU_MASK_FEAT_SD,	"SD"},
	{IA64_CPU_MASK_FEAT_AO,	"AO"}
};
size_t nb_feat = sizeof(ia64_cpu_feat) / sizeof(ia64_cpu_feature);


/****
 functions
****/

/*************************
 * ia64_get_cpuid_data() *
 ***********************************************************************
 DESCR
	gather and save ia64 cpu data

 IN
	pdata:	parsing data structure
	pht:	storage hash table

 OUT
	boolean
 ***********************************************************************/

bool ia64_get_cpuid_data(prsdata *pdata, htable *pht) {
	char			 buffer[TMP_BUF_LEN],
					*pstr;
	htable			*phtbis;
	uint64_t		 regbuf[3],
					 level,
					 rslt;
	unsigned int	 i;

	regbuf[0] = ia64_get_cpuid_register(0);
	regbuf[1] = ia64_get_cpuid_register(1);
	regbuf[2] = 0;
	if (hash_update_dup(pht, PMKCONF_HW_IA64_CPU_VENDOR, regbuf) == HASH_ADD_FAIL)
		return(false);
/*debugf("cpuid register 0 = %x", regbuf[0]);*/
/*debugf("cpuid register 1 = %x", regbuf[1]);*/
/*debugf("vendor = '%s'", regbuf);           */

	regbuf[0] = ia64_get_cpuid_register(3);
/*debugf("cpuid register 3 = %x", regbuf[0]);*/

	level = regbuf[0] & IA64_CPU_MASK_LEVEL;
/*debugf("level = %x", level);*/

	rslt = (regbuf[0] & IA64_CPU_MASK_REVISION) >> 8;
	if (snprintf_b(buffer, sizeof(buffer), "%u", rslt) == false)
		return(false);
	if (hash_update_dup(pht, PMKCONF_HW_IA64_CPU_REVISION, buffer) == HASH_ADD_FAIL)
		return(false);
/*debugf("revision = %x", rslt);*/

	rslt = (regbuf[0] & IA64_CPU_MASK_MODEL) >> 16;
	if (snprintf_b(buffer, sizeof(buffer), "%u", rslt) == false)
		return(false);
	if (hash_update_dup(pht, PMKCONF_HW_IA64_CPU_MODEL, buffer) == HASH_ADD_FAIL)
		return(false);
/*debugf("model = %x", rslt);*/

	rslt = (regbuf[0] & IA64_CPU_MASK_FAMILY) >> 24;
	if (snprintf_b(buffer, sizeof(buffer), "%u", rslt) == false)
		return(false);
	if (hash_update_dup(pht, PMKCONF_HW_IA64_CPU_FAMILY, buffer) == HASH_ADD_FAIL)
		return(false);
/*debugf("family = %x", rslt);*/

	phtbis = (htable *) seek_key(pdata, LIST_IA64_CPU_CLASS);
	if (phtbis != NULL) {
		snprintf_b(buffer, sizeof(buffer), /* no check needed */
				IA64_CPU_CLASS_FAMILY_FMT, rslt);
/*debugf("buffer = '%s'", buffer);*/

		pstr = po_get_str(hash_get(phtbis, buffer)); /* no check needed */
		if (pstr == NULL) {
/*debugf("getting default");*/
			/* not found, get default */
			pstr = po_get_str(hash_get(phtbis, "DEFAULT"));
			if (pstr == NULL) {
				errorf("failed to get default value for cpu class family.");
				return(false);
			}
		}
/*debugf("pstr = '%s'", pstr);*/

		if (hash_update_dup(pht, PMKCONF_HW_IA64_CPU_CLASS,
						pstr) == HASH_ADD_FAIL) {
			/* err msg ? */
			return(false);
		}
	} else {
		errorf("failed to find '%s' key\n", LIST_IA64_CPU_CLASS);
		return(false);
	}


	rslt = (regbuf[0] & IA64_CPU_MASK_ARCHREV) >> 32;
	if (snprintf_b(buffer, sizeof(buffer), "%u", rslt) == false)
		return(false);
	if (hash_update_dup(pht, PMKCONF_HW_IA64_CPU_ARCHREV, buffer) == HASH_ADD_FAIL)
		return(false);
	/*printf("archrev = %x\n", rslt);*/

	if (level >= 0x04) {
		/* getting feature register */
		rslt = ia64_get_cpuid_register(4);
		/*printf("cpuid register 4 = %x\n", regbuf[0]);*/

		/* processing feature mask */
		buffer[0] = CHAR_EOS;
		for (i = 0 ; i < nb_feat ; i++) {
			if ((rslt & ia64_cpu_feat[i].mask) != 0) {
				strlcat(buffer, ia64_cpu_feat[i].descr,
						sizeof(buffer)); /* no check */
				if (strlcat_b(buffer, " ", sizeof(buffer)) == false)
					return(false); /* err msg ? */
			}
		}
		if (hash_update_dup(pht, PMKCONF_HW_IA64_CPU_FEATURES,
					buffer) == HASH_ADD_FAIL)
			/* err msg ? */
			return(false);
	}


	return(true);
}

#endif /* ARCH_IA64 */



syntax highlighted by Code2HTML, v. 0.9.1