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 2007 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 25 */ 26 27 #pragma ident "%Z%%M% %I% %E% SMI" 28 29 #include <sys/types.h> 30 #include <sys/processor.h> 31 #include <fm/fmd_fmri.h> 32 #include <fm/libtopo.h> 33 34 #include <strings.h> 35 #include <errno.h> 36 #include <kstat.h> 37 38 /* 39 * The scheme plugin for cpu FMRIs. 40 */ 41 42 ssize_t 43 fmd_fmri_nvl2str(nvlist_t *nvl, char *buf, size_t buflen) 44 { 45 int err; 46 uint8_t version; 47 uint32_t cpuid; 48 uint64_t serint; 49 char *serstr; 50 51 if (nvlist_lookup_uint8(nvl, FM_VERSION, &version) != 0) 52 return (fmd_fmri_set_errno(EINVAL)); 53 54 if (version == CPU_SCHEME_VERSION0) { 55 if (nvlist_lookup_uint32(nvl, FM_FMRI_CPU_ID, &cpuid) != 0 || 56 nvlist_lookup_uint64(nvl, FM_FMRI_CPU_SERIAL_ID, &serint) 57 != 0) 58 return (fmd_fmri_set_errno(EINVAL)); 59 60 return (snprintf(buf, buflen, "cpu:///%s=%u/%s=%llX", 61 FM_FMRI_CPU_ID, cpuid, FM_FMRI_CPU_SERIAL_ID, 62 (u_longlong_t)serint)); 63 64 } else if (version == CPU_SCHEME_VERSION1) { 65 if (nvlist_lookup_uint32(nvl, FM_FMRI_CPU_ID, &cpuid) != 0) 66 return (fmd_fmri_set_errno(EINVAL)); 67 68 /* 69 * Serial number is an optional element 70 */ 71 if ((err = nvlist_lookup_string(nvl, FM_FMRI_CPU_SERIAL_ID, 72 &serstr)) != 0) 73 if (err == ENOENT) 74 return (snprintf(buf, buflen, "cpu:///%s=%u", 75 FM_FMRI_CPU_ID, cpuid)); 76 else 77 return (fmd_fmri_set_errno(EINVAL)); 78 else 79 return (snprintf(buf, buflen, "cpu:///%s=%u/%s=%s", 80 FM_FMRI_CPU_ID, cpuid, FM_FMRI_CPU_SERIAL_ID, 81 serstr)); 82 83 } else { 84 return (fmd_fmri_set_errno(EINVAL)); 85 } 86 } 87 88 /* 89 * Determine if a cpuid is present. 90 */ 91 /*ARGSUSED*/ 92 static int 93 cpu_cpuid_present(uint32_t cpuid) 94 { 95 #ifdef sparc 96 /* 97 * For SPARC, use kstats to see if the cpuid is present. 98 * Note that this may need to change for sun4v. 99 */ 100 kstat_ctl_t *kc; 101 kstat_t *ksp = NULL; 102 if ((kc = kstat_open()) == NULL) 103 return (-1); /* errno is set for us */ 104 ksp = kstat_lookup(kc, "cpu_info", cpuid, NULL); 105 (void) kstat_close(kc); 106 return ((ksp == NULL) ? 0 : 1); 107 #else /* sparc */ 108 /* 109 * For x64, just return true. 110 */ 111 return (1); 112 #endif /* sparc */ 113 } 114 115 static int 116 cpu_get_serialid_kstat(uint32_t cpuid, uint64_t *serialidp) 117 { 118 kstat_named_t *kn; 119 kstat_ctl_t *kc; 120 kstat_t *ksp; 121 int i; 122 123 if ((kc = kstat_open()) == NULL) 124 return (-1); /* errno is set for us */ 125 126 if ((ksp = kstat_lookup(kc, "cpu_info", cpuid, NULL)) == NULL) { 127 (void) kstat_close(kc); 128 return (fmd_fmri_set_errno(ENOENT)); 129 } 130 131 if (kstat_read(kc, ksp, NULL) == -1) { 132 int oserr = errno; 133 (void) kstat_close(kc); 134 return (fmd_fmri_set_errno(oserr)); 135 } 136 137 for (kn = ksp->ks_data, i = 0; i < ksp->ks_ndata; i++, kn++) { 138 if (strcmp(kn->name, "device_ID") == 0) { 139 *serialidp = kn->value.ui64; 140 (void) kstat_close(kc); 141 return (0); 142 } 143 } 144 145 (void) kstat_close(kc); 146 return (fmd_fmri_set_errno(ENOENT)); 147 } 148 149 static int 150 cpu_get_serialid_V1(uint32_t cpuid, char *serbuf, size_t len) 151 { 152 int err; 153 uint64_t serial = 0; 154 155 err = cpu_get_serialid_kstat(cpuid, &serial); 156 157 (void) snprintf(serbuf, len, "%llX", (u_longlong_t)serial); 158 return (err); 159 } 160 161 static int 162 cpu_get_serialid_V0(uint32_t cpuid, uint64_t *serialidp) 163 { 164 return (cpu_get_serialid_kstat(cpuid, serialidp)); 165 } 166 167 int 168 fmd_fmri_expand(nvlist_t *nvl) 169 { 170 uint8_t version; 171 uint32_t cpuid; 172 uint64_t serialid; 173 char *serstr, serbuf[21]; /* sizeof (UINT64_MAX) + '\0' */ 174 int rc, err; 175 topo_hdl_t *thp; 176 177 if (nvlist_lookup_uint8(nvl, FM_VERSION, &version) != 0 || 178 nvlist_lookup_uint32(nvl, FM_FMRI_CPU_ID, &cpuid) != 0) 179 return (fmd_fmri_set_errno(EINVAL)); 180 181 /* 182 * If the cpu-scheme topology exports this method expand(), invoke it. 183 */ 184 if ((thp = fmd_fmri_topo_hold(TOPO_VERSION)) == NULL) 185 return (fmd_fmri_set_errno(EINVAL)); 186 187 rc = topo_fmri_expand(thp, nvl, &err); 188 fmd_fmri_topo_rele(thp); 189 if (err != ETOPO_METHOD_NOTSUP) 190 return (rc); 191 192 if (version == CPU_SCHEME_VERSION0) { 193 if ((rc = nvlist_lookup_uint64(nvl, FM_FMRI_CPU_SERIAL_ID, 194 &serialid)) != 0) { 195 if (rc != ENOENT) 196 return (fmd_fmri_set_errno(rc)); 197 198 if (cpu_get_serialid_V0(cpuid, &serialid) != 0) 199 return (-1); /* errno is set for us */ 200 201 if ((rc = nvlist_add_uint64(nvl, FM_FMRI_CPU_SERIAL_ID, 202 serialid)) != 0) 203 return (fmd_fmri_set_errno(rc)); 204 } 205 } else if (version == CPU_SCHEME_VERSION1) { 206 if ((rc = nvlist_lookup_string(nvl, FM_FMRI_CPU_SERIAL_ID, 207 &serstr)) != 0) { 208 if (rc != ENOENT) 209 return (fmd_fmri_set_errno(rc)); 210 211 if (cpu_get_serialid_V1(cpuid, serbuf, 21) != 0) 212 return (0); /* Serial number is optional */ 213 214 if ((rc = nvlist_add_string(nvl, FM_FMRI_CPU_SERIAL_ID, 215 serbuf)) != 0) 216 return (fmd_fmri_set_errno(rc)); 217 } 218 } else { 219 return (fmd_fmri_set_errno(EINVAL)); 220 } 221 222 return (0); 223 } 224 225 int 226 fmd_fmri_present(nvlist_t *nvl) 227 { 228 int rc, err; 229 uint8_t version; 230 uint32_t cpuid; 231 uint64_t nvlserid, curserid; 232 char *nvlserstr, curserbuf[21]; /* sizeof (UINT64_MAX) + '\0' */ 233 topo_hdl_t *thp; 234 235 if (nvlist_lookup_uint8(nvl, FM_VERSION, &version) != 0 || 236 nvlist_lookup_uint32(nvl, FM_FMRI_CPU_ID, &cpuid) != 0) 237 return (fmd_fmri_set_errno(EINVAL)); 238 239 /* 240 * If the cpu-scheme topology exports this method present(), invoke it. 241 */ 242 if ((thp = fmd_fmri_topo_hold(TOPO_VERSION)) == NULL) 243 return (fmd_fmri_set_errno(EINVAL)); 244 rc = topo_fmri_present(thp, nvl, &err); 245 fmd_fmri_topo_rele(thp); 246 if (err != ETOPO_METHOD_NOTSUP) 247 return (rc); 248 249 if (version == CPU_SCHEME_VERSION0) { 250 if (nvlist_lookup_uint64(nvl, FM_FMRI_CPU_SERIAL_ID, 251 &nvlserid) != 0) 252 return (fmd_fmri_set_errno(EINVAL)); 253 if (cpu_get_serialid_V0(cpuid, &curserid) != 0) 254 return (errno == ENOENT ? 0 : -1); 255 256 return (curserid == nvlserid); 257 258 } else if (version == CPU_SCHEME_VERSION1) { 259 if ((rc = nvlist_lookup_string(nvl, FM_FMRI_CPU_SERIAL_ID, 260 &nvlserstr)) != 0) 261 if (rc != ENOENT) 262 return (fmd_fmri_set_errno(EINVAL)); 263 264 /* 265 * If serial id is not available, just check if the cpuid 266 * is present. 267 */ 268 if (cpu_get_serialid_V1(cpuid, curserbuf, 21) != 0) 269 return (cpu_cpuid_present(cpuid)); 270 271 return (strcmp(curserbuf, nvlserstr) == 0 ? 1 : 0); 272 273 } else { 274 return (fmd_fmri_set_errno(EINVAL)); 275 } 276 } 277 278 int 279 fmd_fmri_unusable(nvlist_t *nvl) 280 { 281 int rc, err; 282 uint8_t version; 283 uint32_t cpuid; 284 topo_hdl_t *thp; 285 286 if (nvlist_lookup_uint8(nvl, FM_VERSION, &version) != 0 || 287 version > FM_CPU_SCHEME_VERSION || 288 nvlist_lookup_uint32(nvl, FM_FMRI_CPU_ID, &cpuid) != 0) 289 return (fmd_fmri_set_errno(EINVAL)); 290 291 /* 292 * If the cpu-scheme topology exports this method unusable(), invoke it. 293 */ 294 if ((thp = fmd_fmri_topo_hold(TOPO_VERSION)) == NULL) 295 return (fmd_fmri_set_errno(EINVAL)); 296 rc = topo_fmri_unusable(thp, nvl, &err); 297 fmd_fmri_topo_rele(thp); 298 if (err != ETOPO_METHOD_NOTSUP) 299 return (rc); 300 301 return (p_online(cpuid, P_STATUS) == P_FAULTED); 302 } 303