1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License (the "License"). 6 * You may not use this file except in compliance with the License. 7 * 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9 * or http://www.opensolaris.org/os/licensing. 10 * See the License for the specific language governing permissions 11 * and limitations under the License. 12 * 13 * When distributing Covered Code, include this CDDL HEADER in each 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15 * If applicable, add the following below this CDDL HEADER, with the 16 * fields enclosed by brackets "[]" replaced with your own identifying 17 * information: Portions Copyright [yyyy] [name of copyright owner] 18 * 19 * CDDL HEADER END 20 */ 21 /* 22 * Copyright 2007 Sun Microsystems, Inc. All rights reserved. 23 * Use is subject to license terms. 24 */ 25 26 /* 27 * Copyright (c) 2012, Joyent, Inc. All rights reserved. 28 * Copyright 2022 Oxide Computer Company 29 */ 30 31 #include <sys/types.h> 32 #include <sys/systeminfo.h> 33 #include <sys/utsname.h> 34 #include <sys/stat.h> 35 36 #include <sys/auxv.h> 37 #include <sys/cpuid_drv.h> 38 #include <sys/elf.h> 39 40 #include <stdio.h> 41 #include <stdlib.h> 42 #include <strings.h> 43 #include <unistd.h> 44 #include <errno.h> 45 #include <libintl.h> 46 #include <locale.h> 47 #include <fcntl.h> 48 49 #include <elfcap.h> 50 51 static const char dev_cpu_self_cpuid[] = "/dev/" CPUID_SELF_NAME; 52 static char *pgmname; 53 static int mode = 0; 54 55 #define BITS_MODE 0x1 56 #define NATIVE_MODE 0x2 57 #define KERN_MODE 0x4 58 #define VERBOSE_MODE 0x8 59 #define EXTN_MODE 0x10 60 61 static char * 62 getsysinfo(int cmd) 63 { 64 char *buf; 65 size_t bufsize = 20; /* wild guess */ 66 long ret; 67 68 if ((buf = malloc(bufsize)) == NULL) 69 return (NULL); 70 do { 71 ret = sysinfo(cmd, buf, bufsize); 72 if (ret == -1) 73 return (NULL); 74 if (ret > bufsize) { 75 bufsize = ret; 76 buf = realloc(buf, bufsize); 77 } else 78 break; 79 } while (buf != NULL); 80 81 return (buf); 82 } 83 84 /* 85 * Classify isa's as to bitness of the corresponding ABIs. 86 * isa's which have no "official" Solaris ABI are returned 87 * unrecognised i.e. "zero bit". 88 */ 89 static uint_t 90 bitness(const char *isaname) 91 { 92 if (strcmp(isaname, "sparc") == 0 || 93 strcmp(isaname, "i386") == 0) 94 return (32); 95 96 if (strcmp(isaname, "sparcv9") == 0 || 97 strcmp(isaname, "amd64") == 0) 98 return (64); 99 100 return (0); 101 } 102 103 static char * 104 report_abi(int cmd, const char *vfmt) 105 { 106 uint_t bits; 107 char *isa; 108 109 if ((isa = getsysinfo(cmd)) == NULL) 110 return (0); 111 if ((bits = bitness(isa)) == 0) { 112 (void) fprintf(stderr, 113 gettext("%s: unable to identify isa '%s'!\n"), 114 pgmname, isa); 115 exit(3); 116 } 117 118 if (mode & VERBOSE_MODE) 119 (void) printf(vfmt, bits, isa); 120 else if (mode & BITS_MODE) 121 (void) printf("%d\n", bits); 122 else if (mode & (NATIVE_MODE|KERN_MODE)) 123 (void) printf("%s\n", isa); 124 else 125 (void) printf("%s", isa); 126 return (isa); 127 } 128 129 /* 130 * Classify isas as their machine type. 131 */ 132 static ushort_t 133 machtype(const char *isaname) 134 { 135 if (strcmp(isaname, "sparc") == 0) 136 return (EM_SPARC); 137 if (strcmp(isaname, "sparcv9") == 0) 138 return (EM_SPARCV9); 139 if (strcmp(isaname, "i386") == 0) 140 return (EM_386); 141 if (strcmp(isaname, "amd64") == 0) 142 return (EM_AMD64); 143 144 return (0); 145 } 146 147 static void 148 report_hwcap(int d, const char *isa) 149 { 150 struct cpuid_get_hwcap __cgh, *cgh = &__cgh; 151 char cap1[ELFCAP_HW1_BUFSIZE]; 152 char cap2[ELFCAP_HW2_BUFSIZE]; 153 char cap3[ELFCAP_HW3_BUFSIZE]; 154 155 cgh->cgh_archname = (char *)isa; 156 if (ioctl(d, CPUID_GET_HWCAP, cgh) != 0) 157 return; 158 159 (void) elfcap_hw1_to_str(ELFCAP_STYLE_LC, cgh->cgh_hwcap[0], 160 cap1, sizeof (cap1), ELFCAP_FMT_SNGSPACE, machtype(isa)); 161 162 if (cgh->cgh_hwcap[1] != 0) { 163 (void) elfcap_hw2_to_str(ELFCAP_STYLE_LC, cgh->cgh_hwcap[1], 164 cap2, sizeof (cap2), ELFCAP_FMT_SNGSPACE, machtype(isa)); 165 } else { 166 cap2[0] = '\0'; 167 } 168 169 if (cgh->cgh_hwcap[2] != 0) { 170 (void) elfcap_hw3_to_str(ELFCAP_STYLE_LC, cgh->cgh_hwcap[2], 171 cap3, sizeof (cap3), ELFCAP_FMT_SNGSPACE, machtype(isa)); 172 } else { 173 cap3[0] = '\0'; 174 } 175 176 if (mode & EXTN_MODE) { 177 (void) printf(":"); 178 if (cgh->cgh_hwcap[2] != 0) 179 (void) printf(" %s", cap3); 180 if (cgh->cgh_hwcap[1] != 0) 181 (void) printf(" %s", cap2); 182 (void) printf(" %s", cap1); 183 (void) printf("\n"); 184 } else { 185 char *p; 186 int linecnt = 0; 187 188 for (p = strtok(cap3, " "); p; p = strtok(NULL, " ")) { 189 if (linecnt + strlen(p) > 68) { 190 (void) printf("\n"); 191 linecnt = 0; 192 } 193 if (linecnt == 0) 194 linecnt = printf("\t"); 195 linecnt += printf("%s ", p); 196 } 197 198 for (p = strtok(cap2, " "); p; p = strtok(NULL, " ")) { 199 if (linecnt + strlen(p) > 68) { 200 (void) printf("\n"); 201 linecnt = 0; 202 } 203 if (linecnt == 0) 204 linecnt = printf("\t"); 205 linecnt += printf("%s ", p); 206 } 207 208 for (p = strtok(cap1, " "); p; p = strtok(NULL, " ")) { 209 if (linecnt + strlen(p) > 68) { 210 (void) printf("\n"); 211 linecnt = 0; 212 } 213 if (linecnt == 0) 214 linecnt = printf("\t"); 215 linecnt += printf("%s ", p); 216 } 217 218 if (linecnt != 0) 219 (void) printf("\n"); 220 } 221 } 222 223 #if !defined(TEXT_DOMAIN) 224 #define TEXT_DOMAIN "SYS_TEST" 225 #endif 226 227 int 228 main(int argc, char *argv[]) 229 { 230 int errflg = 0; 231 int c; 232 char *vfmt; 233 char *isa, *isa32; 234 int d = -1; 235 const int excl_modes = /* exclusive mode settings */ 236 NATIVE_MODE | BITS_MODE | KERN_MODE | EXTN_MODE; 237 238 (void) setlocale(LC_ALL, ""); 239 (void) textdomain(TEXT_DOMAIN); 240 241 if ((pgmname = strrchr(*argv, '/')) == 0) 242 pgmname = argv[0]; 243 else 244 pgmname++; 245 246 while ((c = getopt(argc, argv, "nbkvx")) != EOF) 247 switch (c) { 248 case 'n': 249 if (mode & excl_modes) 250 errflg++; 251 mode |= NATIVE_MODE; 252 break; 253 case 'b': 254 if (mode & excl_modes) 255 errflg++; 256 mode |= BITS_MODE; 257 break; 258 case 'k': 259 if (mode & excl_modes) 260 errflg++; 261 mode |= KERN_MODE; 262 break; 263 case 'x': 264 if (mode & excl_modes || mode & VERBOSE_MODE) 265 errflg++; 266 mode |= EXTN_MODE; 267 break; 268 case 'v': 269 if (mode & EXTN_MODE) 270 errflg++; 271 mode |= VERBOSE_MODE; 272 break; 273 case '?': 274 default: 275 errflg++; 276 break; 277 } 278 279 if (errflg || optind != argc) { 280 (void) fprintf(stderr, 281 gettext("usage: %s [ [-v] [-b | -n | -k] | [-x] ]\n"), 282 pgmname); 283 return (1); 284 } 285 286 /* 287 * We use dev_cpu_self_cpuid for discovering hardware capabilities; 288 * but we only complain if we can't open it if we've been 289 * asked to report on those capabilities. 290 */ 291 if ((mode & (VERBOSE_MODE|EXTN_MODE)) != 0 && 292 (d = open(dev_cpu_self_cpuid, O_RDONLY)) == -1) 293 perror(dev_cpu_self_cpuid), exit(1); 294 295 if (mode & KERN_MODE) { 296 vfmt = gettext("%d-bit %s kernel modules\n"); 297 (void) report_abi(SI_ARCHITECTURE_K, vfmt); 298 return (0); 299 } 300 301 vfmt = gettext("%d-bit %s applications\n"); 302 303 if (mode & (BITS_MODE | NATIVE_MODE)) { 304 if ((isa = report_abi(SI_ARCHITECTURE_64, vfmt)) == NULL) 305 isa = report_abi(SI_ARCHITECTURE_32, vfmt); 306 if (isa != NULL && (mode & VERBOSE_MODE) != 0) 307 report_hwcap(d, isa); 308 } else { 309 if ((isa = report_abi(SI_ARCHITECTURE_64, vfmt)) != NULL) { 310 if (mode & (EXTN_MODE|VERBOSE_MODE)) 311 report_hwcap(d, isa); 312 else 313 (void) putchar(' '); 314 } 315 316 if ((isa32 = report_abi(SI_ARCHITECTURE_32, vfmt)) != NULL) { 317 if (mode & (EXTN_MODE|VERBOSE_MODE)) 318 report_hwcap(d, isa32); 319 } 320 321 if ((isa32 != NULL || isa != NULL) && 322 (mode & (EXTN_MODE|VERBOSE_MODE)) == 0) 323 (void) putchar('\n'); 324 } 325 326 return (0); 327 } 328