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 2009 Sun Microsystems, Inc. All rights reserved.
23 * Use is subject to license terms.
24 */
25
26 #include <sys/stat.h>
27 #include <sys/types.h>
28 #include <sys/time.h>
29
30 #include <sys/fm/protocol.h>
31 #include <sys/fm/smb/fmsmb.h>
32 #include <sys/devfm.h>
33
34 #include <sys/cpu_module.h>
35
36 #define ANY_ID (uint_t)-1
37
38 /*
39 * INIT_HDLS is the initial size of cmi_hdl_t array. We fill the array
40 * during cmi_hdl_walk, if the array overflows, we will reallocate
41 * a new array twice the size of the old one.
42 */
43 #define INIT_HDLS 16
44
45 typedef struct fm_cmi_walk_t
46 {
47 uint_t chipid; /* chipid to match during walk */
48 uint_t coreid; /* coreid to match */
49 uint_t strandid; /* strandid to match */
50 int (*cbfunc)(cmi_hdl_t, void *, void *); /* callback function */
51 cmi_hdl_t *hdls; /* allocated array to save the handles */
52 int nhdl_max; /* allocated array size */
53 int nhdl; /* handles saved */
54 } fm_cmi_walk_t;
55
56 extern int x86gentopo_legacy;
57
58 int
fm_get_paddr(nvlist_t * nvl,uint64_t * paddr)59 fm_get_paddr(nvlist_t *nvl, uint64_t *paddr)
60 {
61 uint8_t version;
62 uint64_t pa;
63 char *scheme;
64 int err;
65
66 /* Verify FMRI scheme name and version number */
67 if ((nvlist_lookup_string(nvl, FM_FMRI_SCHEME, &scheme) != 0) ||
68 (strcmp(scheme, FM_FMRI_SCHEME_HC) != 0) ||
69 (nvlist_lookup_uint8(nvl, FM_VERSION, &version) != 0) ||
70 version > FM_HC_SCHEME_VERSION) {
71 return (EINVAL);
72 }
73
74 if ((err = cmi_mc_unumtopa(NULL, nvl, &pa)) != CMI_SUCCESS &&
75 err != CMIERR_MC_PARTIALUNUMTOPA)
76 return (EINVAL);
77
78 *paddr = pa;
79 return (0);
80 }
81
82 /*
83 * Routines for cmi handles walk.
84 */
85
86 static void
walk_init(fm_cmi_walk_t * wp,uint_t chipid,uint_t coreid,uint_t strandid,int (* cbfunc)(cmi_hdl_t,void *,void *))87 walk_init(fm_cmi_walk_t *wp, uint_t chipid, uint_t coreid, uint_t strandid,
88 int (*cbfunc)(cmi_hdl_t, void *, void *))
89 {
90 wp->chipid = chipid;
91 wp->coreid = coreid;
92 wp->strandid = strandid;
93 /*
94 * If callback is not set, we allocate an array to save the
95 * cmi handles.
96 */
97 if ((wp->cbfunc = cbfunc) == NULL) {
98 wp->hdls = kmem_alloc(sizeof (cmi_hdl_t) * INIT_HDLS, KM_SLEEP);
99 wp->nhdl_max = INIT_HDLS;
100 wp->nhdl = 0;
101 }
102 }
103
104 static void
walk_fini(fm_cmi_walk_t * wp)105 walk_fini(fm_cmi_walk_t *wp)
106 {
107 if (wp->cbfunc == NULL)
108 kmem_free(wp->hdls, sizeof (cmi_hdl_t) * wp->nhdl_max);
109 }
110
111 static int
select_cmi_hdl(cmi_hdl_t hdl,void * arg1,void * arg2,void * arg3)112 select_cmi_hdl(cmi_hdl_t hdl, void *arg1, void *arg2, void *arg3)
113 {
114 fm_cmi_walk_t *wp = (fm_cmi_walk_t *)arg1;
115
116 if (wp->chipid != ANY_ID && wp->chipid != cmi_hdl_chipid(hdl))
117 return (CMI_HDL_WALK_NEXT);
118 if (wp->coreid != ANY_ID && wp->coreid != cmi_hdl_coreid(hdl))
119 return (CMI_HDL_WALK_NEXT);
120 if (wp->strandid != ANY_ID && wp->strandid != cmi_hdl_strandid(hdl))
121 return (CMI_HDL_WALK_NEXT);
122
123 /*
124 * Call the callback function if any exists, otherwise we hold a
125 * reference of the handle and push it to preallocated array.
126 * If the allocated array is going to overflow, reallocate a
127 * bigger one to replace it.
128 */
129 if (wp->cbfunc != NULL)
130 return (wp->cbfunc(hdl, arg2, arg3));
131
132 if (wp->nhdl == wp->nhdl_max) {
133 size_t sz = sizeof (cmi_hdl_t) * wp->nhdl_max;
134 cmi_hdl_t *newarray = kmem_alloc(sz << 1, KM_SLEEP);
135
136 bcopy(wp->hdls, newarray, sz);
137 kmem_free(wp->hdls, sz);
138 wp->hdls = newarray;
139 wp->nhdl_max <<= 1;
140 }
141
142 cmi_hdl_hold(hdl);
143 wp->hdls[wp->nhdl++] = hdl;
144
145 return (CMI_HDL_WALK_NEXT);
146 }
147
148 static void
populate_cpu(nvlist_t ** nvlp,cmi_hdl_t hdl)149 populate_cpu(nvlist_t **nvlp, cmi_hdl_t hdl)
150 {
151 uint_t fm_chipid;
152 uint16_t smbios_id;
153
154 (void) nvlist_alloc(nvlp, NV_UNIQUE_NAME, KM_SLEEP);
155
156 /*
157 * If SMBIOS satisfies FMA Topology needs, gather
158 * more information on the chip's physical roots
159 * like /chassis=x/motherboard=y/cpuboard=z and
160 * set the chip_id to match the SMBIOS' Type 4
161 * ordering & this has to match the ereport's chip
162 * resource instance derived off of SMBIOS.
163 * Multi-Chip-Module support should set the chipid
164 * in terms of the processor package rather than
165 * the die/node in the processor package, for FM.
166 */
167
168 if (!x86gentopo_legacy) {
169 smbios_id = cmi_hdl_smbiosid(hdl);
170 fm_chipid = cmi_hdl_smb_chipid(hdl);
171 (void) nvlist_add_nvlist(*nvlp, FM_PHYSCPU_INFO_CHIP_ROOTS,
172 cmi_hdl_smb_bboard(hdl));
173 (void) nvlist_add_uint16(*nvlp, FM_PHYSCPU_INFO_SMBIOS_ID,
174 (uint16_t)smbios_id);
175 } else
176 fm_chipid = cmi_hdl_chipid(hdl);
177
178 fm_payload_set(*nvlp,
179 FM_PHYSCPU_INFO_VENDOR_ID, DATA_TYPE_STRING,
180 cmi_hdl_vendorstr(hdl),
181 FM_PHYSCPU_INFO_FAMILY, DATA_TYPE_INT32,
182 (int32_t)cmi_hdl_family(hdl),
183 FM_PHYSCPU_INFO_MODEL, DATA_TYPE_INT32,
184 (int32_t)cmi_hdl_model(hdl),
185 FM_PHYSCPU_INFO_STEPPING, DATA_TYPE_INT32,
186 (int32_t)cmi_hdl_stepping(hdl),
187 FM_PHYSCPU_INFO_CHIP_ID, DATA_TYPE_INT32,
188 (int32_t)fm_chipid,
189 FM_PHYSCPU_INFO_NPROCNODES, DATA_TYPE_INT32,
190 (int32_t)cmi_hdl_procnodes_per_pkg(hdl),
191 FM_PHYSCPU_INFO_PROCNODE_ID, DATA_TYPE_INT32,
192 (int32_t)cmi_hdl_procnodeid(hdl),
193 FM_PHYSCPU_INFO_CORE_ID, DATA_TYPE_INT32,
194 (int32_t)cmi_hdl_coreid(hdl),
195 FM_PHYSCPU_INFO_STRAND_ID, DATA_TYPE_INT32,
196 (int32_t)cmi_hdl_strandid(hdl),
197 FM_PHYSCPU_INFO_STRAND_APICID, DATA_TYPE_INT32,
198 (int32_t)cmi_hdl_strand_apicid(hdl),
199 FM_PHYSCPU_INFO_CHIP_REV, DATA_TYPE_STRING,
200 cmi_hdl_chiprevstr(hdl),
201 FM_PHYSCPU_INFO_SOCKET_TYPE, DATA_TYPE_UINT32,
202 (uint32_t)cmi_hdl_getsockettype(hdl),
203 FM_PHYSCPU_INFO_CPU_ID, DATA_TYPE_INT32,
204 (int32_t)cmi_hdl_logical_id(hdl),
205 NULL);
206 }
207
208 /*ARGSUSED*/
209 int
fm_ioctl_physcpu_info(int cmd,nvlist_t * invl,nvlist_t ** onvlp)210 fm_ioctl_physcpu_info(int cmd, nvlist_t *invl, nvlist_t **onvlp)
211 {
212 nvlist_t **cpus, *nvl;
213 int i, err;
214 fm_cmi_walk_t wk;
215
216 /*
217 * Do a walk to save all the cmi handles in the array.
218 */
219 walk_init(&wk, ANY_ID, ANY_ID, ANY_ID, NULL);
220 cmi_hdl_walk(select_cmi_hdl, &wk, NULL, NULL);
221
222 if (wk.nhdl == 0) {
223 walk_fini(&wk);
224 return (ENOENT);
225 }
226
227 cpus = kmem_alloc(sizeof (nvlist_t *) * wk.nhdl, KM_SLEEP);
228 for (i = 0; i < wk.nhdl; i++) {
229 populate_cpu(cpus + i, wk.hdls[i]);
230 cmi_hdl_rele(wk.hdls[i]);
231 }
232
233 walk_fini(&wk);
234
235 (void) nvlist_alloc(&nvl, NV_UNIQUE_NAME, KM_SLEEP);
236 err = nvlist_add_nvlist_array(nvl, FM_PHYSCPU_INFO_CPUS,
237 cpus, wk.nhdl);
238
239 for (i = 0; i < wk.nhdl; i++)
240 nvlist_free(cpus[i]);
241 kmem_free(cpus, sizeof (nvlist_t *) * wk.nhdl);
242
243 if (err != 0) {
244 nvlist_free(nvl);
245 return (err);
246 }
247
248 *onvlp = nvl;
249 return (0);
250 }
251
252 int
fm_ioctl_cpu_retire(int cmd,nvlist_t * invl,nvlist_t ** onvlp)253 fm_ioctl_cpu_retire(int cmd, nvlist_t *invl, nvlist_t **onvlp)
254 {
255 int32_t chipid, coreid, strandid;
256 int rc, new_status, old_status;
257 cmi_hdl_t hdl;
258 nvlist_t *nvl;
259
260 switch (cmd) {
261 case FM_IOC_CPU_RETIRE:
262 new_status = P_FAULTED;
263 break;
264 case FM_IOC_CPU_STATUS:
265 new_status = P_STATUS;
266 break;
267 case FM_IOC_CPU_UNRETIRE:
268 new_status = P_ONLINE;
269 break;
270 default:
271 return (ENOTTY);
272 }
273
274 if (nvlist_lookup_int32(invl, FM_CPU_RETIRE_CHIP_ID, &chipid) != 0 ||
275 nvlist_lookup_int32(invl, FM_CPU_RETIRE_CORE_ID, &coreid) != 0 ||
276 nvlist_lookup_int32(invl, FM_CPU_RETIRE_STRAND_ID, &strandid) != 0)
277 return (EINVAL);
278
279 hdl = cmi_hdl_lookup(CMI_HDL_NEUTRAL, chipid, coreid, strandid);
280 if (hdl == NULL)
281 return (EINVAL);
282
283 rc = cmi_hdl_online(hdl, new_status, &old_status);
284 cmi_hdl_rele(hdl);
285
286 if (rc == 0) {
287 (void) nvlist_alloc(&nvl, NV_UNIQUE_NAME, KM_SLEEP);
288 (void) nvlist_add_int32(nvl, FM_CPU_RETIRE_OLDSTATUS,
289 old_status);
290 *onvlp = nvl;
291 }
292
293 return (rc);
294 }
295
296 /*
297 * Retrun the value of x86gentopo_legacy variable as an nvpair.
298 *
299 * The caller is responsible for freeing the nvlist.
300 */
301 /* ARGSUSED */
302 int
fm_ioctl_gentopo_legacy(int cmd,nvlist_t * invl,nvlist_t ** onvlp)303 fm_ioctl_gentopo_legacy(int cmd, nvlist_t *invl, nvlist_t **onvlp)
304 {
305 nvlist_t *nvl;
306
307 if (cmd != FM_IOC_GENTOPO_LEGACY) {
308 return (ENOTTY);
309 }
310
311 /*
312 * Inform the caller of the intentions of the ereport generators to
313 * generate either a "generic" or "legacy" x86 topology.
314 */
315
316 (void) nvlist_alloc(&nvl, NV_UNIQUE_NAME, KM_SLEEP);
317 (void) nvlist_add_int32(nvl, FM_GENTOPO_LEGACY, x86gentopo_legacy);
318 *onvlp = nvl;
319
320 return (0);
321 }
322