1491f61a1SYanmin Sun /*
2491f61a1SYanmin Sun  * CDDL HEADER START
3491f61a1SYanmin Sun  *
4491f61a1SYanmin Sun  * The contents of this file are subject to the terms of the
5491f61a1SYanmin Sun  * Common Development and Distribution License (the "License").
6491f61a1SYanmin Sun  * You may not use this file except in compliance with the License.
7491f61a1SYanmin Sun  *
8491f61a1SYanmin Sun  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9491f61a1SYanmin Sun  * or http://www.opensolaris.org/os/licensing.
10491f61a1SYanmin Sun  * See the License for the specific language governing permissions
11491f61a1SYanmin Sun  * and limitations under the License.
12491f61a1SYanmin Sun  *
13491f61a1SYanmin Sun  * When distributing Covered Code, include this CDDL HEADER in each
14491f61a1SYanmin Sun  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15491f61a1SYanmin Sun  * If applicable, add the following below this CDDL HEADER, with the
16491f61a1SYanmin Sun  * fields enclosed by brackets "[]" replaced with your own identifying
17491f61a1SYanmin Sun  * information: Portions Copyright [yyyy] [name of copyright owner]
18491f61a1SYanmin Sun  *
19491f61a1SYanmin Sun  * CDDL HEADER END
20491f61a1SYanmin Sun  */
21491f61a1SYanmin Sun 
22491f61a1SYanmin Sun /*
2363d763c8SLuping Quan  * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
24491f61a1SYanmin Sun  */
25491f61a1SYanmin Sun 
26491f61a1SYanmin Sun /*
2763d763c8SLuping Quan  * FMA capability messenger
28491f61a1SYanmin Sun  *
2963d763c8SLuping Quan  * fdd-msg module is called once when fmd starts up. It does the following
3063d763c8SLuping Quan  * based on different scenarios
31491f61a1SYanmin Sun  *
3263d763c8SLuping Quan  * 1. If it's on a x86 platform, fdd-msg module sends fdd running on service
3363d763c8SLuping Quan  * processor a message (ILOM) which indicates the Solaris host FMA capability.
3463d763c8SLuping Quan  * The message is sent via the BMC driver (KCS interface) to the IPMI stack
3563d763c8SLuping Quan  * of ILOM using the IPMI Sun OEM core tunnel command. The sub-command is
3663d763c8SLuping Quan  * CORE_TUNNEL_SUBCMD_HOSTCAP. The IPMI stack posts an host FMA capability
3763d763c8SLuping Quan  * event to the event manager upon receiving this message. fdd subscribes to
3863d763c8SLuping Quan  * the event manager for this event. Upon receving this event, fdd will adjust
3963d763c8SLuping Quan  * its configuration.
4063d763c8SLuping Quan  *
4163d763c8SLuping Quan  * 2. If it's on a Sparc platform, fdd-msg module just exit for now.
42491f61a1SYanmin Sun  */
43491f61a1SYanmin Sun 
44491f61a1SYanmin Sun #include <errno.h>
45491f61a1SYanmin Sun #include <stdio.h>
46491f61a1SYanmin Sun #include <strings.h>
47dfe4040dSLuping Quan #include <sys/systeminfo.h>
48491f61a1SYanmin Sun #include <libipmi.h>
4963d763c8SLuping Quan #include <sys/devfm.h>
50491f61a1SYanmin Sun #include <fm/fmd_api.h>
5163d763c8SLuping Quan #if defined(__x86)
5263d763c8SLuping Quan #include <sys/x86_archext.h>
5363d763c8SLuping Quan #include <fm/fmd_agent.h>
5463d763c8SLuping Quan #include <libnvpair.h>
5563d763c8SLuping Quan #endif
56491f61a1SYanmin Sun 
57491f61a1SYanmin Sun #define	CMD_SUNOEM_CORE_TUNNEL 0x44
58491f61a1SYanmin Sun #define	CORE_TUNNEL_SUBCMD_HOSTFMACAP 2
59491f61a1SYanmin Sun #define	OEM_DATA_LENGTH 3
60491f61a1SYanmin Sun #define	VERSION 0x10
6163d763c8SLuping Quan 
6263d763c8SLuping Quan #if defined(__x86)
6363d763c8SLuping Quan typedef struct cpu_tbl {
6463d763c8SLuping Quan 	char vendor[X86_VENDOR_STRLEN];
6563d763c8SLuping Quan 	int32_t family;
6663d763c8SLuping Quan 	int32_t model;
6763d763c8SLuping Quan 	char *propname;
6863d763c8SLuping Quan } cpu_tbl_t;
6963d763c8SLuping Quan 
7063d763c8SLuping Quan static cpu_tbl_t fma_cap_list[] = {
7163d763c8SLuping Quan 	{"GenuineIntel", 6, 26, "NHMEP_fma_cap"},
7263d763c8SLuping Quan 	{"GenuineIntel", 6, 46, "NHMEX_fma_cap"},
7363d763c8SLuping Quan 	{"GenuineIntel", 6, 44, "WSMEP_fma_cap"},
7463d763c8SLuping Quan 	{"GenuineIntel", 6, 47, "INTLN_fma_cap"},
75*940f2f58SToomas Soome 	{0, 0, 0, 0}
7663d763c8SLuping Quan };
7763d763c8SLuping Quan #endif
78491f61a1SYanmin Sun 
79491f61a1SYanmin Sun static int
check_sunoem(ipmi_handle_t * ipmi_hdl)80491f61a1SYanmin Sun check_sunoem(ipmi_handle_t *ipmi_hdl)
81491f61a1SYanmin Sun {
82491f61a1SYanmin Sun 	ipmi_deviceid_t *devid;
83491f61a1SYanmin Sun 
84491f61a1SYanmin Sun 	if ((devid = ipmi_get_deviceid(ipmi_hdl)) == NULL)
85491f61a1SYanmin Sun 		return (-1);
86491f61a1SYanmin Sun 
87491f61a1SYanmin Sun 	if (!ipmi_is_sun_ilom(devid))
88491f61a1SYanmin Sun 		return (-2);
89491f61a1SYanmin Sun 
90491f61a1SYanmin Sun 	return (0);
91491f61a1SYanmin Sun }
92491f61a1SYanmin Sun 
9363d763c8SLuping Quan #if defined(__x86)
9463d763c8SLuping Quan static int32_t
fma_cap_cpu_info(cpu_tbl_t * ci)9563d763c8SLuping Quan fma_cap_cpu_info(cpu_tbl_t *ci)
9663d763c8SLuping Quan {
9763d763c8SLuping Quan 	nvlist_t **cpus, *nvl;
9863d763c8SLuping Quan 	uint_t ncpu, i;
9963d763c8SLuping Quan 	fmd_agent_hdl_t *hdl;
10063d763c8SLuping Quan 	char *ven;
10163d763c8SLuping Quan 	int32_t family, model;
10263d763c8SLuping Quan 
10363d763c8SLuping Quan 	if ((hdl = fmd_agent_open(FMD_AGENT_VERSION)) == NULL)
10463d763c8SLuping Quan 		return (-1);
10563d763c8SLuping Quan 	if (fmd_agent_physcpu_info(hdl, &cpus, &ncpu) != 0) {
10663d763c8SLuping Quan 		fmd_agent_close(hdl);
10763d763c8SLuping Quan 		return (-1);
10863d763c8SLuping Quan 	}
10963d763c8SLuping Quan 	fmd_agent_close(hdl);
11063d763c8SLuping Quan 
11163d763c8SLuping Quan 	if (cpus == NULL)
11263d763c8SLuping Quan 		return (-1);
11363d763c8SLuping Quan 
11463d763c8SLuping Quan 	/*
11563d763c8SLuping Quan 	 * There is no mixed CPU type on x86 systems, it's ok to
11663d763c8SLuping Quan 	 * just pick the first one
11763d763c8SLuping Quan 	 */
11863d763c8SLuping Quan 	nvl = cpus[0];
11963d763c8SLuping Quan 	if (nvlist_lookup_string(nvl, FM_PHYSCPU_INFO_VENDOR_ID, &ven) != 0 ||
12063d763c8SLuping Quan 	    nvlist_lookup_int32(nvl, FM_PHYSCPU_INFO_FAMILY, &family) != 0 ||
12163d763c8SLuping Quan 	    nvlist_lookup_int32(nvl, FM_PHYSCPU_INFO_MODEL, &model) != 0) {
12263d763c8SLuping Quan 		for (i = 0; i < ncpu; i++)
12363d763c8SLuping Quan 			nvlist_free(cpus[i]);
12463d763c8SLuping Quan 		umem_free(cpus, sizeof (nvlist_t *) * ncpu);
12563d763c8SLuping Quan 		return (-1);
12663d763c8SLuping Quan 	}
12763d763c8SLuping Quan 
12863d763c8SLuping Quan 	(void) snprintf(ci->vendor, X86_VENDOR_STRLEN, "%s", ven);
12963d763c8SLuping Quan 	ci->family = family;
13063d763c8SLuping Quan 	ci->model = model;
13163d763c8SLuping Quan 
13263d763c8SLuping Quan 	for (i = 0; i < ncpu; i++)
13363d763c8SLuping Quan 		nvlist_free(cpus[i]);
13463d763c8SLuping Quan 	umem_free(cpus, sizeof (nvlist_t *) * ncpu);
13563d763c8SLuping Quan 	return (0);
13663d763c8SLuping Quan }
13763d763c8SLuping Quan #endif
13863d763c8SLuping Quan 
13963d763c8SLuping Quan static uint32_t
get_cap_conf(fmd_hdl_t * hdl)14063d763c8SLuping Quan get_cap_conf(fmd_hdl_t *hdl)
14163d763c8SLuping Quan {
14263d763c8SLuping Quan 	uint32_t fma_cap;
14363d763c8SLuping Quan #if defined(__x86)
14463d763c8SLuping Quan 	int found = 0;
14563d763c8SLuping Quan 	cpu_tbl_t *cl, ci;
14663d763c8SLuping Quan 
14763d763c8SLuping Quan 	if (fma_cap_cpu_info(&ci) == 0) {
14863d763c8SLuping Quan 		fmd_hdl_debug(hdl, "Got CPU info: vendor=%s, family=%d, "
14963d763c8SLuping Quan 		    "model=%d\n", ci.vendor, ci.family, ci.model);
15063d763c8SLuping Quan 		for (cl = fma_cap_list; cl->propname != NULL; cl++) {
15163d763c8SLuping Quan 			if (strncmp(ci.vendor, cl->vendor,
15263d763c8SLuping Quan 			    X86_VENDOR_STRLEN) == 0 &&
15363d763c8SLuping Quan 			    ci.family == cl->family &&
15463d763c8SLuping Quan 			    ci.model == cl->model) {
15563d763c8SLuping Quan 				found++;
15663d763c8SLuping Quan 				break;
15763d763c8SLuping Quan 			}
15863d763c8SLuping Quan 		}
15963d763c8SLuping Quan 	} else {
16063d763c8SLuping Quan 		fmd_hdl_debug(hdl, "Failed to get CPU info");
16163d763c8SLuping Quan 	}
16263d763c8SLuping Quan 
16363d763c8SLuping Quan 	if (found) {
16463d763c8SLuping Quan 		fma_cap = fmd_prop_get_int32(hdl, cl->propname);
16563d763c8SLuping Quan 		fmd_hdl_debug(hdl, "Found property, FMA capability=0x%x",
16663d763c8SLuping Quan 		    fma_cap);
16763d763c8SLuping Quan 	} else {
16863d763c8SLuping Quan #endif
16963d763c8SLuping Quan 		fma_cap = fmd_prop_get_int32(hdl, "default_fma_cap");
17063d763c8SLuping Quan 		fmd_hdl_debug(hdl, "Didn't find FMA capability property, "
17163d763c8SLuping Quan 		    "use default=0x%x", fma_cap);
17263d763c8SLuping Quan #if defined(__x86)
17363d763c8SLuping Quan 	}
17463d763c8SLuping Quan #endif
17563d763c8SLuping Quan 
17663d763c8SLuping Quan 	return (fma_cap);
17763d763c8SLuping Quan }
17863d763c8SLuping Quan 
179491f61a1SYanmin Sun static void
send_fma_cap_to_ilom(fmd_hdl_t * hdl,uint32_t fma_cap)18063d763c8SLuping Quan send_fma_cap_to_ilom(fmd_hdl_t *hdl, uint32_t fma_cap)
181491f61a1SYanmin Sun {
18263d763c8SLuping Quan 	int error;
18363d763c8SLuping Quan 	char *msg;
184491f61a1SYanmin Sun 	ipmi_handle_t *ipmi_hdl;
185491f61a1SYanmin Sun 	ipmi_cmd_t cmd;
186491f61a1SYanmin Sun 	uint8_t oem_data[OEM_DATA_LENGTH];
187491f61a1SYanmin Sun 
18863d763c8SLuping Quan 	if ((ipmi_hdl = ipmi_open(&error, &msg, IPMI_TRANSPORT_BMC, NULL))
18963d763c8SLuping Quan 	    == NULL) {
19063d763c8SLuping Quan 		/*
191989f2807SJerry Jelinek 		 * If /dev/ipmi0 doesn't exist on the system, then return
19263d763c8SLuping Quan 		 * without doing anything.
19363d763c8SLuping Quan 		 */
19463d763c8SLuping Quan 		if (error != EIPMI_BMC_OPEN_FAILED)
19563d763c8SLuping Quan 			fmd_hdl_abort(hdl, "Failed to initialize IPMI "
19663d763c8SLuping Quan 			    "connection: %s\n", msg);
19763d763c8SLuping Quan 		fmd_hdl_debug(hdl, "Failed: no IPMI connection present");
19863d763c8SLuping Quan 		return;
19963d763c8SLuping Quan 	}
20063d763c8SLuping Quan 
20163d763c8SLuping Quan 	/*
20263d763c8SLuping Quan 	 * Check if it's Sun ILOM
20363d763c8SLuping Quan 	 */
20463d763c8SLuping Quan 	if (check_sunoem(ipmi_hdl) != 0) {
20563d763c8SLuping Quan 		fmd_hdl_debug(hdl, "Service Processor does not run "
20663d763c8SLuping Quan 		    "Sun ILOM");
20763d763c8SLuping Quan 		ipmi_close(ipmi_hdl);
20863d763c8SLuping Quan 		return;
20963d763c8SLuping Quan 	}
210491f61a1SYanmin Sun 
211491f61a1SYanmin Sun 	oem_data[0] = CORE_TUNNEL_SUBCMD_HOSTFMACAP;
212491f61a1SYanmin Sun 	oem_data[1] = VERSION;
21363d763c8SLuping Quan 	oem_data[2] = fma_cap;
214491f61a1SYanmin Sun 
215491f61a1SYanmin Sun 	cmd.ic_netfn = IPMI_NETFN_OEM;
216491f61a1SYanmin Sun 	cmd.ic_lun = 0;
217491f61a1SYanmin Sun 	cmd.ic_cmd = CMD_SUNOEM_CORE_TUNNEL;
218491f61a1SYanmin Sun 	cmd.ic_dlen = OEM_DATA_LENGTH;
219491f61a1SYanmin Sun 	cmd.ic_data = oem_data;
220491f61a1SYanmin Sun 
221491f61a1SYanmin Sun 	if (ipmi_send(ipmi_hdl, &cmd) == NULL) {
222491f61a1SYanmin Sun 		fmd_hdl_debug(hdl, "Failed to send Solaris FMA "
22363d763c8SLuping Quan 		    "capability to ilom: %s", ipmi_errmsg(ipmi_hdl));
224491f61a1SYanmin Sun 	}
225491f61a1SYanmin Sun 
226491f61a1SYanmin Sun 	ipmi_close(ipmi_hdl);
22763d763c8SLuping Quan }
22863d763c8SLuping Quan 
22963d763c8SLuping Quan /*ARGSUSED*/
23063d763c8SLuping Quan static void
fma_cap_init(fmd_hdl_t * hdl,id_t id,void * data)23163d763c8SLuping Quan fma_cap_init(fmd_hdl_t *hdl, id_t id, void *data)
23263d763c8SLuping Quan {
23363d763c8SLuping Quan 	uint32_t	fma_cap;
23463d763c8SLuping Quan 
23563d763c8SLuping Quan 	fma_cap = get_cap_conf(hdl);
23663d763c8SLuping Quan 	send_fma_cap_to_ilom(hdl, fma_cap);
23763d763c8SLuping Quan 
238491f61a1SYanmin Sun 	fmd_hdl_unregister(hdl);
239491f61a1SYanmin Sun }
240491f61a1SYanmin Sun 
241491f61a1SYanmin Sun static const fmd_hdl_ops_t fmd_ops = {
242491f61a1SYanmin Sun 	NULL,		/* fmdo_recv */
24363d763c8SLuping Quan 	fma_cap_init,	/* fmdo_timeout */
244491f61a1SYanmin Sun 	NULL,		/* fmdo_close */
245491f61a1SYanmin Sun 	NULL,		/* fmdo_stats */
246491f61a1SYanmin Sun 	NULL,		/* fmdo_gc */
247dfe4040dSLuping Quan 	NULL,		/* fmdo_send */
248dfe4040dSLuping Quan 	NULL,		/* fmdo_topo */
249491f61a1SYanmin Sun };
250491f61a1SYanmin Sun 
251491f61a1SYanmin Sun static const fmd_prop_t fmd_props[] = {
252491f61a1SYanmin Sun 	{ "interval", FMD_TYPE_TIME, "1s" },
25363d763c8SLuping Quan 	{ "default_fma_cap", FMD_TYPE_UINT32, "0x3" },
25463d763c8SLuping Quan 	{ "NHMEP_fma_cap", FMD_TYPE_UINT32, "0x3" },
25563d763c8SLuping Quan 	{ "NHMEX_fma_cap", FMD_TYPE_UINT32, "0x2" },
25663d763c8SLuping Quan 	{ "WSMEP_fma_cap", FMD_TYPE_UINT32, "0x3" },
25763d763c8SLuping Quan 	{ "INTLN_fma_cap", FMD_TYPE_UINT32, "0x2" },
258491f61a1SYanmin Sun 	{ NULL, 0, NULL }
259491f61a1SYanmin Sun };
260491f61a1SYanmin Sun 
261491f61a1SYanmin Sun static const fmd_hdl_info_t fmd_info = {
26263d763c8SLuping Quan 	"FMA Capability Messenger", "1.1", &fmd_ops, fmd_props
263491f61a1SYanmin Sun };
264491f61a1SYanmin Sun 
265491f61a1SYanmin Sun void
_fmd_init(fmd_hdl_t * hdl)266491f61a1SYanmin Sun _fmd_init(fmd_hdl_t *hdl)
267491f61a1SYanmin Sun {
268dfe4040dSLuping Quan 	char isa[8];
269dfe4040dSLuping Quan 
27063d763c8SLuping Quan 	/*
27163d763c8SLuping Quan 	 * For now the module only sends message to ILOM on i386 platforms
27263d763c8SLuping Quan 	 * till CR 6933053 is fixed. Module unregister may cause etm module
27363d763c8SLuping Quan 	 * core dump due to 6933053.
27463d763c8SLuping Quan 	 */
275dfe4040dSLuping Quan 	if ((sysinfo(SI_ARCHITECTURE, isa, sizeof (isa)) == -1) ||
276dfe4040dSLuping Quan 	    (strncmp(isa, "i386", 4) != 0))
277dfe4040dSLuping Quan 		return;
278491f61a1SYanmin Sun 
279491f61a1SYanmin Sun 	if (fmd_hdl_register(hdl, FMD_API_VERSION, &fmd_info) != 0)
280491f61a1SYanmin Sun 		return;
281491f61a1SYanmin Sun 
282491f61a1SYanmin Sun 	/*
283491f61a1SYanmin Sun 	 * Setup the timer.
284491f61a1SYanmin Sun 	 */
285dfe4040dSLuping Quan 	(void) fmd_timer_install(hdl, NULL, NULL, 2000000000ULL);
286491f61a1SYanmin Sun }
287491f61a1SYanmin Sun 
28863d763c8SLuping Quan /*ARGSUSED*/
289491f61a1SYanmin Sun void
_fmd_fini(fmd_hdl_t * hdl)290491f61a1SYanmin Sun _fmd_fini(fmd_hdl_t *hdl)
291491f61a1SYanmin Sun {
292491f61a1SYanmin Sun }
293