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