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 176 if (nvlist_lookup_uint8(nvl, FM_VERSION, &version) != 0 || 177 nvlist_lookup_uint32(nvl, FM_FMRI_CPU_ID, &cpuid) != 0) 178 return (fmd_fmri_set_errno(EINVAL)); 179 180 /* 181 * If the cpu-scheme topology exports this method expand(), invoke it. 182 */ 183 rc = topo_fmri_expand(fmd_fmri_topology(TOPO_VERSION), nvl, &err); 184 if (err != ETOPO_METHOD_NOTSUP) 185 return (rc); 186 187 if (version == CPU_SCHEME_VERSION0) { 188 if ((rc = nvlist_lookup_uint64(nvl, FM_FMRI_CPU_SERIAL_ID, 189 &serialid)) != 0) { 190 if (rc != ENOENT) 191 return (fmd_fmri_set_errno(rc)); 192 193 if (cpu_get_serialid_V0(cpuid, &serialid) != 0) 194 return (-1); /* errno is set for us */ 195 196 if ((rc = nvlist_add_uint64(nvl, FM_FMRI_CPU_SERIAL_ID, 197 serialid)) != 0) 198 return (fmd_fmri_set_errno(rc)); 199 } 200 } else if (version == CPU_SCHEME_VERSION1) { 201 if ((rc = nvlist_lookup_string(nvl, FM_FMRI_CPU_SERIAL_ID, 202 &serstr)) != 0) { 203 if (rc != ENOENT) 204 return (fmd_fmri_set_errno(rc)); 205 206 if (cpu_get_serialid_V1(cpuid, serbuf, 21) != 0) 207 return (0); /* Serial number is optional */ 208 209 if ((rc = nvlist_add_string(nvl, FM_FMRI_CPU_SERIAL_ID, 210 serbuf)) != 0) 211 return (fmd_fmri_set_errno(rc)); 212 } 213 } else { 214 return (fmd_fmri_set_errno(EINVAL)); 215 } 216 217 return (0); 218 } 219 220 int 221 fmd_fmri_present(nvlist_t *nvl) 222 { 223 int rc, err; 224 uint8_t version; 225 uint32_t cpuid; 226 uint64_t nvlserid, curserid; 227 char *nvlserstr, curserbuf[21]; /* sizeof (UINT64_MAX) + '\0' */ 228 229 if (nvlist_lookup_uint8(nvl, FM_VERSION, &version) != 0 || 230 nvlist_lookup_uint32(nvl, FM_FMRI_CPU_ID, &cpuid) != 0) 231 return (fmd_fmri_set_errno(EINVAL)); 232 233 /* 234 * If the cpu-scheme topology exports this method present(), invoke it. 235 */ 236 rc = topo_fmri_present(fmd_fmri_topology(TOPO_VERSION), nvl, &err); 237 if (err != ETOPO_METHOD_NOTSUP) 238 return (rc); 239 240 if (version == CPU_SCHEME_VERSION0) { 241 if (nvlist_lookup_uint64(nvl, FM_FMRI_CPU_SERIAL_ID, 242 &nvlserid) != 0) 243 return (fmd_fmri_set_errno(EINVAL)); 244 if (cpu_get_serialid_V0(cpuid, &curserid) != 0) 245 return (errno == ENOENT ? 0 : -1); 246 247 return (curserid == nvlserid); 248 249 } else if (version == CPU_SCHEME_VERSION1) { 250 if ((rc = nvlist_lookup_string(nvl, FM_FMRI_CPU_SERIAL_ID, 251 &nvlserstr)) != 0) 252 if (rc != ENOENT) 253 return (fmd_fmri_set_errno(EINVAL)); 254 255 /* 256 * If serial id is not available, just check if the cpuid 257 * is present. 258 */ 259 if (cpu_get_serialid_V1(cpuid, curserbuf, 21) != 0) 260 return (cpu_cpuid_present(cpuid)); 261 262 return (strcmp(curserbuf, nvlserstr) == 0 ? 1 : 0); 263 264 } else { 265 return (fmd_fmri_set_errno(EINVAL)); 266 } 267 } 268 269 int 270 fmd_fmri_unusable(nvlist_t *nvl) 271 { 272 int rc, err; 273 uint8_t version; 274 uint32_t cpuid; 275 276 if (nvlist_lookup_uint8(nvl, FM_VERSION, &version) != 0 || 277 version > FM_CPU_SCHEME_VERSION || 278 nvlist_lookup_uint32(nvl, FM_FMRI_CPU_ID, &cpuid) != 0) 279 return (fmd_fmri_set_errno(EINVAL)); 280 281 /* 282 * If the cpu-scheme topology exports this method unusable(), invoke it. 283 */ 284 rc = topo_fmri_unusable(fmd_fmri_topology(TOPO_VERSION), nvl, &err); 285 if (err != ETOPO_METHOD_NOTSUP) 286 return (rc); 287 288 return (p_online(cpuid, P_STATUS) == P_FAULTED); 289 } 290