xref: /illumos-gate/usr/src/cmd/isainfo/isainfo.c (revision 56726c7e)
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