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 (c) 2010, Oracle and/or its affiliates. All rights reserved. 24 */ 25 26 /* 27 * FMA capability messenger 28 * 29 * fdd-msg module is called once when fmd starts up. It does the following 30 * based on different scenarios 31 * 32 * 1. If it's on a x86 platform, fdd-msg module sends fdd running on service 33 * processor a message (ILOM) which indicates the Solaris host FMA capability. 34 * The message is sent via the BMC driver (KCS interface) to the IPMI stack 35 * of ILOM using the IPMI Sun OEM core tunnel command. The sub-command is 36 * CORE_TUNNEL_SUBCMD_HOSTCAP. The IPMI stack posts an host FMA capability 37 * event to the event manager upon receiving this message. fdd subscribes to 38 * the event manager for this event. Upon receving this event, fdd will adjust 39 * its configuration. 40 * 41 * 2. If it's on a Sparc platform, fdd-msg module just exit for now. 42 */ 43 44 #include <errno.h> 45 #include <stdio.h> 46 #include <strings.h> 47 #include <sys/systeminfo.h> 48 #include <libipmi.h> 49 #include <sys/devfm.h> 50 #include <fm/fmd_api.h> 51 #if defined(__x86) 52 #include <sys/x86_archext.h> 53 #include <fm/fmd_agent.h> 54 #include <libnvpair.h> 55 #endif 56 57 #define CMD_SUNOEM_CORE_TUNNEL 0x44 58 #define CORE_TUNNEL_SUBCMD_HOSTFMACAP 2 59 #define OEM_DATA_LENGTH 3 60 #define VERSION 0x10 61 62 #if defined(__x86) 63 typedef struct cpu_tbl { 64 char vendor[X86_VENDOR_STRLEN]; 65 int32_t family; 66 int32_t model; 67 char *propname; 68 } cpu_tbl_t; 69 70 static cpu_tbl_t fma_cap_list[] = { 71 {"GenuineIntel", 6, 26, "NHMEP_fma_cap"}, 72 {"GenuineIntel", 6, 46, "NHMEX_fma_cap"}, 73 {"GenuineIntel", 6, 44, "WSMEP_fma_cap"}, 74 {"GenuineIntel", 6, 47, "INTLN_fma_cap"}, 75 {0, 0, 0, 0} 76 }; 77 #endif 78 79 static int 80 check_sunoem(ipmi_handle_t *ipmi_hdl) 81 { 82 ipmi_deviceid_t *devid; 83 84 if ((devid = ipmi_get_deviceid(ipmi_hdl)) == NULL) 85 return (-1); 86 87 if (!ipmi_is_sun_ilom(devid)) 88 return (-2); 89 90 return (0); 91 } 92 93 #if defined(__x86) 94 static int32_t 95 fma_cap_cpu_info(cpu_tbl_t *ci) 96 { 97 nvlist_t **cpus, *nvl; 98 uint_t ncpu, i; 99 fmd_agent_hdl_t *hdl; 100 char *ven; 101 int32_t family, model; 102 103 if ((hdl = fmd_agent_open(FMD_AGENT_VERSION)) == NULL) 104 return (-1); 105 if (fmd_agent_physcpu_info(hdl, &cpus, &ncpu) != 0) { 106 fmd_agent_close(hdl); 107 return (-1); 108 } 109 fmd_agent_close(hdl); 110 111 if (cpus == NULL) 112 return (-1); 113 114 /* 115 * There is no mixed CPU type on x86 systems, it's ok to 116 * just pick the first one 117 */ 118 nvl = cpus[0]; 119 if (nvlist_lookup_string(nvl, FM_PHYSCPU_INFO_VENDOR_ID, &ven) != 0 || 120 nvlist_lookup_int32(nvl, FM_PHYSCPU_INFO_FAMILY, &family) != 0 || 121 nvlist_lookup_int32(nvl, FM_PHYSCPU_INFO_MODEL, &model) != 0) { 122 for (i = 0; i < ncpu; i++) 123 nvlist_free(cpus[i]); 124 umem_free(cpus, sizeof (nvlist_t *) * ncpu); 125 return (-1); 126 } 127 128 (void) snprintf(ci->vendor, X86_VENDOR_STRLEN, "%s", ven); 129 ci->family = family; 130 ci->model = model; 131 132 for (i = 0; i < ncpu; i++) 133 nvlist_free(cpus[i]); 134 umem_free(cpus, sizeof (nvlist_t *) * ncpu); 135 return (0); 136 } 137 #endif 138 139 static uint32_t 140 get_cap_conf(fmd_hdl_t *hdl) 141 { 142 uint32_t fma_cap; 143 #if defined(__x86) 144 int found = 0; 145 cpu_tbl_t *cl, ci; 146 147 if (fma_cap_cpu_info(&ci) == 0) { 148 fmd_hdl_debug(hdl, "Got CPU info: vendor=%s, family=%d, " 149 "model=%d\n", ci.vendor, ci.family, ci.model); 150 for (cl = fma_cap_list; cl->propname != NULL; cl++) { 151 if (strncmp(ci.vendor, cl->vendor, 152 X86_VENDOR_STRLEN) == 0 && 153 ci.family == cl->family && 154 ci.model == cl->model) { 155 found++; 156 break; 157 } 158 } 159 } else { 160 fmd_hdl_debug(hdl, "Failed to get CPU info"); 161 } 162 163 if (found) { 164 fma_cap = fmd_prop_get_int32(hdl, cl->propname); 165 fmd_hdl_debug(hdl, "Found property, FMA capability=0x%x", 166 fma_cap); 167 } else { 168 #endif 169 fma_cap = fmd_prop_get_int32(hdl, "default_fma_cap"); 170 fmd_hdl_debug(hdl, "Didn't find FMA capability property, " 171 "use default=0x%x", fma_cap); 172 #if defined(__x86) 173 } 174 #endif 175 176 return (fma_cap); 177 } 178 179 static void 180 send_fma_cap_to_ilom(fmd_hdl_t *hdl, uint32_t fma_cap) 181 { 182 int error; 183 char *msg; 184 ipmi_handle_t *ipmi_hdl; 185 ipmi_cmd_t cmd; 186 uint8_t oem_data[OEM_DATA_LENGTH]; 187 188 if ((ipmi_hdl = ipmi_open(&error, &msg, IPMI_TRANSPORT_BMC, NULL)) 189 == NULL) { 190 /* 191 * If /dev/ipmi0 doesn't exist on the system, then return 192 * without doing anything. 193 */ 194 if (error != EIPMI_BMC_OPEN_FAILED) 195 fmd_hdl_abort(hdl, "Failed to initialize IPMI " 196 "connection: %s\n", msg); 197 fmd_hdl_debug(hdl, "Failed: no IPMI connection present"); 198 return; 199 } 200 201 /* 202 * Check if it's Sun ILOM 203 */ 204 if (check_sunoem(ipmi_hdl) != 0) { 205 fmd_hdl_debug(hdl, "Service Processor does not run " 206 "Sun ILOM"); 207 ipmi_close(ipmi_hdl); 208 return; 209 } 210 211 oem_data[0] = CORE_TUNNEL_SUBCMD_HOSTFMACAP; 212 oem_data[1] = VERSION; 213 oem_data[2] = fma_cap; 214 215 cmd.ic_netfn = IPMI_NETFN_OEM; 216 cmd.ic_lun = 0; 217 cmd.ic_cmd = CMD_SUNOEM_CORE_TUNNEL; 218 cmd.ic_dlen = OEM_DATA_LENGTH; 219 cmd.ic_data = oem_data; 220 221 if (ipmi_send(ipmi_hdl, &cmd) == NULL) { 222 fmd_hdl_debug(hdl, "Failed to send Solaris FMA " 223 "capability to ilom: %s", ipmi_errmsg(ipmi_hdl)); 224 } 225 226 ipmi_close(ipmi_hdl); 227 } 228 229 /*ARGSUSED*/ 230 static void 231 fma_cap_init(fmd_hdl_t *hdl, id_t id, void *data) 232 { 233 uint32_t fma_cap; 234 235 fma_cap = get_cap_conf(hdl); 236 send_fma_cap_to_ilom(hdl, fma_cap); 237 238 fmd_hdl_unregister(hdl); 239 } 240 241 static const fmd_hdl_ops_t fmd_ops = { 242 NULL, /* fmdo_recv */ 243 fma_cap_init, /* fmdo_timeout */ 244 NULL, /* fmdo_close */ 245 NULL, /* fmdo_stats */ 246 NULL, /* fmdo_gc */ 247 NULL, /* fmdo_send */ 248 NULL, /* fmdo_topo */ 249 }; 250 251 static const fmd_prop_t fmd_props[] = { 252 { "interval", FMD_TYPE_TIME, "1s" }, 253 { "default_fma_cap", FMD_TYPE_UINT32, "0x3" }, 254 { "NHMEP_fma_cap", FMD_TYPE_UINT32, "0x3" }, 255 { "NHMEX_fma_cap", FMD_TYPE_UINT32, "0x2" }, 256 { "WSMEP_fma_cap", FMD_TYPE_UINT32, "0x3" }, 257 { "INTLN_fma_cap", FMD_TYPE_UINT32, "0x2" }, 258 { NULL, 0, NULL } 259 }; 260 261 static const fmd_hdl_info_t fmd_info = { 262 "FMA Capability Messenger", "1.1", &fmd_ops, fmd_props 263 }; 264 265 void 266 _fmd_init(fmd_hdl_t *hdl) 267 { 268 char isa[8]; 269 270 /* 271 * For now the module only sends message to ILOM on i386 platforms 272 * till CR 6933053 is fixed. Module unregister may cause etm module 273 * core dump due to 6933053. 274 */ 275 if ((sysinfo(SI_ARCHITECTURE, isa, sizeof (isa)) == -1) || 276 (strncmp(isa, "i386", 4) != 0)) 277 return; 278 279 if (fmd_hdl_register(hdl, FMD_API_VERSION, &fmd_info) != 0) 280 return; 281 282 /* 283 * Setup the timer. 284 */ 285 (void) fmd_timer_install(hdl, NULL, NULL, 2000000000ULL); 286 } 287 288 /*ARGSUSED*/ 289 void 290 _fmd_fini(fmd_hdl_t *hdl) 291 { 292 } 293