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 2008 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/devfm.h> 32 33 #include <sys/cpu_module.h> 34 35 #define ANY_ID (uint_t)-1 36 37 /* 38 * INIT_HDLS is the initial size of cmi_hdl_t array. We fill the array 39 * during cmi_hdl_walk, if the array overflows, we will reallocate 40 * a new array twice the size of the old one. 41 */ 42 #define INIT_HDLS 16 43 44 typedef struct fm_cmi_walk_t 45 { 46 uint_t chipid; /* chipid to match during walk */ 47 uint_t coreid; /* coreid to match */ 48 uint_t strandid; /* strandid to match */ 49 int (*cbfunc)(cmi_hdl_t, void *, void *); /* callback function */ 50 cmi_hdl_t *hdls; /* allocated array to save the handles */ 51 int nhdl_max; /* allocated array size */ 52 int nhdl; /* handles saved */ 53 } fm_cmi_walk_t; 54 55 int 56 fm_get_paddr(nvlist_t *nvl, uint64_t *paddr) 57 { 58 uint8_t version; 59 uint64_t pa; 60 char *scheme; 61 int err; 62 63 /* Verify FMRI scheme name and version number */ 64 if ((nvlist_lookup_string(nvl, FM_FMRI_SCHEME, &scheme) != 0) || 65 (strcmp(scheme, FM_FMRI_SCHEME_HC) != 0) || 66 (nvlist_lookup_uint8(nvl, FM_VERSION, &version) != 0) || 67 version > FM_HC_SCHEME_VERSION) { 68 return (EINVAL); 69 } 70 71 if ((err = cmi_mc_unumtopa(NULL, nvl, &pa)) != CMI_SUCCESS && 72 err != CMIERR_MC_PARTIALUNUMTOPA) 73 return (EINVAL); 74 75 *paddr = pa; 76 return (0); 77 } 78 79 /* 80 * Routines for cmi handles walk. 81 */ 82 83 static void 84 walk_init(fm_cmi_walk_t *wp, uint_t chipid, uint_t coreid, uint_t strandid, 85 int (*cbfunc)(cmi_hdl_t, void *, void *)) 86 { 87 wp->chipid = chipid; 88 wp->coreid = coreid; 89 wp->strandid = strandid; 90 /* 91 * If callback is not set, we allocate an array to save the 92 * cmi handles. 93 */ 94 if ((wp->cbfunc = cbfunc) == NULL) { 95 wp->hdls = kmem_alloc(sizeof (cmi_hdl_t) * INIT_HDLS, KM_SLEEP); 96 wp->nhdl_max = INIT_HDLS; 97 wp->nhdl = 0; 98 } 99 } 100 101 static void 102 walk_fini(fm_cmi_walk_t *wp) 103 { 104 if (wp->cbfunc == NULL) 105 kmem_free(wp->hdls, sizeof (cmi_hdl_t) * wp->nhdl_max); 106 } 107 108 static int 109 select_cmi_hdl(cmi_hdl_t hdl, void *arg1, void *arg2, void *arg3) 110 { 111 fm_cmi_walk_t *wp = (fm_cmi_walk_t *)arg1; 112 113 if (wp->chipid != ANY_ID && wp->chipid != cmi_hdl_chipid(hdl)) 114 return (CMI_HDL_WALK_NEXT); 115 if (wp->coreid != ANY_ID && wp->coreid != cmi_hdl_coreid(hdl)) 116 return (CMI_HDL_WALK_NEXT); 117 if (wp->strandid != ANY_ID && wp->strandid != cmi_hdl_strandid(hdl)) 118 return (CMI_HDL_WALK_NEXT); 119 120 /* 121 * Call the callback function if any exists, otherwise we hold a 122 * reference of the handle and push it to preallocated array. 123 * If the allocated array is going to overflow, reallocate a 124 * bigger one to replace it. 125 */ 126 if (wp->cbfunc != NULL) 127 return (wp->cbfunc(hdl, arg2, arg3)); 128 129 if (wp->nhdl == wp->nhdl_max) { 130 size_t sz = sizeof (cmi_hdl_t) * wp->nhdl_max; 131 cmi_hdl_t *newarray = kmem_alloc(sz << 1, KM_SLEEP); 132 133 bcopy(wp->hdls, newarray, sz); 134 kmem_free(wp->hdls, sz); 135 wp->hdls = newarray; 136 wp->nhdl_max <<= 1; 137 } 138 139 cmi_hdl_hold(hdl); 140 wp->hdls[wp->nhdl++] = hdl; 141 142 return (CMI_HDL_WALK_NEXT); 143 } 144 145 static void 146 populate_cpu(nvlist_t **nvlp, cmi_hdl_t hdl) 147 { 148 (void) nvlist_alloc(nvlp, NV_UNIQUE_NAME, KM_SLEEP); 149 fm_payload_set(*nvlp, 150 FM_PHYSCPU_INFO_VENDOR_ID, DATA_TYPE_STRING, 151 cmi_hdl_vendorstr(hdl), 152 FM_PHYSCPU_INFO_FAMILY, DATA_TYPE_INT32, 153 (int32_t)cmi_hdl_family(hdl), 154 FM_PHYSCPU_INFO_MODEL, DATA_TYPE_INT32, 155 (int32_t)cmi_hdl_model(hdl), 156 FM_PHYSCPU_INFO_STEPPING, DATA_TYPE_INT32, 157 (int32_t)cmi_hdl_stepping(hdl), 158 FM_PHYSCPU_INFO_CHIP_ID, DATA_TYPE_INT32, 159 (int32_t)cmi_hdl_chipid(hdl), 160 FM_PHYSCPU_INFO_CORE_ID, DATA_TYPE_INT32, 161 (int32_t)cmi_hdl_coreid(hdl), 162 FM_PHYSCPU_INFO_STRAND_ID, DATA_TYPE_INT32, 163 (int32_t)cmi_hdl_strandid(hdl), 164 FM_PHYSCPU_INFO_CHIP_REV, DATA_TYPE_STRING, 165 cmi_hdl_chiprevstr(hdl), 166 FM_PHYSCPU_INFO_SOCKET_TYPE, DATA_TYPE_UINT32, 167 (uint32_t)cmi_hdl_getsockettype(hdl), 168 FM_PHYSCPU_INFO_CPU_ID, DATA_TYPE_INT32, 169 (int32_t)cmi_hdl_logical_id(hdl), 170 NULL); 171 } 172 173 /*ARGSUSED*/ 174 int 175 fm_ioctl_physcpu_info(int cmd, nvlist_t *invl, nvlist_t **onvlp) 176 { 177 nvlist_t **cpus, *nvl; 178 int i, err; 179 fm_cmi_walk_t wk; 180 181 /* 182 * Do a walk to save all the cmi handles in the array. 183 */ 184 walk_init(&wk, ANY_ID, ANY_ID, ANY_ID, NULL); 185 cmi_hdl_walk(select_cmi_hdl, &wk, NULL, NULL); 186 187 if (wk.nhdl == 0) { 188 walk_fini(&wk); 189 return (ENOENT); 190 } 191 192 cpus = kmem_alloc(sizeof (nvlist_t *) * wk.nhdl, KM_SLEEP); 193 for (i = 0; i < wk.nhdl; i++) { 194 populate_cpu(cpus + i, wk.hdls[i]); 195 cmi_hdl_rele(wk.hdls[i]); 196 } 197 198 walk_fini(&wk); 199 200 (void) nvlist_alloc(&nvl, NV_UNIQUE_NAME, KM_SLEEP); 201 err = nvlist_add_nvlist_array(nvl, FM_PHYSCPU_INFO_CPUS, 202 cpus, wk.nhdl); 203 204 for (i = 0; i < wk.nhdl; i++) 205 nvlist_free(cpus[i]); 206 kmem_free(cpus, sizeof (nvlist_t *) * wk.nhdl); 207 208 if (err != 0) { 209 nvlist_free(nvl); 210 return (err); 211 } 212 213 *onvlp = nvl; 214 return (0); 215 } 216 217 int 218 fm_ioctl_cpu_retire(int cmd, nvlist_t *invl, nvlist_t **onvlp) 219 { 220 int32_t chipid, coreid, strandid; 221 int rc, new_status, old_status; 222 cmi_hdl_t hdl; 223 nvlist_t *nvl; 224 225 switch (cmd) { 226 case FM_IOC_CPU_RETIRE: 227 new_status = P_FAULTED; 228 break; 229 case FM_IOC_CPU_STATUS: 230 new_status = P_STATUS; 231 break; 232 case FM_IOC_CPU_UNRETIRE: 233 new_status = P_ONLINE; 234 break; 235 default: 236 return (ENOTTY); 237 } 238 239 if (nvlist_lookup_int32(invl, FM_CPU_RETIRE_CHIP_ID, &chipid) != 0 || 240 nvlist_lookup_int32(invl, FM_CPU_RETIRE_CORE_ID, &coreid) != 0 || 241 nvlist_lookup_int32(invl, FM_CPU_RETIRE_STRAND_ID, &strandid) != 0) 242 return (EINVAL); 243 244 hdl = cmi_hdl_lookup(CMI_HDL_NEUTRAL, chipid, coreid, strandid); 245 if (hdl == NULL) 246 return (EINVAL); 247 248 rc = cmi_hdl_online(hdl, new_status, &old_status); 249 cmi_hdl_rele(hdl); 250 251 if (rc == 0) { 252 (void) nvlist_alloc(&nvl, NV_UNIQUE_NAME, KM_SLEEP); 253 (void) nvlist_add_int32(nvl, FM_CPU_RETIRE_OLDSTATUS, 254 old_status); 255 *onvlp = nvl; 256 } 257 258 return (rc); 259 } 260