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 *
getsysinfo(int cmd)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
bitness(const char * isaname)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 *
report_abi(int cmd,const char * vfmt)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
machtype(const char * isaname)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
report_hwcap(int d,const char * isa)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
main(int argc,char * argv[])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