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 2008 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 /*
28  * Enumerate a CPU node
29  */
30 #include <sys/types.h>
31 #include <strings.h>
32 #include <sys/fm/protocol.h>
33 #include <fm/topo_mod.h>
34 #include <fm/topo_hc.h>
35 #include "pi_impl.h"
36 
37 #define	_ENUM_NAME	"enum_cpu"
38 
39 typedef struct cpuwalk_s {
40 	topo_mod_t	*mod;
41 	char		*serial;
42 } cpuwalk_t;
43 
44 static int pi_enum_cpu_serial(topo_mod_t *, md_t *, mde_cookie_t, char **);
45 static int pi_enum_cpu_serial_cb(md_t *, mde_cookie_t, mde_cookie_t, void *);
46 
47 int
pi_enum_cpu(topo_mod_t * mod,md_t * mdp,mde_cookie_t mde_node,topo_instance_t inst,tnode_t * t_parent,const char * hc_name,tnode_t ** t_node)48 pi_enum_cpu(topo_mod_t *mod, md_t *mdp, mde_cookie_t mde_node,
49     topo_instance_t inst, tnode_t *t_parent, const char *hc_name,
50     tnode_t **t_node)
51 {
52 	int		result;
53 	int		err;
54 	int		cpumask;
55 	nvlist_t	*asru = NULL;
56 	char		*serial = NULL;
57 
58 	*t_node = NULL;
59 
60 	/*
61 	 * Create the basic topology node for the CPU using the generic
62 	 * enumerator.
63 	 */
64 	result = pi_enum_generic_impl(mod, mdp, mde_node, inst, t_parent,
65 	    t_parent, hc_name, _ENUM_NAME, t_node, 0);
66 	if (result != 0) {
67 		/* Error messages are printed by the generic routine */
68 		return (result);
69 	}
70 
71 	/*
72 	 * If the hc_name is "chip" or "core", set asru to resource,
73 	 * otherwise for "cpu" and "strand", set asru to CPU scheme FMRI.
74 	 */
75 	if (strcmp(hc_name, CHIP) == 0 || strcmp(hc_name, CORE) == 0) {
76 		result = topo_node_resource(*t_node, &asru, &err);
77 		if (result != 0) {
78 			topo_mod_dprintf(mod,
79 			    "%s node_0x%llx failed to get resource: %s\n",
80 			    _ENUM_NAME, (uint64_t)mde_node, topo_strerror(err));
81 			return (-1);
82 		}
83 	} else {
84 		/*
85 		 * Compute ASRU for "cpu" and "strand" node.
86 		 * Get the parameters required to create an FMRI.  The cpumask
87 		 * is on the chip itself and while it may be part of an ereport
88 		 * payload is unavailable here, so we set it to zero.
89 		 */
90 		cpumask = 0;
91 
92 		/*
93 		 * Find the serial number, which is on the "chip" node, not the
94 		 * "cpu" node.
95 		 */
96 		result = pi_enum_cpu_serial(mod, mdp, mde_node, &serial);
97 		if (result != 0 || serial == NULL) {
98 			topo_mod_dprintf(mod,
99 			    "%s node_0x%llx failed to find serial number.\n",
100 			    _ENUM_NAME, (uint64_t)mde_node);
101 			return (result);
102 		}
103 
104 		/*
105 		 * Create a CPU scheme FMRI and set it as the ASRU for the CPU
106 		 * node
107 		 */
108 		asru = topo_mod_cpufmri(mod, FM_CPU_SCHEME_VERSION, inst,
109 		    cpumask, serial);
110 		topo_mod_strfree(mod, serial);
111 		if (asru == NULL) {
112 			topo_mod_dprintf(mod, "%s node_0x%llx failed to "
113 			    "compute cpu scheme ASRU: %s\n",
114 			    _ENUM_NAME, (uint64_t)mde_node,
115 			    topo_strerror(topo_mod_errno(mod)));
116 			return (-1);
117 		}
118 	}
119 
120 	/* Set the ASRU on the node without flags (the 0) */
121 	result = topo_node_asru_set(*t_node, asru, 0, &err);
122 	nvlist_free(asru);
123 	if (result != 0) {
124 		topo_mod_dprintf(mod,
125 		    "%s node_0x%llx failed to set ASRU: %s\n", _ENUM_NAME,
126 		    (uint64_t)mde_node, topo_strerror(err));
127 		return (-1);
128 	}
129 
130 	return (0);
131 }
132 
133 
134 static int
pi_enum_cpu_serial(topo_mod_t * mod,md_t * mdp,mde_cookie_t mde_node,char ** serial)135 pi_enum_cpu_serial(topo_mod_t *mod, md_t *mdp, mde_cookie_t mde_node,
136     char **serial)
137 {
138 	int			result;
139 	cpuwalk_t		args;
140 	mde_str_cookie_t	component_cookie;
141 	mde_str_cookie_t	back_cookie;
142 
143 	args.mod = mod;
144 	args.serial = NULL;
145 
146 	/*
147 	 * Search backwards through the PRI graph, starting at the current
148 	 * strand (aka cpu) mde_node, and find the MD_STR_CHIP node.  This
149 	 * node has the serial number for the cpu.
150 	 */
151 	component_cookie = md_find_name(mdp, MD_STR_COMPONENT);
152 	back_cookie	 = md_find_name(mdp, MD_STR_BACK);
153 
154 	result = md_walk_dag(mdp, mde_node, component_cookie, back_cookie,
155 	    pi_enum_cpu_serial_cb, (void *)&args);
156 	*serial = args.serial;
157 
158 	return (result);
159 }
160 
161 
162 /*ARGSUSED*/
163 static int
pi_enum_cpu_serial_cb(md_t * mdp,mde_cookie_t mde_parent,mde_cookie_t mde_node,void * private)164 pi_enum_cpu_serial_cb(md_t *mdp, mde_cookie_t mde_parent,
165     mde_cookie_t mde_node, void *private)
166 {
167 	char		*hc_name;
168 	cpuwalk_t	*args = (cpuwalk_t *)private;
169 
170 	if (args == NULL) {
171 		return (MDE_WALK_ERROR);
172 	}
173 	args->serial = NULL;
174 
175 	hc_name = pi_get_topo_hc_name(args->mod, mdp, mde_node);
176 	if (hc_name != NULL && strcmp(hc_name, MD_STR_CHIP) == 0) {
177 		args->serial = pi_get_serial(args->mod, mdp, mde_node);
178 	}
179 	topo_mod_strfree(args->mod, hc_name);
180 
181 	return ((args->serial == NULL ? MDE_WALK_NEXT : MDE_WALK_DONE));
182 }
183