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 /*
23 * Copyright (c) 1990, 2010, Oracle and/or its affiliates. All rights reserved.
24 * Copyright 2011, Joyent, Inc. All rights reserved.
25 * Copyright (c) 2019 Peter Tribble.
26 * Copyright (c) 2022 Sachidananda Urs <sacchi@gmail.com>
27 * Copyright 2022 Oxide Computer Company
28 */
29
30 /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
31 /* All Rights Reserved */
32
33 #include <stdio.h>
34 #include <stdarg.h>
35 #include <stdlib.h>
36 #include <unistd.h>
37 #include <strings.h>
38 #include <sys/systeminfo.h>
39 #include <sys/types.h>
40 #include <sys/stat.h>
41 #include <err.h>
42 #include "prtconf.h"
43
44 struct prt_opts opts;
45 struct prt_dbg dbg;
46 static char new_path[MAXPATHLEN];
47
48 #define INDENT_LENGTH 4
49
50 static const char *usage =
51 "%s [ -F | -m | -V | -x | -abcdvpPD ] [ <device_path > ]\n";
52
53 static void
setpname(const char * name)54 setpname(const char *name)
55 {
56 char *p;
57
58 if (name == NULL)
59 opts.o_progname = "prtconf";
60 else if ((p = strrchr(name, '/')) != NULL)
61 opts.o_progname = (const char *) p + 1;
62 else
63 opts.o_progname = name;
64 }
65
66 /*PRINTFLIKE1*/
67 void
dprintf(const char * fmt,...)68 dprintf(const char *fmt, ...)
69 {
70 if (dbg.d_debug) {
71 va_list ap;
72 va_start(ap, fmt);
73 (void) vfprintf(stderr, fmt, ap);
74 va_end(ap);
75 }
76 }
77
78 void
indent_to_level(int ilev)79 indent_to_level(int ilev)
80 {
81 (void) printf("%*s", INDENT_LENGTH * ilev, "");
82 }
83
84 /*
85 * debug version has two more flags:
86 * -L force load driver
87 * -M: print per driver list
88 */
89
90 #ifdef DEBUG
91 static const char *optstring = "abcdDvVxmpPFf:M:LuC";
92 #else
93 static const char *optstring = "abcdDvVxmpPFf:uC";
94 #endif /* DEBUG */
95
96 int
main(int argc,char * argv[])97 main(int argc, char *argv[])
98 {
99 long pagesize, npages;
100 int c, ret;
101 char hw_provider[SYS_NMLN];
102
103 setpname(argv[0]);
104 opts.o_promdev = "/dev/openprom";
105
106 while ((c = getopt(argc, argv, optstring)) != -1) {
107 switch (c) {
108 case 'a':
109 ++opts.o_ancestors;
110 break;
111 case 'b':
112 ++opts.o_productinfo;
113 break;
114 case 'c':
115 ++opts.o_children;
116 break;
117 case 'd':
118 ++opts.o_pciid;
119 break;
120 case 'D':
121 ++opts.o_drv_name;
122 break;
123 case 'v':
124 ++opts.o_verbose;
125 break;
126 case 'm':
127 ++opts.o_memory;
128 break;
129 case 'p':
130 ++opts.o_prominfo;
131 break;
132 case 'f':
133 opts.o_promdev = optarg;
134 break;
135 case 'V':
136 ++opts.o_promversion;
137 break;
138 case 'x':
139 ++opts.o_prom_ready64;
140 break;
141 case 'F':
142 ++opts.o_fbname;
143 ++opts.o_noheader;
144 break;
145 case 'P':
146 ++opts.o_pseudodevs;
147 break;
148 case 'C':
149 ++opts.o_forcecache;
150 break;
151 #ifdef DEBUG
152 case 'M':
153 dbg.d_drivername = optarg;
154 ++dbg.d_bydriver;
155 break;
156 case 'L':
157 ++dbg.d_forceload;
158 break;
159 #endif /* DEBUG */
160
161 default:
162 (void) fprintf(stderr, usage, opts.o_progname);
163 return (1);
164 }
165 }
166
167 (void) uname(&opts.o_uts);
168
169 if (opts.o_verbose || opts.o_pciid) {
170 opts.o_pcidb = pcidb_open(PCIDB_VERSION);
171 if (opts.o_pcidb == NULL) {
172 warn("pcidb facility not available, PCI names will "
173 "not be translated");
174 }
175 }
176
177 if (opts.o_fbname)
178 return (do_fbname());
179
180 if (opts.o_promversion)
181 return (do_promversion());
182
183 if (opts.o_prom_ready64)
184 return (0);
185
186 if (opts.o_productinfo)
187 return (do_productinfo());
188
189 opts.o_devices_path = NULL;
190 opts.o_devt = DDI_DEV_T_NONE;
191 opts.o_target = 0;
192 if (optind < argc) {
193 struct stat sinfo;
194 char *path = argv[optind];
195 int error;
196
197 if (opts.o_prominfo) {
198 /* PROM tree cannot be used with path */
199 (void) fprintf(stderr, "%s: path and -p option are "
200 "mutually exclusive\n", opts.o_progname);
201 return (1);
202 }
203
204 if (strlen(path) >= MAXPATHLEN) {
205 (void) fprintf(stderr, "%s: "
206 "path specified is too long\n", opts.o_progname);
207 return (1);
208 }
209
210 if ((error = stat(path, &sinfo)) != 0) {
211
212 /* an invalid path was specified */
213 (void) fprintf(stderr, "%s: invalid path specified\n",
214 opts.o_progname);
215 return (1);
216
217 } else if (((sinfo.st_mode & S_IFMT) == S_IFCHR) ||
218 ((sinfo.st_mode & S_IFMT) == S_IFBLK)) {
219
220 opts.o_devt = sinfo.st_rdev;
221 error = 0;
222
223 } else if ((sinfo.st_mode & S_IFMT) == S_IFDIR) {
224 size_t len, plen;
225
226 if (realpath(path, new_path) == NULL) {
227 (void) fprintf(stderr, "%s: invalid device"
228 " path specified\n",
229 opts.o_progname);
230 return (1);
231 }
232
233 len = strlen(new_path);
234 plen = strlen("/devices");
235 if (len < plen) {
236 /* This is not a valid /devices path */
237 error = 1;
238 } else if ((len == plen) &&
239 (strcmp(new_path, "/devices") == 0)) {
240 /* /devices is the root nexus */
241 opts.o_devices_path = "/";
242 error = 0;
243 } else if (strncmp(new_path, "/devices/", plen + 1)) {
244 /* This is not a valid /devices path */
245 error = 1;
246 } else {
247 /* a /devices/ path was specified */
248 opts.o_devices_path = new_path + plen;
249 error = 0;
250 }
251
252 } else {
253 /* an invalid device path was specified */
254 error = 1;
255 }
256
257 if (error) {
258 (void) fprintf(stderr, "%s: "
259 "invalid device path specified\n",
260 opts.o_progname);
261 return (1);
262 }
263
264 opts.o_target = 1;
265 }
266
267 if ((opts.o_ancestors || opts.o_children) && (!opts.o_target)) {
268 (void) fprintf(stderr, "%s: options require a device path\n",
269 opts.o_progname);
270 return (1);
271 }
272
273 if (opts.o_target) {
274 prtconf_devinfo();
275 return (0);
276 }
277
278 if (!opts.o_memory) {
279 ret = sysinfo(SI_HW_PROVIDER, hw_provider,
280 sizeof (hw_provider));
281 /*
282 * If 0 bytes are returned (the system returns '1', for the \0),
283 * we're probably on x86, default to "Unknown Hardware Vendor".
284 */
285 if (ret <= 1) {
286 (void) strncpy(hw_provider, "Unknown Hardware Vendor",
287 sizeof (hw_provider));
288 }
289 (void) printf("System Configuration: %s %s\n", hw_provider,
290 opts.o_uts.machine);
291 }
292
293 pagesize = sysconf(_SC_PAGESIZE);
294 npages = sysconf(_SC_PHYS_PAGES);
295 if (pagesize == -1 || npages == -1) {
296 if (opts.o_memory) {
297 (void) printf("0\n");
298 return (1);
299 } else {
300 (void) printf("Memory size: unable to determine\n");
301 }
302 } else {
303 const int64_t mbyte = 1024 * 1024;
304 int64_t ii = (int64_t)pagesize * npages;
305
306 if (opts.o_memory) {
307 (void) printf("%ld\n", (long)((ii+mbyte-1) / mbyte));
308 return (0);
309 } else {
310 (void) printf("Memory size: %ld Megabytes\n",
311 (long)((ii+mbyte-1) / mbyte));
312 }
313 }
314
315 if (opts.o_prominfo) {
316 (void) printf("System Peripherals (PROM Nodes):\n\n");
317 if (do_prominfo() == 0)
318 return (0);
319 (void) fprintf(stderr, "%s: Defaulting to non-PROM mode...\n",
320 opts.o_progname);
321 }
322
323 (void) printf("System Peripherals (Software Nodes):\n\n");
324
325 (void) prtconf_devinfo();
326
327 return (0);
328 }
329