/* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright 2007 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ #pragma ident "%Z%%M% %I% %E% SMI" /* LINTLIBRARY */ /* * String conversion routine for hardware capabilities types. */ #include <strings.h> #include <stdio.h> #include <ctype.h> #include <limits.h> #include <sys/machelf.h> #include <sys/elf.h> #include <sys/auxv_SPARC.h> #include <sys/auxv_386.h> #include <elfcap.h> /* * Define separators for val2str processing. */ static const Fmt_desc format[] = { {" ", 1 }, {" ", 2 }, {" | ", 3 } }; /* * Define all known capabilities as both lower and upper case strings. This * duplication is necessary, rather than have one string and use something * like toupper(), as a client such as ld.so.1 doesn't need the overhead of * dragging in the internationalization support of toupper(). The Intel 3DNow * flags are a slightly odd convention too. * * Define all known software capabilities. */ #ifdef CAP_UPPERCASE static const char Sf1_fpknwn[] = "FPKNWN"; static const char Sf1_fpused[] = "FPUSED"; #elif CAP_LOWERCASE static const char Sf1_fpknwn[] = "fpknwn"; static const char Sf1_fpused[] = "fpused"; #else #error "Software Capabilities - what case do you want?" #endif /* * Order the software capabilities to match their numeric value. See SF1_SUNW_ * values in sys/elf.h. */ static const Cap_desc sf1[] = { { SF1_SUNW_FPKNWN, Sf1_fpknwn, (sizeof (Sf1_fpknwn) - 1) }, { SF1_SUNW_FPUSED, Sf1_fpused, (sizeof (Sf1_fpused) - 1) } }; static const uint_t sf1_num = sizeof (sf1) / sizeof (Cap_desc); /* * Define all known SPARC hardware capabilities. */ #ifdef CAP_UPPERCASE static const char Hw1_s_mul32[] = "MUL32"; static const char Hw1_s_div32[] = "DIV32"; static const char Hw1_s_fsmuld[] = "FSMULD"; static const char Hw1_s_v8plus[] = "V8PLUS"; static const char Hw1_s_popc[] = "POPC"; static const char Hw1_s_vis[] = "VIS"; static const char Hw1_s_vis2[] = "VIS2"; static const char Hw1_s_asi_blk_init[] = "ASI_BLK_INIT"; static const char Hw1_s_fmaf[] = "FMAF"; #elif CAP_LOWERCASE static const char Hw1_s_mul32[] = "mul32"; static const char Hw1_s_div32[] = "div32"; static const char Hw1_s_fsmuld[] = "fsmuld"; static const char Hw1_s_v8plus[] = "v8plus"; static const char Hw1_s_popc[] = "popc"; static const char Hw1_s_vis[] = "vis"; static const char Hw1_s_vis2[] = "vis2"; static const char Hw1_s_asi_blk_init[] = "asi_blk_init"; static const char Hw1_s_fmaf[] = "fmaf"; #else #error "Hardware Capabilities (sparc) - what case do you want?" #endif /* * Order the SPARC hardware capabilities to match their numeric value. See * AV_SPARC_ values in sys/auxv_SPARC.h. */ static const Cap_desc hw1_s[] = { { AV_SPARC_MUL32, Hw1_s_mul32, sizeof (Hw1_s_mul32) - 1 }, { AV_SPARC_DIV32, Hw1_s_div32, sizeof (Hw1_s_div32) - 1 }, { AV_SPARC_FSMULD, Hw1_s_fsmuld, sizeof (Hw1_s_fsmuld) - 1 }, { AV_SPARC_V8PLUS, Hw1_s_v8plus, sizeof (Hw1_s_v8plus) - 1 }, { AV_SPARC_POPC, Hw1_s_popc, sizeof (Hw1_s_popc) - 1 }, { AV_SPARC_VIS, Hw1_s_vis, sizeof (Hw1_s_vis) - 1 }, { AV_SPARC_VIS2, Hw1_s_vis2, sizeof (Hw1_s_vis2) - 1 }, { AV_SPARC_ASI_BLK_INIT, Hw1_s_asi_blk_init, sizeof (Hw1_s_asi_blk_init) - 1 }, { AV_SPARC_FMAF, Hw1_s_fmaf, sizeof (Hw1_s_fmaf) - 1 } }; static const uint_t hw1_s_num = sizeof (hw1_s) / sizeof (Cap_desc); /* * Define all known Intel hardware capabilities. */ #ifdef CAP_UPPERCASE static const char Hw1_i_fpu[] = "FPU"; static const char Hw1_i_tsc[] = "TSC"; static const char Hw1_i_cx8[] = "CX8"; static const char Hw1_i_sep[] = "SEP"; static const char Hw1_i_amd_sysc[] = "AMD_SYSC"; static const char Hw1_i_cmov[] = "CMOV"; static const char Hw1_i_mmx[] = "MMX"; static const char Hw1_i_amd_mmx[] = "AMD_MMX"; static const char Hw1_i_amd_3dnow[] = "AMD_3DNow"; static const char Hw1_i_amd_3dnowx[] = "AMD_3DNowx"; static const char Hw1_i_fxsr[] = "FXSR"; static const char Hw1_i_sse[] = "SSE"; static const char Hw1_i_sse2[] = "SSE2"; static const char Hw1_i_pause[] = "PAUSE"; static const char Hw1_i_sse3[] = "SSE3"; static const char Hw1_i_mon[] = "MON"; static const char Hw1_i_cx16[] = "CX16"; static const char Hw1_i_ahf[] = "AHF"; static const char Hw1_i_tscp[] = "TSCP"; #elif CAP_LOWERCASE static const char Hw1_i_fpu[] = "fpu"; static const char Hw1_i_tsc[] = "tsc"; static const char Hw1_i_cx8[] = "cx8"; static const char Hw1_i_sep[] = "sep"; static const char Hw1_i_amd_sysc[] = "amd_sysc"; static const char Hw1_i_cmov[] = "cmov"; static const char Hw1_i_mmx[] = "mmx"; static const char Hw1_i_amd_mmx[] = "amd_mmx"; static const char Hw1_i_amd_3dnow[] = "amd_3dnow"; static const char Hw1_i_amd_3dnowx[] = "amd_3dnowx"; static const char Hw1_i_fxsr[] = "fxsr"; static const char Hw1_i_sse[] = "sse"; static const char Hw1_i_sse2[] = "sse2"; static const char Hw1_i_pause[] = "pause"; static const char Hw1_i_sse3[] = "sse3"; static const char Hw1_i_mon[] = "mon"; static const char Hw1_i_cx16[] = "cx16"; static const char Hw1_i_ahf[] = "ahf"; static const char Hw1_i_tscp[] = "tscp"; #else #error "Hardware Capabilities (intel) - what case do you want?" #endif /* * Order the Intel hardware capabilities to match their numeric value. See * AV_386_ values in sys/auxv_386.h. */ static const Cap_desc hw1_i[] = { { AV_386_FPU, Hw1_i_fpu, sizeof (Hw1_i_fpu) - 1 }, { AV_386_TSC, Hw1_i_tsc, sizeof (Hw1_i_tsc) - 1 }, { AV_386_CX8, Hw1_i_cx8, sizeof (Hw1_i_cx8) - 1 }, { AV_386_SEP, Hw1_i_sep, sizeof (Hw1_i_sep) - 1 }, { AV_386_AMD_SYSC, Hw1_i_amd_sysc, sizeof (Hw1_i_amd_sysc) - 1 }, { AV_386_CMOV, Hw1_i_cmov, sizeof (Hw1_i_cmov) - 1 }, { AV_386_MMX, Hw1_i_mmx, sizeof (Hw1_i_mmx) - 1 }, { AV_386_AMD_MMX, Hw1_i_amd_mmx, sizeof (Hw1_i_amd_mmx) - 1 }, { AV_386_AMD_3DNow, Hw1_i_amd_3dnow, sizeof (Hw1_i_amd_3dnow) - 1 }, { AV_386_AMD_3DNowx, Hw1_i_amd_3dnowx, sizeof (Hw1_i_amd_3dnowx) - 1 }, { AV_386_FXSR, Hw1_i_fxsr, sizeof (Hw1_i_fxsr) - 1 }, { AV_386_SSE, Hw1_i_sse, sizeof (Hw1_i_sse) - 1 }, { AV_386_SSE2, Hw1_i_sse2, sizeof (Hw1_i_sse2) - 1 }, { AV_386_PAUSE, Hw1_i_pause, sizeof (Hw1_i_pause) - 1 }, { AV_386_SSE3, Hw1_i_sse3, sizeof (Hw1_i_sse3) - 1 }, { AV_386_MON, Hw1_i_mon, sizeof (Hw1_i_mon) - 1 }, { AV_386_CX16, Hw1_i_cx16, sizeof (Hw1_i_cx16) - 1 }, { AV_386_AHF, Hw1_i_ahf, sizeof (Hw1_i_ahf) - 1 }, { AV_386_TSCP, Hw1_i_tscp, sizeof (Hw1_i_tscp) - 1 } }; static const uint_t hw1_i_num = sizeof (hw1_i) / sizeof (Cap_desc); /* * Concatenate a token to the string buffer. This can be a capabilities token * or a separator token. */ static int token(char **ostr, size_t *olen, const char *nstr, size_t nlen) { if (*olen < nlen) return (CAP_ERR_BUFOVFL); (void) strcat(*ostr, nstr); *ostr += nlen; *olen -= nlen; return (0); } /* * Expand a capabilities value into the strings defined in the associated * capabilities descriptor. */ static int expand(uint64_t val, const Cap_desc *cdp, uint_t cnum, char *str, size_t slen, int fmt) { uint_t cnt, mask; int follow = 0, err; if (val == 0) return (0); for (cnt = WORD_BIT, mask = 0x80000000; cnt; cnt--, (mask = mask >> 1)) { if ((val & mask) && (cnt <= cnum) && cdp[cnt - 1].c_val) { if (follow++ && ((err = token(&str, &slen, format[fmt].f_str, format[fmt].f_len)) != 0)) return (err); if ((err = token(&str, &slen, cdp[cnt - 1].c_str, cdp[cnt - 1].c_len)) != 0) return (err); val = val & ~mask; } } /* * If there are any unknown bits remaining display the numeric value. */ if (val) { if (follow && ((err = token(&str, &slen, format[fmt].f_str, format[fmt].f_len)) != 0)) return (err); (void) snprintf(str, slen, "0x%llx", val); } return (0); } /* * Expand a CA_SUNW_HW_1 value. */ int hwcap_1_val2str(uint64_t val, char *str, size_t len, int fmt, ushort_t mach) { /* * Initialize the string buffer, and validate the format request. */ *str = '\0'; if (fmt > CAP_MAX_TYPE) return (CAP_ERR_INVFMT); if ((mach == EM_386) || (mach == EM_IA_64) || (mach == EM_AMD64)) return (expand(val, &hw1_i[0], hw1_i_num, str, len, fmt)); if ((mach == EM_SPARC) || (mach == EM_SPARC32PLUS) || (mach == EM_SPARCV9)) return (expand(val, &hw1_s[0], hw1_s_num, str, len, fmt)); return (CAP_ERR_UNKMACH); } /* * Expand a CA_SUNW_SF_1 value. Note, that at present these capabilities are * common across all platforms. The use of "mach" is therefore redundant, but * is retained for compatibility with the interface of hwcap_1_val2str(), and * possible future expansion. */ int /* ARGSUSED4 */ sfcap_1_val2str(uint64_t val, char *str, size_t len, int fmt, ushort_t mach) { /* * Initialize the string buffer, and validate the format request. */ *str = '\0'; if (fmt > CAP_MAX_TYPE) return (CAP_ERR_INVFMT); return (expand(val, &sf1[0], sf1_num, str, len, fmt)); } /* * Determine capability type from the capability tag. */ int cap_val2str(uint64_t tag, uint64_t val, char *str, size_t len, int fmt, ushort_t mach) { if (tag == CA_SUNW_HW_1) return (hwcap_1_val2str(val, str, len, fmt, mach)); if (tag == CA_SUNW_SF_1) return (sfcap_1_val2str(val, str, len, fmt, mach)); return (CAP_ERR_UNKTAG); } /* * Determine a capabilities value from a capabilities string. */ static uint64_t value(const char *str, const Cap_desc *cdp, uint_t cnum) { uint_t num; for (num = 0; num < cnum; num++) { if (strcmp(str, cdp[num].c_str) == 0) return (cdp[num].c_val); } return (0); } uint64_t sfcap_1_str2val(const char *str, ushort_t mach) { return (value(str, &sf1[0], sf1_num)); } uint64_t hwcap_1_str2val(const char *str, ushort_t mach) { if ((mach == EM_386) || (mach == EM_IA_64) || (mach == EM_AMD64)) return (value(str, &hw1_i[0], hw1_i_num)); if ((mach == EM_SPARC) || (mach == EM_SPARC32PLUS) || (mach == EM_SPARCV9)) return (value(str, &hw1_s[0], hw1_s_num)); return (0); }