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_CORE_ID, DATA_TYPE_INT32, 190 (int32_t)cmi_hdl_coreid(hdl), 191 FM_PHYSCPU_INFO_STRAND_ID, DATA_TYPE_INT32, 192 (int32_t)cmi_hdl_strandid(hdl), 193 FM_PHYSCPU_INFO_STRAND_APICID, DATA_TYPE_INT32, 194 (int32_t)cmi_hdl_strand_apicid(hdl), 195 FM_PHYSCPU_INFO_CHIP_REV, DATA_TYPE_STRING, 196 cmi_hdl_chiprevstr(hdl), 197 FM_PHYSCPU_INFO_SOCKET_TYPE, DATA_TYPE_UINT32, 198 (uint32_t)cmi_hdl_getsockettype(hdl), 199 FM_PHYSCPU_INFO_CPU_ID, DATA_TYPE_INT32, 200 (int32_t)cmi_hdl_logical_id(hdl), 201 NULL); 202 } 203 204 /*ARGSUSED*/ 205 int 206 fm_ioctl_physcpu_info(int cmd, nvlist_t *invl, nvlist_t **onvlp) 207 { 208 nvlist_t **cpus, *nvl; 209 int i, err; 210 fm_cmi_walk_t wk; 211 212 /* 213 * Do a walk to save all the cmi handles in the array. 214 */ 215 walk_init(&wk, ANY_ID, ANY_ID, ANY_ID, NULL); 216 cmi_hdl_walk(select_cmi_hdl, &wk, NULL, NULL); 217 218 if (wk.nhdl == 0) { 219 walk_fini(&wk); 220 return (ENOENT); 221 } 222 223 cpus = kmem_alloc(sizeof (nvlist_t *) * wk.nhdl, KM_SLEEP); 224 for (i = 0; i < wk.nhdl; i++) { 225 populate_cpu(cpus + i, wk.hdls[i]); 226 cmi_hdl_rele(wk.hdls[i]); 227 } 228 229 walk_fini(&wk); 230 231 (void) nvlist_alloc(&nvl, NV_UNIQUE_NAME, KM_SLEEP); 232 err = nvlist_add_nvlist_array(nvl, FM_PHYSCPU_INFO_CPUS, 233 cpus, wk.nhdl); 234 235 for (i = 0; i < wk.nhdl; i++) 236 nvlist_free(cpus[i]); 237 kmem_free(cpus, sizeof (nvlist_t *) * wk.nhdl); 238 239 if (err != 0) { 240 nvlist_free(nvl); 241 return (err); 242 } 243 244 *onvlp = nvl; 245 return (0); 246 } 247 248 int 249 fm_ioctl_cpu_retire(int cmd, nvlist_t *invl, nvlist_t **onvlp) 250 { 251 int32_t chipid, coreid, strandid; 252 int rc, new_status, old_status; 253 cmi_hdl_t hdl; 254 nvlist_t *nvl; 255 256 switch (cmd) { 257 case FM_IOC_CPU_RETIRE: 258 new_status = P_FAULTED; 259 break; 260 case FM_IOC_CPU_STATUS: 261 new_status = P_STATUS; 262 break; 263 case FM_IOC_CPU_UNRETIRE: 264 new_status = P_ONLINE; 265 break; 266 default: 267 return (ENOTTY); 268 } 269 270 if (nvlist_lookup_int32(invl, FM_CPU_RETIRE_CHIP_ID, &chipid) != 0 || 271 nvlist_lookup_int32(invl, FM_CPU_RETIRE_CORE_ID, &coreid) != 0 || 272 nvlist_lookup_int32(invl, FM_CPU_RETIRE_STRAND_ID, &strandid) != 0) 273 return (EINVAL); 274 275 hdl = cmi_hdl_lookup(CMI_HDL_NEUTRAL, chipid, coreid, strandid); 276 if (hdl == NULL) 277 return (EINVAL); 278 279 rc = cmi_hdl_online(hdl, new_status, &old_status); 280 cmi_hdl_rele(hdl); 281 282 if (rc == 0) { 283 (void) nvlist_alloc(&nvl, NV_UNIQUE_NAME, KM_SLEEP); 284 (void) nvlist_add_int32(nvl, FM_CPU_RETIRE_OLDSTATUS, 285 old_status); 286 *onvlp = nvl; 287 } 288 289 return (rc); 290 } 291 292 /* 293 * Retrun the value of x86gentopo_legacy variable as an nvpair. 294 * 295 * The caller is responsible for freeing the nvlist. 296 */ 297 /* ARGSUSED */ 298 int 299 fm_ioctl_gentopo_legacy(int cmd, nvlist_t *invl, nvlist_t **onvlp) 300 { 301 nvlist_t *nvl; 302 303 if (cmd != FM_IOC_GENTOPO_LEGACY) { 304 return (ENOTTY); 305 } 306 307 /* 308 * Inform the caller of the intentions of the ereport generators to 309 * generate either a "generic" or "legacy" x86 topology. 310 */ 311 312 (void) nvlist_alloc(&nvl, NV_UNIQUE_NAME, KM_SLEEP); 313 (void) nvlist_add_int32(nvl, FM_GENTOPO_LEGACY, x86gentopo_legacy); 314 *onvlp = nvl; 315 316 return (0); 317 } 318