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 2009 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 /*
28  *  Subroutines used by the i86pc Generic Topology Enumerator
29  */
30 
31 #include <sys/types.h>
32 #include <strings.h>
33 #include <deflt.h>
34 #include <fcntl.h>
35 #include <unistd.h>
36 #include <fm/topo_mod.h>
37 #include <fm/topo_hc.h>
38 #include <sys/devfm.h>
39 #include <sys/systeminfo.h>
40 #include <sys/fm/protocol.h>
41 #include <sys/utsname.h>
42 #include <sys/smbios.h>
43 #include <sys/smbios_impl.h>
44 #include <x86pi_impl.h>
45 
46 
47 static const topo_pgroup_info_t sys_pgroup = {
48 	TOPO_PGROUP_SYSTEM,
49 	TOPO_STABILITY_PRIVATE,
50 	TOPO_STABILITY_PRIVATE,
51 	1
52 };
53 
54 static const topo_pgroup_info_t auth_pgroup = {
55 	FM_FMRI_AUTHORITY,
56 	TOPO_STABILITY_PRIVATE,
57 	TOPO_STABILITY_PRIVATE,
58 	1
59 };
60 
61 
62 /*
63  * Free hcfmri strings.
64  */
65 void
66 x86pi_hcfmri_info_fini(topo_mod_t *mod, x86pi_hcfmri_t *hc)
67 {
68 	if (hc->hc_name != NULL)
69 		topo_mod_strfree(mod, (char *)hc->hc_name);
70 	if (hc->manufacturer != NULL)
71 		topo_mod_strfree(mod, (char *)hc->manufacturer);
72 	if (hc->product != NULL)
73 		topo_mod_strfree(mod, (char *)hc->product);
74 	if (hc->version != NULL)
75 		topo_mod_strfree(mod, (char *)hc->version);
76 	if (hc->serial_number != NULL)
77 		topo_mod_strfree(mod, (char *)hc->serial_number);
78 	if (hc->asset_tag != NULL)
79 		topo_mod_strfree(mod, (char *)hc->asset_tag);
80 	if (hc->location != NULL)
81 		topo_mod_strfree(mod, (char *)hc->location);
82 	if (hc->part_number != NULL)
83 		topo_mod_strfree(mod, (char *)hc->part_number);
84 }
85 
86 
87 /*
88  * Get the server hostname (the ID as far as the topo authority is
89  * concerned) from sysinfo and return a copy to the caller.
90  *
91  * The string must be freed with topo_mod_strfree()
92  */
93 char *
94 x86pi_get_serverid(topo_mod_t *mod)
95 {
96 	int result;
97 	char hostname[MAXNAMELEN];
98 
99 	topo_mod_dprintf(mod, "x86pi_get_serverid\n");
100 
101 	result = sysinfo(SI_HOSTNAME, hostname, sizeof (hostname));
102 	/* Everything is freed up and it's time to return the platform-id */
103 	if (result == -1) {
104 		return (NULL);
105 	}
106 	topo_mod_dprintf(mod, "x86pi_get_serverid: hostname = %s\n", hostname);
107 
108 	return (topo_mod_strdup(mod, hostname));
109 }
110 
111 
112 /*
113  * Get copy of SMBIOS.
114  */
115 smbios_hdl_t *
116 x86pi_smb_open(topo_mod_t *mod)
117 {
118 	smbios_hdl_t *smb_hdl;
119 	char *f = "x86pi_smb_open";
120 
121 	topo_mod_dprintf(mod, "%s\n", f);
122 
123 	smb_hdl = topo_mod_smbios(mod);
124 	if (smb_hdl == NULL) {
125 		topo_mod_dprintf(mod, "%s: failed to load SMBIOS\n", f);
126 		return (NULL);
127 	}
128 
129 	return (smb_hdl);
130 }
131 
132 
133 /*
134  * Go through the smbios structures looking for a type. Fill in
135  * the structure count as well as the id(s) of the struct types.
136  */
137 void
138 x86pi_smb_strcnt(smbios_hdl_t *shp, smbs_cnt_t *stype)
139 {
140 	const smb_struct_t *sp = shp->sh_structs;
141 	int nstructs = shp->sh_nstructs;
142 	int i, cnt;
143 
144 	for (i = 0, cnt = 0; i < nstructs; i++, sp++) {
145 		if (sp->smbst_hdr->smbh_type == stype->type) {
146 			stype->ids[cnt].node = NULL;
147 			stype->ids[cnt].id = sp->smbst_hdr->smbh_hdl;
148 			cnt++;
149 		}
150 	}
151 
152 	stype->count = cnt;
153 }
154 
155 
156 /*
157  * Calculate the authority information for a node.  Inherit the data if
158  * possible, but always create an appropriate property group.
159  */
160 int
161 x86pi_set_auth(topo_mod_t *mod, x86pi_hcfmri_t *hcfmri, tnode_t *t_parent,
162     tnode_t *t_node)
163 {
164 	int 		result;
165 	int		err;
166 	int		is_chassis = 0;
167 	int		chassis_instance = 0;
168 	nvlist_t	*auth;
169 	char		*val = NULL;
170 	char		*prod = NULL;
171 	char		*psn = NULL;
172 	char		*csn = NULL;
173 	char		*server = NULL;
174 	char		*f = "x86pi_set_auth";
175 
176 	if (mod == NULL || t_parent == NULL || t_node == NULL) {
177 		return (-1);
178 	}
179 
180 	result = topo_pgroup_create(t_node, &auth_pgroup, &err);
181 	if (result != 0 && err != ETOPO_PROP_DEFD) {
182 		/*
183 		 * We failed to create the property group and it was not
184 		 * already defined.  Set the err code and return failure.
185 		 */
186 		topo_mod_seterrno(mod, err);
187 		return (-1);
188 	}
189 
190 	/* Get the authority information already available from the parent */
191 	auth = topo_mod_auth(mod, t_parent);
192 
193 	/* Determnine if this is a chassis node and set it's instance */
194 	if ((strlen(hcfmri->hc_name) == strlen(CHASSIS)) &&
195 	    strncmp(hcfmri->hc_name, CHASSIS, strlen(CHASSIS)) == 0) {
196 		is_chassis = 1;
197 		chassis_instance = hcfmri->instance;
198 	}
199 
200 	/*
201 	 * Set the authority data, inheriting it if possible, but creating it
202 	 * if necessary.
203 	 */
204 
205 	/* product-id */
206 	result = topo_prop_inherit(t_node, FM_FMRI_AUTHORITY,
207 	    FM_FMRI_AUTH_PRODUCT, &err);
208 	if (result != 0 && err != ETOPO_PROP_DEFD) {
209 		result = nvlist_lookup_string(auth, FM_FMRI_AUTH_PRODUCT,
210 		    &prod);
211 		if (result != 0 || prod == NULL) {
212 			/*
213 			 * No product information in the parent node or auth
214 			 * list. Use the product information in the hcfrmi
215 			 * struct.
216 			 */
217 			prod = (char *)hcfmri->product;
218 			if (prod == NULL) {
219 				topo_mod_dprintf(mod, "%s: product name not "
220 				    "found for %s node\n", f, hcfmri->hc_name);
221 			}
222 		}
223 
224 		/*
225 		 * We continue even if the product information is not available
226 		 * to enumerate as much as possible.
227 		 */
228 		if (prod != NULL) {
229 			result = topo_prop_set_string(t_node, FM_FMRI_AUTHORITY,
230 			    FM_FMRI_AUTH_PRODUCT, TOPO_PROP_IMMUTABLE, prod,
231 			    &err);
232 			if (result != 0) {
233 				/* Preserve the error and continue */
234 				topo_mod_seterrno(mod, err);
235 				topo_mod_dprintf(mod, "%s: failed to set "
236 				    "property %s (%d) : %s\n", f,
237 				    FM_FMRI_AUTH_PRODUCT, err,
238 				    topo_strerror(err));
239 			}
240 		}
241 	}
242 
243 	/* product-sn */
244 	result = topo_prop_inherit(t_node, FM_FMRI_AUTHORITY,
245 	    FM_FMRI_AUTH_PRODUCT_SN, &err);
246 	if (result != 0 && err != ETOPO_PROP_DEFD) {
247 		result = nvlist_lookup_string(auth, FM_FMRI_AUTH_PRODUCT_SN,
248 		    &psn);
249 		if (result != 0 || psn == NULL) {
250 			/*
251 			 * No product-sn information in the parent node or auth
252 			 * list.
253 			 */
254 			topo_mod_dprintf(mod, "%s: psn not found\n", f);
255 		} else {
256 			/*
257 			 * We continue even if the product-sn information is
258 			 * not available to enumerate as much as possible.
259 			 */
260 			result = topo_prop_set_string(t_node, FM_FMRI_AUTHORITY,
261 			    FM_FMRI_AUTH_PRODUCT_SN, TOPO_PROP_IMMUTABLE, psn,
262 			    &err);
263 			if (result != 0) {
264 				/* Preserve the error and continue */
265 				topo_mod_seterrno(mod, err);
266 				topo_mod_dprintf(mod, "%s: failed to "
267 				    "set property %s (%d) : %s\n", f,
268 				    FM_FMRI_AUTH_PRODUCT_SN, err,
269 				    topo_strerror(err));
270 			}
271 		}
272 	}
273 
274 	/* chassis-id */
275 	if (is_chassis == 0 || (is_chassis == 1 && chassis_instance == 0)) {
276 		/* either not a chassis node, or chassis #0 */
277 		result = topo_prop_inherit(t_node, FM_FMRI_AUTHORITY,
278 		    FM_FMRI_AUTH_CHASSIS, &err);
279 	} else {
280 		/* chassis 'n' in a >1 chassis system */
281 		result = err = -1;
282 	}
283 	if (result != 0 && err != ETOPO_PROP_DEFD) {
284 		if (is_chassis == 0) {
285 			result = nvlist_lookup_string(auth,
286 			    FM_FMRI_AUTH_CHASSIS, &csn);
287 			if (result != 0 || csn == NULL) {
288 				/*
289 				 * No chassis information in the parent
290 				 * node or auth list.
291 				 */
292 				topo_mod_dprintf(mod,
293 				    "%s: csn name not found\n", f);
294 			}
295 		} else {
296 			/*
297 			 * So as not to blindly set the chassis-id to
298 			 * chassis #0's serial number.
299 			 */
300 			csn = val = topo_mod_strdup(mod, hcfmri->serial_number);
301 		}
302 
303 		/*
304 		 * We continue even if the chassis information is not available
305 		 * to enumerate as much as possible.
306 		 */
307 		if (csn != NULL) {
308 			if (is_chassis == 1)
309 				result = topo_prop_set_string(t_node,
310 				    FM_FMRI_AUTHORITY, FM_FMRI_AUTH_CHASSIS,
311 				    TOPO_PROP_MUTABLE, csn, &err);
312 			else
313 				result = topo_prop_set_string(t_node,
314 				    FM_FMRI_AUTHORITY, FM_FMRI_AUTH_CHASSIS,
315 				    TOPO_PROP_IMMUTABLE, csn, &err);
316 
317 			if (result != 0) {
318 				/* Preserve the error and continue */
319 				topo_mod_seterrno(mod, err);
320 				topo_mod_dprintf(mod, "%s: failed to "
321 				    "set property %s (%d) : %s\n", f,
322 				    FM_FMRI_AUTH_CHASSIS, err,
323 				    topo_strerror(err));
324 			}
325 		}
326 
327 		if (val != NULL) {
328 			topo_mod_strfree(mod, val);
329 			val = NULL;
330 		}
331 	}
332 
333 	/* server-id */
334 	result = topo_prop_inherit(t_node, FM_FMRI_AUTHORITY,
335 	    FM_FMRI_AUTH_SERVER, &err);
336 	if (result != 0 && err != ETOPO_PROP_DEFD) {
337 		result = nvlist_lookup_string(auth, FM_FMRI_AUTH_SERVER,
338 		    &server);
339 		if (result != 0 || server == NULL) {
340 			/*
341 			 * No server information in the parent node or auth
342 			 * list.  Find the server information in hostname.
343 			 */
344 			server = val = x86pi_get_serverid(mod);
345 			if (server == NULL) {
346 				topo_mod_dprintf(mod, "%s: server "
347 				    "name not found for %s node\n", f,
348 				    hcfmri->hc_name);
349 			}
350 		}
351 
352 		/*
353 		 * We continue even if the server information is not available
354 		 * to enumerate as much as possible.
355 		 */
356 		if (server != NULL) {
357 			result = topo_prop_set_string(t_node, FM_FMRI_AUTHORITY,
358 			    FM_FMRI_AUTH_SERVER, TOPO_PROP_IMMUTABLE, server,
359 			    &err);
360 			if (result != 0) {
361 				/* Preserve the error and continue */
362 				topo_mod_seterrno(mod, err);
363 				topo_mod_dprintf(mod, "%s: failed to "
364 				    "set property %s (%d) : %s\n", f,
365 				    FM_FMRI_AUTH_SERVER, err,
366 				    topo_strerror(err));
367 			}
368 		}
369 
370 		if (val != NULL)
371 			topo_mod_strfree(mod, val);
372 	}
373 
374 	nvlist_free(auth);
375 
376 	return (0);
377 }
378 
379 
380 /*
381  * Calculate a generic FRU for the given node.  If the node is not a FRU,
382  * then inherit the FRU data from the nodes parent.
383  */
384 int
385 x86pi_set_frufmri(topo_mod_t *mod, x86pi_hcfmri_t *hcfmri, tnode_t *t_parent,
386     tnode_t *t_node, int flag)
387 {
388 	int		result;
389 	int		err;
390 
391 	nvlist_t	*auth = NULL;
392 	nvlist_t	*frufmri = NULL;
393 
394 	if (t_node == NULL || mod == NULL) {
395 		return (-1);
396 	}
397 
398 	/*
399 	 * Determine if this node is a FRU
400 	 */
401 	if (!(flag & X86PI_ENUM_FRU)) {
402 		/* This node is not a FRU.  Inherit from parent and return */
403 		topo_node_fru_set(t_node, NULL, 0, &result);
404 		return (0);
405 	}
406 
407 	/*
408 	 * This node is a FRU.  Create an FMRI.
409 	 */
410 	auth	= topo_mod_auth(mod, t_parent);
411 	frufmri	= topo_mod_hcfmri(mod, t_parent, FM_HC_SCHEME_VERSION,
412 	    hcfmri->hc_name, hcfmri->instance, NULL, auth,
413 	    hcfmri->part_number, hcfmri->version, hcfmri->serial_number);
414 	if (frufmri == NULL) {
415 		topo_mod_dprintf(mod, "failed to create FRU: %s\n",
416 		    topo_strerror(topo_mod_errno(mod)));
417 	}
418 	nvlist_free(auth);
419 
420 	/* Set the FRU, whether NULL or not */
421 	result = topo_node_fru_set(t_node, frufmri, 0, &err);
422 	if (result != 0)  {
423 		topo_mod_seterrno(mod, err);
424 	}
425 	nvlist_free(frufmri);
426 
427 	return (result);
428 }
429 
430 
431 /*
432  * Set the label for a topo node.
433  */
434 int
435 x86pi_set_label(topo_mod_t *mod, const char *label, const char *name,
436     tnode_t *t_node)
437 {
438 	int	result;
439 	int	err;
440 
441 	if (mod == NULL) {
442 		return (-1);
443 	}
444 
445 	/*
446 	 * Set the label for this topology node.
447 	 * Note that a NULL label will inherit the label from topology
448 	 * node's parent.
449 	 */
450 	result = topo_node_label_set(t_node, (char *)label, &err);
451 	if (result != 0) {
452 		topo_mod_seterrno(mod, err);
453 		topo_mod_dprintf(mod, "x86pi_set_label: failed with label %s "
454 		    "on %s node: %s\n", (label == NULL ? "NULL" : label),
455 		    name, topo_strerror(err));
456 	}
457 
458 	return (result);
459 }
460 
461 
462 /*
463  * Calculate the system information for a node.  Inherit the data if
464  * possible, but always create an appropriate property group.
465  */
466 int
467 x86pi_set_system(topo_mod_t *mod, tnode_t *t_node)
468 {
469 	int		result;
470 	int		err;
471 	struct utsname	uts;
472 	char		isa[MAXNAMELEN];
473 
474 	if (mod == NULL || t_node == NULL) {
475 		return (-1);
476 	}
477 
478 	result = topo_pgroup_create(t_node, &sys_pgroup, &err);
479 	if (result != 0 && err != ETOPO_PROP_DEFD) {
480 		/*
481 		 * We failed to create the property group and it was not
482 		 * already defined.  Set the err code and return failure.
483 		 */
484 		topo_mod_seterrno(mod, err);
485 		return (-1);
486 	}
487 
488 	result = topo_prop_inherit(t_node, TOPO_PGROUP_SYSTEM, TOPO_PROP_ISA,
489 	    &err);
490 	if (result != 0 && err != ETOPO_PROP_DEFD) {
491 		isa[0] = '\0';
492 		result = sysinfo(SI_ARCHITECTURE, isa, sizeof (isa));
493 		if (result == -1) {
494 			/* Preserve the error and continue */
495 			topo_mod_dprintf(mod, "x86pi_set_system: failed to "
496 			    "read SI_ARCHITECTURE: %d\n", errno);
497 		}
498 		if (strnlen(isa, MAXNAMELEN) > 0) {
499 			result = topo_prop_set_string(t_node,
500 			    TOPO_PGROUP_SYSTEM, TOPO_PROP_ISA,
501 			    TOPO_PROP_IMMUTABLE, isa, &err);
502 			if (result != 0) {
503 				/* Preserve the error and continue */
504 				topo_mod_seterrno(mod, err);
505 				topo_mod_dprintf(mod,
506 				    "x86pi_set_auth: failed to "
507 				    "set property %s (%d) : %s\n",
508 				    TOPO_PROP_ISA, err, topo_strerror(err));
509 			}
510 		}
511 	}
512 
513 	result = topo_prop_inherit(t_node, TOPO_PGROUP_SYSTEM,
514 	    TOPO_PROP_MACHINE, &err);
515 	if (result != 0 && err != ETOPO_PROP_DEFD) {
516 		result = uname(&uts);
517 		if (result == -1) {
518 			/* Preserve the error and continue */
519 			topo_mod_seterrno(mod, errno);
520 			topo_mod_dprintf(mod, "x86pi_set_system: failed to "
521 			    "read uname: %d\n", errno);
522 		}
523 		if (strnlen(uts.machine, sizeof (uts.machine)) > 0) {
524 			result = topo_prop_set_string(t_node,
525 			    TOPO_PGROUP_SYSTEM, TOPO_PROP_MACHINE,
526 			    TOPO_PROP_IMMUTABLE, uts.machine, &err);
527 			if (result != 0) {
528 				/* Preserve the error and continue */
529 				topo_mod_seterrno(mod, err);
530 				topo_mod_dprintf(mod,
531 				    "x86pi_set_auth: failed to "
532 				    "set property %s (%d) : %s\n",
533 				    TOPO_PROP_MACHINE, err, topo_strerror(err));
534 			}
535 		}
536 	}
537 
538 	return (0);
539 }
540 
541 /*
542  * All the checks for compatibility are done within the kernel where the
543  * ereport generators are. They'll determine first if there's a problem
544  * and the topo enum will follow suit. The /dev/fm ioclt returns the value
545  * of the x86gentopo_legacy kernel variable which determines if this platform
546  * will provide an x86 generic topo or legacy topo enumeration.
547  */
548 /* ARGSUSED */
549 int
550 x86pi_check_comp(topo_mod_t *mod, smbios_hdl_t *shp)
551 {
552 	int rv;
553 	int fd;
554 	int32_t legacy;
555 	nvlist_t *nvl = NULL;
556 	fm_ioc_data_t fid;
557 	char *ibuf = NULL, *obuf = NULL;
558 	size_t insz = 0, outsz = 0;
559 	char *f = "x86pi_check_comp";
560 
561 	/* open /dev/fm */
562 	fd = open("/dev/fm", O_RDONLY);
563 	if (fd < 0) {
564 		topo_mod_dprintf(mod, "%s: failed to open /dev/fm.\n", f);
565 		return (X86PI_NONE);
566 	}
567 
568 	/* set up buffers and ioctl data structure */
569 	outsz = FM_IOC_MAXBUFSZ;
570 	obuf = topo_mod_alloc(mod, outsz);
571 	if (obuf == NULL) {
572 		perror("umem_alloc");
573 		return (X86PI_NONE);
574 	}
575 
576 	fid.fid_version = 1;
577 	fid.fid_insz = insz;
578 	fid.fid_inbuf = ibuf;
579 	fid.fid_outsz = outsz;
580 	fid.fid_outbuf = obuf;
581 
582 	/* send the ioctl to /dev/fm to retrieve legacy variable */
583 	rv = ioctl(fd, FM_IOC_GENTOPO_LEGACY, &fid);
584 	if (rv < 0) {
585 		topo_mod_dprintf(mod, "%s: ioctl to /dev/fm failed", f);
586 		perror("fm_ioctl");
587 		(void) close(fd);
588 		return (X86PI_NONE);
589 	}
590 	(void) close(fd);
591 
592 	(void) nvlist_unpack(fid.fid_outbuf, fid.fid_outsz, &nvl, 0);
593 	(void) nvlist_lookup_int32(nvl, FM_GENTOPO_LEGACY, &legacy);
594 
595 	nvlist_free(nvl);
596 	topo_mod_free(mod, obuf, outsz);
597 
598 	if (legacy == 1) {
599 		/* legacy kernel variable set; will do the same */
600 		return (X86PI_NONE);
601 	}
602 
603 	/* legacy kernel variable not set; generic topo enum */
604 	return (X86PI_FULL);
605 }
606 
607 const char *
608 x86pi_cleanup_smbios_str(topo_mod_t *mod, const char *begin, int str_type)
609 {
610 	char buf[MAXNAMELEN];
611 	const char *end, *cp;
612 	char *pp;
613 	char c;
614 	int i;
615 
616 	end = begin + strlen(begin);
617 
618 	while (begin < end && isspace(*begin))
619 		begin++;
620 	while (begin < end && isspace(*(end - 1)))
621 		end--;
622 
623 	if (begin >= end)
624 		return (NULL);
625 
626 	cp = begin;
627 	for (i = 0; i < MAXNAMELEN - 1; i++) {
628 		if (cp >= end)
629 			break;
630 		c = *cp;
631 		if (str_type == LABEL) {
632 			if (!isprint(c))
633 				buf[i] = '-';
634 			else
635 				buf[i] = c;
636 		} else {
637 			if (c == ':' || c == '=' || c == '/' ||
638 			    isspace(c) || !isprint(c))
639 				buf[i] = '-';
640 			else
641 				buf[i] = c;
642 		}
643 		cp++;
644 	}
645 	buf[i] = 0;
646 
647 	pp = topo_mod_strdup(mod, buf);
648 
649 	if (str_type == LABEL)
650 		topo_mod_strfree(mod, (char *)begin);
651 
652 	return (pp);
653 }
654