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 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 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 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 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