11991Sheppo /*
21991Sheppo * CDDL HEADER START
31991Sheppo *
41991Sheppo * The contents of this file are subject to the terms of the
51991Sheppo * Common Development and Distribution License (the "License").
61991Sheppo * You may not use this file except in compliance with the License.
71991Sheppo *
81991Sheppo * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
91991Sheppo * or http://www.opensolaris.org/os/licensing.
101991Sheppo * See the License for the specific language governing permissions
111991Sheppo * and limitations under the License.
121991Sheppo *
131991Sheppo * When distributing Covered Code, include this CDDL HEADER in each
141991Sheppo * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
151991Sheppo * If applicable, add the following below this CDDL HEADER, with the
161991Sheppo * fields enclosed by brackets "[]" replaced with your own identifying
171991Sheppo * information: Portions Copyright [yyyy] [name of copyright owner]
181991Sheppo *
191991Sheppo * CDDL HEADER END
201991Sheppo */
211991Sheppo
221991Sheppo /*
23*7799SRichard.Bean@Sun.COM * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
241991Sheppo * Use is subject to license terms.
251991Sheppo */
261991Sheppo
271991Sheppo /*
281991Sheppo * sun4v Fault Isolation Services Module
291991Sheppo */
301991Sheppo
311991Sheppo #include <sys/modctl.h>
321991Sheppo #include <sys/cmn_err.h>
331991Sheppo #include <sys/machsystm.h>
341991Sheppo #include <sys/processor.h>
351991Sheppo #include <sys/mem.h>
361991Sheppo #include <vm/page.h>
371991Sheppo #include <sys/note.h>
381991Sheppo #include <sys/ds.h>
391991Sheppo #include <sys/fault_iso.h>
401991Sheppo
411991Sheppo /*
421991Sheppo * Debugging routines
431991Sheppo */
441991Sheppo #ifdef DEBUG
451991Sheppo uint_t fi_debug = 0x0;
461991Sheppo #define FI_DBG if (fi_debug) cmn_err
471991Sheppo #else /* DEBUG */
481991Sheppo #define FI_DBG _NOTE(CONSTCOND) if (0) cmn_err
491991Sheppo #endif /* DEBUG */
501991Sheppo
511991Sheppo /*
521991Sheppo * Domains Services interaction
531991Sheppo */
541991Sheppo static ds_svc_hdl_t cpu_handle;
551991Sheppo static ds_svc_hdl_t mem_handle;
561991Sheppo
571991Sheppo static ds_ver_t fi_vers[] = { { 1, 0 } };
581991Sheppo #define FI_NVERS (sizeof (fi_vers) / sizeof (fi_vers[0]))
591991Sheppo
601991Sheppo static ds_capability_t cpu_cap = {
611991Sheppo "fma-cpu-service", /* svc_id */
621991Sheppo fi_vers, /* vers */
631991Sheppo FI_NVERS /* nvers */
641991Sheppo };
651991Sheppo
661991Sheppo static ds_capability_t mem_cap = {
671991Sheppo "fma-mem-service", /* svc_id */
681991Sheppo fi_vers, /* vers */
691991Sheppo FI_NVERS /* nvers */
701991Sheppo };
711991Sheppo
721991Sheppo static void fi_reg_handler(ds_cb_arg_t arg, ds_ver_t *ver, ds_svc_hdl_t hdl);
731991Sheppo static void fi_unreg_handler(ds_cb_arg_t arg);
741991Sheppo
751991Sheppo static void cpu_data_handler(ds_cb_arg_t arg, void *buf, size_t buflen);
761991Sheppo static void mem_data_handler(ds_cb_arg_t arg, void *buf, size_t buflen);
771991Sheppo
781991Sheppo static ds_clnt_ops_t cpu_ops = {
791991Sheppo fi_reg_handler, /* ds_reg_cb */
801991Sheppo fi_unreg_handler, /* ds_unreg_cb */
811991Sheppo cpu_data_handler, /* ds_data_cb */
821991Sheppo &cpu_handle /* cb_arg */
831991Sheppo };
841991Sheppo
851991Sheppo static ds_clnt_ops_t mem_ops = {
861991Sheppo fi_reg_handler, /* ds_reg_cb */
871991Sheppo fi_unreg_handler, /* ds_unreg_cb */
881991Sheppo mem_data_handler, /* ds_data_cb */
891991Sheppo &mem_handle /* cb_arg */
901991Sheppo };
911991Sheppo
921991Sheppo static int fi_init(void);
931991Sheppo static void fi_fini(void);
941991Sheppo
951991Sheppo static struct modlmisc modlmisc = {
961991Sheppo &mod_miscops,
97*7799SRichard.Bean@Sun.COM "sun4v Fault Isolation Services"
981991Sheppo };
991991Sheppo
1001991Sheppo static struct modlinkage modlinkage = {
1011991Sheppo MODREV_1,
1021991Sheppo (void *)&modlmisc,
1031991Sheppo NULL
1041991Sheppo };
1051991Sheppo
1061991Sheppo int
_init(void)1071991Sheppo _init(void)
1081991Sheppo {
1091991Sheppo int rv;
1101991Sheppo
1111991Sheppo if ((rv = fi_init()) != 0)
1121991Sheppo return (rv);
1131991Sheppo
1141991Sheppo if ((rv = mod_install(&modlinkage)) != 0)
1151991Sheppo fi_fini();
1161991Sheppo
1171991Sheppo return (rv);
1181991Sheppo }
1191991Sheppo
1201991Sheppo int
_info(struct modinfo * modinfop)1211991Sheppo _info(struct modinfo *modinfop)
1221991Sheppo {
1231991Sheppo return (mod_info(&modlinkage, modinfop));
1241991Sheppo }
1251991Sheppo
1261991Sheppo int fi_allow_unload;
1271991Sheppo
1281991Sheppo int
_fini(void)1291991Sheppo _fini(void)
1301991Sheppo {
1311991Sheppo int status;
1321991Sheppo
1331991Sheppo if (fi_allow_unload == 0)
1341991Sheppo return (EBUSY);
1351991Sheppo
1361991Sheppo if ((status = mod_remove(&modlinkage)) == 0)
1371991Sheppo fi_fini();
1381991Sheppo
1391991Sheppo return (status);
1401991Sheppo }
1411991Sheppo
1421991Sheppo static int
fi_init(void)1431991Sheppo fi_init(void)
1441991Sheppo {
1451991Sheppo int rv;
1461991Sheppo
1471991Sheppo /* register CPU service with domain services framework */
1481991Sheppo rv = ds_cap_init(&cpu_cap, &cpu_ops);
1491991Sheppo if (rv != 0) {
1501991Sheppo FI_DBG(CE_CONT, "ds_cap_init failed: %d", rv);
1511991Sheppo return (rv);
1521991Sheppo }
1531991Sheppo
1541991Sheppo /* register MEM servicewith domain services framework */
1551991Sheppo rv = ds_cap_init(&mem_cap, &mem_ops);
1561991Sheppo if (rv != 0) {
1571991Sheppo FI_DBG(CE_CONT, "ds_cap_init failed: %d", rv);
1581991Sheppo (void) ds_cap_fini(&cpu_cap);
1591991Sheppo return (rv);
1601991Sheppo }
1611991Sheppo
1621991Sheppo return (rv);
1631991Sheppo }
1641991Sheppo
1651991Sheppo static void
fi_fini(void)1661991Sheppo fi_fini(void)
1671991Sheppo {
1681991Sheppo /*
1691991Sheppo * Stop incoming requests from Zeus
1701991Sheppo */
1711991Sheppo (void) ds_cap_fini(&cpu_cap);
1721991Sheppo (void) ds_cap_fini(&mem_cap);
1731991Sheppo }
1741991Sheppo
1751991Sheppo static void
cpu_data_handler(ds_cb_arg_t arg,void * buf,size_t buflen)1761991Sheppo cpu_data_handler(ds_cb_arg_t arg, void *buf, size_t buflen)
1771991Sheppo {
1781991Sheppo _NOTE(ARGUNUSED(arg))
1791991Sheppo
1801991Sheppo fma_cpu_service_req_t *msg = buf;
1811991Sheppo fma_cpu_resp_t resp_msg;
1821991Sheppo int rv = 0;
1831991Sheppo int cpu_status;
1841991Sheppo int resp_back = 0;
1851991Sheppo
1861991Sheppo /*
1871991Sheppo * If the buffer is the wrong size for CPU calls or is NULL then
1881991Sheppo * do not return any message. The call from the ldom mgr. will time out
1891991Sheppo * and the response will be NULL.
1901991Sheppo */
1911991Sheppo if (msg == NULL || buflen != sizeof (fma_cpu_service_req_t)) {
1921991Sheppo return;
1931991Sheppo }
1941991Sheppo
1951991Sheppo FI_DBG(CE_CONT, "req_num = %ld, msg_type = %d, cpu_id = %d\n",
1961991Sheppo msg->req_num, msg->msg_type, msg->cpu_id);
1971991Sheppo
1981991Sheppo resp_msg.req_num = msg->req_num;
1991991Sheppo
2001991Sheppo switch (msg->msg_type) {
2011991Sheppo case FMA_CPU_REQ_STATUS:
2021991Sheppo rv = p_online_internal(msg->cpu_id, P_STATUS,
2031991Sheppo &cpu_status);
2041991Sheppo if (rv == EINVAL) {
2051991Sheppo FI_DBG(CE_CONT, "Failed p_online call failed."
2061991Sheppo "Invalid CPU\n");
2071991Sheppo resp_msg.result = FMA_CPU_RESP_FAILURE;
2081991Sheppo resp_msg.status = FMA_CPU_STAT_ILLEGAL;
2091991Sheppo resp_back = 1;
2101991Sheppo }
2111991Sheppo break;
2121991Sheppo case FMA_CPU_REQ_OFFLINE:
2132336Snarayan rv = p_online_internal(msg->cpu_id, P_FAULTED,
2141991Sheppo &cpu_status);
2151991Sheppo if (rv == EINVAL) {
2161991Sheppo FI_DBG(CE_CONT, "Failed p_online call failed."
2171991Sheppo "Invalid CPU\n");
2181991Sheppo resp_msg.result = FMA_CPU_RESP_FAILURE;
2191991Sheppo resp_msg.status = FMA_CPU_STAT_ILLEGAL;
2201991Sheppo resp_back = 1;
2211991Sheppo } else if (rv == EBUSY) {
2221991Sheppo FI_DBG(CE_CONT, "Failed p_online call failed."
2231991Sheppo "Tried to offline while busy\n");
2241991Sheppo resp_msg.result = FMA_CPU_RESP_FAILURE;
2251991Sheppo resp_msg.status = FMA_CPU_STAT_ONLINE;
2261991Sheppo resp_back = 1;
2271991Sheppo }
2281991Sheppo break;
2291991Sheppo case FMA_CPU_REQ_ONLINE:
2301991Sheppo rv = p_online_internal(msg->cpu_id, P_ONLINE,
2311991Sheppo &cpu_status);
2321991Sheppo if (rv == EINVAL) {
2331991Sheppo FI_DBG(CE_CONT, "Failed p_online call failed."
2341991Sheppo "Invalid CPU\n");
2351991Sheppo resp_msg.result = FMA_CPU_RESP_FAILURE;
2361991Sheppo resp_msg.status = FMA_CPU_STAT_ILLEGAL;
2371991Sheppo resp_back = 1;
2381991Sheppo } else if (rv == ENOTSUP) {
2391991Sheppo FI_DBG(CE_CONT, "Failed p_online call failed."
2401991Sheppo "Online not supported for single CPU\n");
2411991Sheppo resp_msg.result = FMA_CPU_RESP_FAILURE;
2421991Sheppo resp_msg.status = FMA_CPU_STAT_OFFLINE;
2431991Sheppo resp_back = 1;
2441991Sheppo }
2451991Sheppo break;
2461991Sheppo default:
2471991Sheppo /*
2481991Sheppo * If the msg_type was of unknown type simply return and
2491991Sheppo * have the ldom mgr. time out with a NULL response.
2501991Sheppo */
2511991Sheppo return;
2521991Sheppo }
2531991Sheppo
2541991Sheppo if (rv != 0) {
2551991Sheppo if (resp_back) {
2561991Sheppo if ((rv = ds_cap_send(cpu_handle, &resp_msg,
2574419Snevin sizeof (resp_msg))) != 0) {
2581991Sheppo FI_DBG(CE_CONT, "ds_cap_send failed (%d)\n",
2591991Sheppo rv);
2601991Sheppo }
2611991Sheppo return;
2621991Sheppo }
2631991Sheppo ASSERT((rv == EINVAL) || ((rv == EBUSY) &&
2644419Snevin (msg->msg_type == FMA_CPU_REQ_OFFLINE)) ||
2654419Snevin ((rv == ENOTSUP) && (msg->msg_type == FMA_CPU_REQ_ONLINE)));
2661991Sheppo
2671991Sheppo cmn_err(CE_WARN, "p_online_internal error not handled "
2681991Sheppo "rv = %d\n", rv);
2691991Sheppo }
2701991Sheppo
2711991Sheppo resp_msg.req_num = msg->req_num;
2721991Sheppo resp_msg.result = FMA_CPU_RESP_OK;
2731991Sheppo
2741991Sheppo switch (cpu_status) {
2751991Sheppo case P_OFFLINE:
2761991Sheppo case P_FAULTED:
2771991Sheppo case P_POWEROFF:
2781991Sheppo case P_SPARE:
2791991Sheppo resp_msg.status = FMA_CPU_STAT_OFFLINE;
2801991Sheppo break;
2811991Sheppo case P_ONLINE:
2821991Sheppo case P_NOINTR:
2831991Sheppo resp_msg.status = FMA_CPU_STAT_ONLINE;
2841991Sheppo break;
2851991Sheppo default:
2861991Sheppo resp_msg.status = FMA_CPU_STAT_ILLEGAL;
2871991Sheppo }
2881991Sheppo
2891991Sheppo if ((rv = ds_cap_send(cpu_handle, &resp_msg,
2901991Sheppo sizeof (resp_msg))) != 0) {
2911991Sheppo FI_DBG(CE_CONT, "ds_cap_send failed (%d)\n", rv);
2921991Sheppo }
2931991Sheppo }
2941991Sheppo
2951991Sheppo static void
mem_data_handler(ds_cb_arg_t arg,void * buf,size_t buflen)2961991Sheppo mem_data_handler(ds_cb_arg_t arg, void *buf, size_t buflen)
2971991Sheppo {
2981991Sheppo _NOTE(ARGUNUSED(arg))
2991991Sheppo
3001991Sheppo fma_mem_service_req_t *msg = buf;
3011991Sheppo fma_mem_resp_t resp_msg;
3021991Sheppo int rv = 0;
3031991Sheppo
3041991Sheppo /*
3051991Sheppo * If the buffer is the wrong size for Mem calls or is NULL then
3061991Sheppo * do not return any message. The call from the ldom mgr. will time out
3071991Sheppo * and the response will be NULL.
3081991Sheppo */
3091991Sheppo if (msg == NULL || buflen != sizeof (fma_mem_service_req_t)) {
3101991Sheppo return;
3111991Sheppo }
3121991Sheppo
3131991Sheppo FI_DBG(CE_CONT, "req_num = %ld, msg_type = %d, memory addr = 0x%lx"
3141991Sheppo "memory length = 0x%lx\n", msg->req_num, msg->msg_type,
3151991Sheppo msg->real_addr, msg->length);
3161991Sheppo
3171991Sheppo resp_msg.req_num = msg->req_num;
3181991Sheppo resp_msg.res_addr = msg->real_addr;
3191991Sheppo resp_msg.res_length = msg->length;
3201991Sheppo
3211991Sheppo /*
3221991Sheppo * Information about return values for page calls can be referenced
3231991Sheppo * in usr/src/uts/common/vm/page_retire.c
3241991Sheppo */
3251991Sheppo switch (msg->msg_type) {
3261991Sheppo case FMA_MEM_REQ_STATUS:
3271991Sheppo rv = page_retire_check(msg->real_addr, NULL);
3281991Sheppo switch (rv) {
3291991Sheppo /* Page is retired */
3301991Sheppo case 0:
3311991Sheppo resp_msg.result = FMA_MEM_RESP_OK;
3321991Sheppo resp_msg.status = FMA_MEM_STAT_RETIRED;
3331991Sheppo break;
3341991Sheppo /* Page is pending. Send back failure and not retired */
3351991Sheppo case EAGAIN:
3361991Sheppo resp_msg.result = FMA_MEM_RESP_FAILURE;
3371991Sheppo resp_msg.status = FMA_MEM_STAT_NOTRETIRED;
3381991Sheppo break;
3391991Sheppo /* Page is not retired. */
3401991Sheppo case EIO:
3414419Snevin resp_msg.result = FMA_MEM_RESP_OK;
3421991Sheppo resp_msg.status = FMA_MEM_STAT_NOTRETIRED;
3431991Sheppo break;
3441991Sheppo /* PA is not valid */
3451991Sheppo case EINVAL:
3461991Sheppo resp_msg.result = FMA_MEM_RESP_FAILURE;
3471991Sheppo resp_msg.status = FMA_MEM_STAT_ILLEGAL;
3481991Sheppo break;
3491991Sheppo default:
3501991Sheppo ASSERT((rv == 0) || (rv == EAGAIN) || (rv == EIO) ||
3511991Sheppo (rv == EINVAL));
3521991Sheppo cmn_err(CE_WARN, "fault_iso: return value from "
3531991Sheppo "page_retire_check invalid: %d\n", rv);
3541991Sheppo }
3551991Sheppo break;
3561991Sheppo case FMA_MEM_REQ_RETIRE:
3571991Sheppo rv = page_retire(msg->real_addr, PR_FMA);
3581991Sheppo switch (rv) {
3591991Sheppo /* Page retired successfully */
3601991Sheppo case 0:
3611991Sheppo resp_msg.result = FMA_MEM_RESP_OK;
3621991Sheppo resp_msg.status = FMA_MEM_STAT_RETIRED;
3631991Sheppo break;
3641991Sheppo /* Tried to retire and now Pending retirement */
3651991Sheppo case EAGAIN:
3661991Sheppo resp_msg.result = FMA_MEM_RESP_FAILURE;
3671991Sheppo resp_msg.status = FMA_MEM_STAT_NOTRETIRED;
3681991Sheppo break;
3691991Sheppo /* Did not try to retire. Page already retired */
3701991Sheppo case EIO:
3711991Sheppo resp_msg.result = FMA_MEM_RESP_FAILURE;
3721991Sheppo resp_msg.status = FMA_MEM_STAT_RETIRED;
3731991Sheppo break;
3741991Sheppo /* PA is not valid */
3751991Sheppo case EINVAL:
3761991Sheppo resp_msg.result = FMA_MEM_RESP_FAILURE;
3771991Sheppo resp_msg.status = FMA_MEM_STAT_ILLEGAL;
3781991Sheppo break;
3791991Sheppo default:
3801991Sheppo ASSERT((rv == 0) || (rv == EAGAIN) || (rv == EIO) ||
3811991Sheppo (rv == EINVAL));
3821991Sheppo cmn_err(CE_WARN, "fault_iso: return value from "
3831991Sheppo "page_retire invalid: %d\n", rv);
3841991Sheppo }
3851991Sheppo break;
3861991Sheppo case FMA_MEM_REQ_RESURRECT:
3871991Sheppo rv = page_unretire(msg->real_addr);
3881991Sheppo switch (rv) {
3891991Sheppo /* Page succesfullly unretired */
3901991Sheppo case 0:
3911991Sheppo resp_msg.result = FMA_MEM_RESP_OK;
3921991Sheppo resp_msg.status = FMA_MEM_STAT_NOTRETIRED;
3931991Sheppo break;
3941991Sheppo /* Page could not be locked. Still retired */
3951991Sheppo case EAGAIN:
3961991Sheppo resp_msg.result = FMA_MEM_RESP_FAILURE;
3971991Sheppo resp_msg.status = FMA_MEM_STAT_RETIRED;
3981991Sheppo break;
3991991Sheppo /* Page was not retired already */
4001991Sheppo case EIO:
4011991Sheppo resp_msg.result = FMA_MEM_RESP_FAILURE;
4021991Sheppo resp_msg.status = FMA_MEM_STAT_NOTRETIRED;
4031991Sheppo break;
4041991Sheppo /* PA is not valid */
4051991Sheppo case EINVAL:
4061991Sheppo resp_msg.result = FMA_MEM_RESP_FAILURE;
4071991Sheppo resp_msg.status = FMA_MEM_STAT_ILLEGAL;
4081991Sheppo break;
4091991Sheppo default:
4101991Sheppo ASSERT((rv == 0) || (rv == EAGAIN) || (rv == EIO) ||
4111991Sheppo (rv == EINVAL));
4121991Sheppo cmn_err(CE_WARN, "fault_iso: return value from "
4131991Sheppo "page_unretire invalid: %d\n", rv);
4141991Sheppo }
4151991Sheppo break;
4161991Sheppo default:
4171991Sheppo /*
4181991Sheppo * If the msg_type was of unknown type simply return and
4191991Sheppo * have the ldom mgr. time out with a NULL response.
4201991Sheppo */
4211991Sheppo return;
4221991Sheppo }
4231991Sheppo
4241991Sheppo if ((rv = ds_cap_send(mem_handle, &resp_msg, sizeof (resp_msg))) != 0) {
4251991Sheppo FI_DBG(CE_CONT, "ds_cap_send failed (%d)\n", rv);
4261991Sheppo }
4271991Sheppo }
4281991Sheppo
4291991Sheppo static void
fi_reg_handler(ds_cb_arg_t arg,ds_ver_t * ver,ds_svc_hdl_t hdl)4301991Sheppo fi_reg_handler(ds_cb_arg_t arg, ds_ver_t *ver, ds_svc_hdl_t hdl)
4311991Sheppo {
4321991Sheppo FI_DBG(CE_CONT, "fi_reg_handler: arg=0x%p, ver=%d.%d, hdl=0x%lx\n",
4331991Sheppo arg, ver->major, ver->minor, hdl);
4341991Sheppo
4351991Sheppo if ((ds_svc_hdl_t *)arg == &cpu_handle)
4361991Sheppo cpu_handle = hdl;
4371991Sheppo if ((ds_svc_hdl_t *)arg == &mem_handle)
4381991Sheppo mem_handle = hdl;
4391991Sheppo }
4401991Sheppo
4411991Sheppo static void
fi_unreg_handler(ds_cb_arg_t arg)4421991Sheppo fi_unreg_handler(ds_cb_arg_t arg)
4431991Sheppo {
4441991Sheppo FI_DBG(CE_CONT, "fi_unreg_handler: arg=0x%p\n", arg);
4451991Sheppo
4461991Sheppo if ((ds_svc_hdl_t *)arg == &cpu_handle)
4471991Sheppo cpu_handle = DS_INVALID_HDL;
4481991Sheppo if ((ds_svc_hdl_t *)arg == &mem_handle)
4491991Sheppo mem_handle = DS_INVALID_HDL;
4501991Sheppo }
451