xref: /illumos-gate/usr/src/uts/sun4v/io/dr_cpu.c (revision bbf215553c7233fbab8a0afdf1fac74c44781867)
11ae08745Sheppo /*
21ae08745Sheppo  * CDDL HEADER START
31ae08745Sheppo  *
41ae08745Sheppo  * The contents of this file are subject to the terms of the
51ae08745Sheppo  * Common Development and Distribution License (the "License").
61ae08745Sheppo  * You may not use this file except in compliance with the License.
71ae08745Sheppo  *
81ae08745Sheppo  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
91ae08745Sheppo  * or http://www.opensolaris.org/os/licensing.
101ae08745Sheppo  * See the License for the specific language governing permissions
111ae08745Sheppo  * and limitations under the License.
121ae08745Sheppo  *
131ae08745Sheppo  * When distributing Covered Code, include this CDDL HEADER in each
141ae08745Sheppo  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
151ae08745Sheppo  * If applicable, add the following below this CDDL HEADER, with the
161ae08745Sheppo  * fields enclosed by brackets "[]" replaced with your own identifying
171ae08745Sheppo  * information: Portions Copyright [yyyy] [name of copyright owner]
181ae08745Sheppo  *
191ae08745Sheppo  * CDDL HEADER END
201ae08745Sheppo  */
211ae08745Sheppo 
221ae08745Sheppo /*
237de586caSJames Marks - Sun Microsystems  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
241ae08745Sheppo  * Use is subject to license terms.
251ae08745Sheppo  */
261ae08745Sheppo 
271ae08745Sheppo /*
28c3377ee9SJohn Levon  * Copyright 2019 Joyent, Inc.
29c3377ee9SJohn Levon  */
30c3377ee9SJohn Levon 
31c3377ee9SJohn Levon /*
321ae08745Sheppo  * sun4v CPU DR Module
331ae08745Sheppo  */
341ae08745Sheppo 
351ae08745Sheppo #include <sys/modctl.h>
361ae08745Sheppo #include <sys/processor.h>
371ae08745Sheppo #include <sys/cpuvar.h>
380c86d1bbSrsmaeda #include <sys/cpupart.h>
391ae08745Sheppo #include <sys/sunddi.h>
401ae08745Sheppo #include <sys/sunndi.h>
411ae08745Sheppo #include <sys/note.h>
421ae08745Sheppo #include <sys/sysevent/dr.h>
431ae08745Sheppo #include <sys/hypervisor_api.h>
441ae08745Sheppo #include <sys/mach_descrip.h>
451ae08745Sheppo #include <sys/mdesc.h>
461ae08745Sheppo #include <sys/ds.h>
471d4b38e0Srsmaeda #include <sys/drctl.h>
481ae08745Sheppo #include <sys/dr_util.h>
491ae08745Sheppo #include <sys/dr_cpu.h>
501ae08745Sheppo #include <sys/promif.h>
511ae08745Sheppo #include <sys/machsystm.h>
521ae08745Sheppo 
531ae08745Sheppo 
541ae08745Sheppo static struct modlmisc modlmisc = {
551ae08745Sheppo 	&mod_miscops,
56f500b196SRichard Bean 	"sun4v CPU DR"
571ae08745Sheppo };
581ae08745Sheppo 
591ae08745Sheppo static struct modlinkage modlinkage = {
601ae08745Sheppo 	MODREV_1,
611ae08745Sheppo 	(void *)&modlmisc,
621ae08745Sheppo 	NULL
631ae08745Sheppo };
641ae08745Sheppo 
651d4b38e0Srsmaeda typedef int (*fn_t)(processorid_t, int *, boolean_t);
661d4b38e0Srsmaeda 
671ae08745Sheppo /*
681ae08745Sheppo  * Global DS Handle
691ae08745Sheppo  */
701ae08745Sheppo static ds_svc_hdl_t ds_handle;
711ae08745Sheppo 
721ae08745Sheppo /*
731ae08745Sheppo  * Supported DS Capability Versions
741ae08745Sheppo  */
7599c7e855SJames Marks - Sun Microsystems static ds_ver_t		dr_cpu_vers[] = { { 1, 1 }, { 1, 0 } };
761ae08745Sheppo #define	DR_CPU_NVERS	(sizeof (dr_cpu_vers) / sizeof (dr_cpu_vers[0]))
771ae08745Sheppo 
7899c7e855SJames Marks - Sun Microsystems static ds_ver_t		version;
7999c7e855SJames Marks - Sun Microsystems 
801ae08745Sheppo /*
811ae08745Sheppo  * DS Capability Description
821ae08745Sheppo  */
831ae08745Sheppo static ds_capability_t dr_cpu_cap = {
841ae08745Sheppo 	DR_CPU_DS_ID,		/* svc_id */
851ae08745Sheppo 	dr_cpu_vers,		/* vers */
861ae08745Sheppo 	DR_CPU_NVERS		/* nvers */
871ae08745Sheppo };
881ae08745Sheppo 
8999c7e855SJames Marks - Sun Microsystems #define	DRCPU_VERS_EQ(_maj, _min) \
9099c7e855SJames Marks - Sun Microsystems 	((version.major == (_maj)) && (version.minor == (_min)))
9199c7e855SJames Marks - Sun Microsystems 
9299c7e855SJames Marks - Sun Microsystems #define	DRCPU_VERS_GTEQ(_maj, _min) \
9399c7e855SJames Marks - Sun Microsystems 	((version.major > (_maj)) ||					\
9499c7e855SJames Marks - Sun Microsystems 	((version.major == (_maj)) && (version.minor >= (_min))))
9599c7e855SJames Marks - Sun Microsystems 
961ae08745Sheppo /*
971ae08745Sheppo  * DS Callbacks
981ae08745Sheppo  */
991ae08745Sheppo static void dr_cpu_reg_handler(ds_cb_arg_t, ds_ver_t *, ds_svc_hdl_t);
1001ae08745Sheppo static void dr_cpu_unreg_handler(ds_cb_arg_t arg);
1011ae08745Sheppo static void dr_cpu_data_handler(ds_cb_arg_t arg, void *buf, size_t buflen);
1021ae08745Sheppo 
1031ae08745Sheppo /*
1041ae08745Sheppo  * DS Client Ops Vector
1051ae08745Sheppo  */
1061ae08745Sheppo static ds_clnt_ops_t dr_cpu_ops = {
1071ae08745Sheppo 	dr_cpu_reg_handler,	/* ds_reg_cb */
1081ae08745Sheppo 	dr_cpu_unreg_handler,	/* ds_unreg_cb */
1091ae08745Sheppo 	dr_cpu_data_handler,	/* ds_data_cb */
1101ae08745Sheppo 	NULL			/* cb_arg */
1111ae08745Sheppo };
1121ae08745Sheppo 
1131ae08745Sheppo /*
1140c86d1bbSrsmaeda  * Operation Results
1150c86d1bbSrsmaeda  *
1160c86d1bbSrsmaeda  * Used internally to gather results while an operation on a
1170c86d1bbSrsmaeda  * list of CPUs is in progress. In particular, it is used to
1180c86d1bbSrsmaeda  * keep track of which CPUs have already failed so that they are
1190c86d1bbSrsmaeda  * not processed further, and the manner in which they failed.
1200c86d1bbSrsmaeda  */
1210c86d1bbSrsmaeda typedef struct {
1220c86d1bbSrsmaeda 	uint32_t	cpuid;
1230c86d1bbSrsmaeda 	uint32_t	result;
1240c86d1bbSrsmaeda 	uint32_t	status;
1250c86d1bbSrsmaeda 	char		*string;
1260c86d1bbSrsmaeda } dr_cpu_res_t;
1270c86d1bbSrsmaeda 
1280c86d1bbSrsmaeda #define	DR_CPU_MAX_ERR_LEN	64	/* maximum error string length */
1290c86d1bbSrsmaeda 
1300c86d1bbSrsmaeda /*
1311ae08745Sheppo  * Internal Functions
1321ae08745Sheppo  */
1331ae08745Sheppo static int dr_cpu_init(void);
1341ae08745Sheppo static int dr_cpu_fini(void);
1351ae08745Sheppo 
1360c86d1bbSrsmaeda static int dr_cpu_list_wrk(dr_cpu_hdr_t *, dr_cpu_hdr_t **, int *);
1371ae08745Sheppo static int dr_cpu_list_status(dr_cpu_hdr_t *, dr_cpu_hdr_t **, int *);
1381ae08745Sheppo 
1391ae08745Sheppo static int dr_cpu_unconfigure(processorid_t, int *status, boolean_t force);
1401d4b38e0Srsmaeda static int dr_cpu_configure(processorid_t, int *status, boolean_t force);
1411ae08745Sheppo static int dr_cpu_status(processorid_t, int *status);
1421ae08745Sheppo 
1430c86d1bbSrsmaeda static void dr_cpu_check_cpus(dr_cpu_hdr_t *req, dr_cpu_res_t *res);
1440c86d1bbSrsmaeda static void dr_cpu_check_psrset(uint32_t *cpuids, dr_cpu_res_t *res, int nres);
1450c86d1bbSrsmaeda static int dr_cpu_check_bound_thr(cpu_t *cp, dr_cpu_res_t *res);
1460c86d1bbSrsmaeda 
1470c86d1bbSrsmaeda static dr_cpu_res_t *dr_cpu_res_array_init(dr_cpu_hdr_t *, drctl_rsrc_t *, int);
1480c86d1bbSrsmaeda static void dr_cpu_res_array_fini(dr_cpu_res_t *res, int nres);
1490c86d1bbSrsmaeda static size_t dr_cpu_pack_response(dr_cpu_hdr_t *req, dr_cpu_res_t *res,
1500c86d1bbSrsmaeda     dr_cpu_hdr_t **respp);
1510c86d1bbSrsmaeda 
1521ae08745Sheppo static int dr_cpu_probe(processorid_t newcpuid);
1531ae08745Sheppo static int dr_cpu_deprobe(processorid_t cpuid);
1541ae08745Sheppo 
1551ae08745Sheppo static dev_info_t *dr_cpu_find_node(processorid_t cpuid);
1561ae08745Sheppo static mde_cookie_t dr_cpu_find_node_md(processorid_t, md_t *, mde_cookie_t *);
1571ae08745Sheppo 
1581ae08745Sheppo int
_init(void)1591ae08745Sheppo _init(void)
1601ae08745Sheppo {
1611ae08745Sheppo 	int	status;
1621ae08745Sheppo 
1631ae08745Sheppo 	/* check that CPU DR is enabled */
1641ae08745Sheppo 	if (dr_is_disabled(DR_TYPE_CPU)) {
1651ae08745Sheppo 		cmn_err(CE_CONT, "!CPU DR is disabled\n");
1661ae08745Sheppo 		return (-1);
1671ae08745Sheppo 	}
1681ae08745Sheppo 
1691ae08745Sheppo 	if ((status = dr_cpu_init()) != 0) {
1701ae08745Sheppo 		cmn_err(CE_NOTE, "CPU DR initialization failed");
1711ae08745Sheppo 		return (status);
1721ae08745Sheppo 	}
1731ae08745Sheppo 
1741ae08745Sheppo 	if ((status = mod_install(&modlinkage)) != 0) {
1751ae08745Sheppo 		(void) dr_cpu_fini();
1761ae08745Sheppo 	}
1771ae08745Sheppo 
1781ae08745Sheppo 	return (status);
1791ae08745Sheppo }
1801ae08745Sheppo 
1811ae08745Sheppo int
_info(struct modinfo * modinfop)1821ae08745Sheppo _info(struct modinfo *modinfop)
1831ae08745Sheppo {
1841ae08745Sheppo 	return (mod_info(&modlinkage, modinfop));
1851ae08745Sheppo }
1861ae08745Sheppo 
1871ae08745Sheppo int dr_cpu_allow_unload;
1881ae08745Sheppo 
1891ae08745Sheppo int
_fini(void)1901ae08745Sheppo _fini(void)
1911ae08745Sheppo {
1921ae08745Sheppo 	int	status;
1931ae08745Sheppo 
1941ae08745Sheppo 	if (dr_cpu_allow_unload == 0)
1951ae08745Sheppo 		return (EBUSY);
1961ae08745Sheppo 
1971ae08745Sheppo 	if ((status = mod_remove(&modlinkage)) == 0) {
1981ae08745Sheppo 		(void) dr_cpu_fini();
1991ae08745Sheppo 	}
2001ae08745Sheppo 
2011ae08745Sheppo 	return (status);
2021ae08745Sheppo }
2031ae08745Sheppo 
2041ae08745Sheppo static int
dr_cpu_init(void)2051ae08745Sheppo dr_cpu_init(void)
2061ae08745Sheppo {
2071ae08745Sheppo 	int	rv;
2081ae08745Sheppo 
2091ae08745Sheppo 	if ((rv = ds_cap_init(&dr_cpu_cap, &dr_cpu_ops)) != 0) {
2101ae08745Sheppo 		cmn_err(CE_NOTE, "ds_cap_init failed: %d", rv);
2111ae08745Sheppo 		return (-1);
2121ae08745Sheppo 	}
2131ae08745Sheppo 
2141ae08745Sheppo 	return (0);
2151ae08745Sheppo }
2161ae08745Sheppo 
2171ae08745Sheppo static int
dr_cpu_fini(void)2181ae08745Sheppo dr_cpu_fini(void)
2191ae08745Sheppo {
2201ae08745Sheppo 	int	rv;
2211ae08745Sheppo 
2221ae08745Sheppo 	if ((rv = ds_cap_fini(&dr_cpu_cap)) != 0) {
2231ae08745Sheppo 		cmn_err(CE_NOTE, "ds_cap_fini failed: %d", rv);
2241ae08745Sheppo 		return (-1);
2251ae08745Sheppo 	}
2261ae08745Sheppo 
2271ae08745Sheppo 	return (0);
2281ae08745Sheppo }
2291ae08745Sheppo 
2301ae08745Sheppo static void
dr_cpu_reg_handler(ds_cb_arg_t arg,ds_ver_t * ver,ds_svc_hdl_t hdl)2311ae08745Sheppo dr_cpu_reg_handler(ds_cb_arg_t arg, ds_ver_t *ver, ds_svc_hdl_t hdl)
2321ae08745Sheppo {
2331ae08745Sheppo 	DR_DBG_CPU("reg_handler: arg=0x%p, ver=%d.%d, hdl=0x%lx\n", arg,
2341ae08745Sheppo 	    ver->major, ver->minor, hdl);
2351ae08745Sheppo 
23699c7e855SJames Marks - Sun Microsystems 	version.major = ver->major;
23799c7e855SJames Marks - Sun Microsystems 	version.minor = ver->minor;
2381ae08745Sheppo 	ds_handle = hdl;
2391ae08745Sheppo }
2401ae08745Sheppo 
2411ae08745Sheppo static void
dr_cpu_unreg_handler(ds_cb_arg_t arg)2421ae08745Sheppo dr_cpu_unreg_handler(ds_cb_arg_t arg)
2431ae08745Sheppo {
2441ae08745Sheppo 	DR_DBG_CPU("unreg_handler: arg=0x%p\n", arg);
2451ae08745Sheppo 
2461ae08745Sheppo 	ds_handle = DS_INVALID_HDL;
2471ae08745Sheppo }
2481ae08745Sheppo 
2491ae08745Sheppo static void
dr_cpu_data_handler(ds_cb_arg_t arg,void * buf,size_t buflen)2501ae08745Sheppo dr_cpu_data_handler(ds_cb_arg_t arg, void *buf, size_t buflen)
2511ae08745Sheppo {
2521ae08745Sheppo 	_NOTE(ARGUNUSED(arg))
2531ae08745Sheppo 
2541ae08745Sheppo 	dr_cpu_hdr_t	*req = buf;
2551ae08745Sheppo 	dr_cpu_hdr_t	err_resp;
2561ae08745Sheppo 	dr_cpu_hdr_t	*resp = &err_resp;
2571ae08745Sheppo 	int		resp_len = 0;
2581ae08745Sheppo 	int		rv;
2591ae08745Sheppo 
2601ae08745Sheppo 	/*
2611ae08745Sheppo 	 * Sanity check the message
2621ae08745Sheppo 	 */
2631ae08745Sheppo 	if (buflen < sizeof (dr_cpu_hdr_t)) {
2641ae08745Sheppo 		DR_DBG_CPU("incoming message short: expected at least %ld "
2651ae08745Sheppo 		    "bytes, received %ld\n", sizeof (dr_cpu_hdr_t), buflen);
2661ae08745Sheppo 		goto done;
2671ae08745Sheppo 	}
2681ae08745Sheppo 
2691ae08745Sheppo 	if (req == NULL) {
2701ae08745Sheppo 		DR_DBG_CPU("empty message: expected at least %ld bytes\n",
2711ae08745Sheppo 		    sizeof (dr_cpu_hdr_t));
2721ae08745Sheppo 		goto done;
2731ae08745Sheppo 	}
2741ae08745Sheppo 
2751ae08745Sheppo 	DR_DBG_CPU("incoming request:\n");
2761ae08745Sheppo 	DR_DBG_DUMP_MSG(buf, buflen);
2771ae08745Sheppo 
2781ae08745Sheppo 	if (req->num_records > NCPU) {
2791ae08745Sheppo 		DR_DBG_CPU("CPU list too long: %d when %d is the maximum\n",
2801ae08745Sheppo 		    req->num_records, NCPU);
2811ae08745Sheppo 		goto done;
2821ae08745Sheppo 	}
2831ae08745Sheppo 
2841ae08745Sheppo 	if (req->num_records == 0) {
2851ae08745Sheppo 		DR_DBG_CPU("No CPU specified for operation\n");
2861ae08745Sheppo 		goto done;
2871ae08745Sheppo 	}
2881ae08745Sheppo 
2891ae08745Sheppo 	/*
2901ae08745Sheppo 	 * Process the command
2911ae08745Sheppo 	 */
2921ae08745Sheppo 	switch (req->msg_type) {
2931ae08745Sheppo 	case DR_CPU_CONFIGURE:
2941ae08745Sheppo 	case DR_CPU_UNCONFIGURE:
2951ae08745Sheppo 	case DR_CPU_FORCE_UNCONFIG:
2960c86d1bbSrsmaeda 		if ((rv = dr_cpu_list_wrk(req, &resp, &resp_len)) != 0) {
2970c86d1bbSrsmaeda 			DR_DBG_CPU("%s%s failed (%d)\n",
2980c86d1bbSrsmaeda 			    (req->msg_type == DR_CPU_CONFIGURE) ?
2990c86d1bbSrsmaeda 			    "CPU configure" : "CPU unconfigure",
3000c86d1bbSrsmaeda 			    (req->msg_type == DR_CPU_FORCE_UNCONFIG) ?
3010c86d1bbSrsmaeda 			    " (forced)" : "", rv);
3020c86d1bbSrsmaeda 		}
3031ae08745Sheppo 		break;
3041ae08745Sheppo 
3051ae08745Sheppo 	case DR_CPU_STATUS:
3061ae08745Sheppo 		if ((rv = dr_cpu_list_status(req, &resp, &resp_len)) != 0)
3070c86d1bbSrsmaeda 			DR_DBG_CPU("CPU status failed (%d)\n", rv);
3081ae08745Sheppo 		break;
3091ae08745Sheppo 
3101ae08745Sheppo 	default:
3111ae08745Sheppo 		cmn_err(CE_NOTE, "unsupported DR operation (%d)",
3121ae08745Sheppo 		    req->msg_type);
3131ae08745Sheppo 		break;
3141ae08745Sheppo 	}
3151ae08745Sheppo 
3161ae08745Sheppo done:
3171ae08745Sheppo 	/* check if an error occurred */
3181ae08745Sheppo 	if (resp == &err_resp) {
3191ae08745Sheppo 		resp->req_num = (req) ? req->req_num : 0;
3201ae08745Sheppo 		resp->msg_type = DR_CPU_ERROR;
3211ae08745Sheppo 		resp->num_records = 0;
3221ae08745Sheppo 		resp_len = sizeof (dr_cpu_hdr_t);
3231ae08745Sheppo 	}
3241ae08745Sheppo 
3250c86d1bbSrsmaeda 	DR_DBG_CPU("outgoing response:\n");
3260c86d1bbSrsmaeda 	DR_DBG_DUMP_MSG(resp, resp_len);
3270c86d1bbSrsmaeda 
3281ae08745Sheppo 	/* send back the response */
3291ae08745Sheppo 	if (ds_cap_send(ds_handle, resp, resp_len) != 0) {
3301ae08745Sheppo 		DR_DBG_CPU("ds_send failed\n");
3311ae08745Sheppo 	}
3321ae08745Sheppo 
3331ae08745Sheppo 	/* free any allocated memory */
33499c7e855SJames Marks - Sun Microsystems 	if (DRCPU_VERS_GTEQ(1, 1) || (resp != &err_resp)) {
33599c7e855SJames Marks - Sun Microsystems 		DR_DBG_KMEM("%s: free addr %p size %d\n",
33699c7e855SJames Marks - Sun Microsystems 		    __func__, (void *)resp, resp_len);
3371ae08745Sheppo 		kmem_free(resp, resp_len);
3381ae08745Sheppo 	}
3391ae08745Sheppo }
3401ae08745Sheppo 
3411ae08745Sheppo /*
34299c7e855SJames Marks - Sun Microsystems  * Create a response message which consists of a header followed
34399c7e855SJames Marks - Sun Microsystems  * by the error string passed in.
34499c7e855SJames Marks - Sun Microsystems  */
34599c7e855SJames Marks - Sun Microsystems static size_t
dr_cpu_err_resp(dr_cpu_hdr_t * req,dr_cpu_hdr_t ** respp,char * msg)34699c7e855SJames Marks - Sun Microsystems dr_cpu_err_resp(dr_cpu_hdr_t *req, dr_cpu_hdr_t **respp, char *msg)
34799c7e855SJames Marks - Sun Microsystems {
34899c7e855SJames Marks - Sun Microsystems 	size_t size;
34999c7e855SJames Marks - Sun Microsystems 	dr_cpu_hdr_t *resp;
35099c7e855SJames Marks - Sun Microsystems 
35199c7e855SJames Marks - Sun Microsystems 	ASSERT((msg != NULL) && (strlen(msg) > 0));
35299c7e855SJames Marks - Sun Microsystems 
35399c7e855SJames Marks - Sun Microsystems 	size = sizeof (*req) + strlen(msg) + 1;
35499c7e855SJames Marks - Sun Microsystems 	resp = kmem_alloc(size, KM_SLEEP);
35599c7e855SJames Marks - Sun Microsystems 	DR_DBG_KMEM("%s: alloc addr %p size %ld\n",
35699c7e855SJames Marks - Sun Microsystems 	    __func__, (void *)resp, size);
35799c7e855SJames Marks - Sun Microsystems 
35899c7e855SJames Marks - Sun Microsystems 	resp->req_num = req->req_num;
35999c7e855SJames Marks - Sun Microsystems 	resp->msg_type = DR_CPU_ERROR;
36099c7e855SJames Marks - Sun Microsystems 	resp->num_records = 0;
36199c7e855SJames Marks - Sun Microsystems 
36299c7e855SJames Marks - Sun Microsystems 	(void) strcpy((char *)(resp) + sizeof (*resp), msg);
36399c7e855SJames Marks - Sun Microsystems 
36499c7e855SJames Marks - Sun Microsystems 	*respp = resp;
36599c7e855SJames Marks - Sun Microsystems 
36699c7e855SJames Marks - Sun Microsystems 	return (size);
36799c7e855SJames Marks - Sun Microsystems }
36899c7e855SJames Marks - Sun Microsystems 
36999c7e855SJames Marks - Sun Microsystems /*
3701d4b38e0Srsmaeda  * Common routine to config or unconfig multiple cpus.  The unconfig
3711d4b38e0Srsmaeda  * case checks with the OS to see if the removal of cpus will be
3721d4b38e0Srsmaeda  * permitted, but can be overridden by the "force" version of the
3731d4b38e0Srsmaeda  * command.  Otherwise, the logic for both cases is identical.
3741d4b38e0Srsmaeda  *
3751d4b38e0Srsmaeda  * Note: Do not modify result buffer or length on error.
3761ae08745Sheppo  */
3771ae08745Sheppo static int
dr_cpu_list_wrk(dr_cpu_hdr_t * req,dr_cpu_hdr_t ** resp,int * resp_len)3780c86d1bbSrsmaeda dr_cpu_list_wrk(dr_cpu_hdr_t *req, dr_cpu_hdr_t **resp, int *resp_len)
3791ae08745Sheppo {
3801d4b38e0Srsmaeda 	int		rv;
3810c86d1bbSrsmaeda 	int		idx;
3820c86d1bbSrsmaeda 	int		count;
3830c86d1bbSrsmaeda 	fn_t		dr_fn;
3840c86d1bbSrsmaeda 	int		se_hint;
3850c86d1bbSrsmaeda 	boolean_t	force = B_FALSE;
3860c86d1bbSrsmaeda 	uint32_t	*req_cpus;
3870c86d1bbSrsmaeda 	dr_cpu_res_t	*res;
3880c86d1bbSrsmaeda 	int		drctl_cmd;
3890c86d1bbSrsmaeda 	int		drctl_flags = 0;
3900c86d1bbSrsmaeda 	drctl_rsrc_t	*drctl_req;
3910c86d1bbSrsmaeda 	size_t		drctl_req_len;
39299c7e855SJames Marks - Sun Microsystems 	drctl_resp_t	*drctl_resp;
39399c7e855SJames Marks - Sun Microsystems 	drctl_rsrc_t	*drctl_rsrc;
39499c7e855SJames Marks - Sun Microsystems 	size_t		drctl_resp_len = 0;
3950c86d1bbSrsmaeda 	drctl_cookie_t	drctl_res_ck;
3960c86d1bbSrsmaeda 
3970c86d1bbSrsmaeda 	ASSERT((req != NULL) && (req->num_records != 0));
3981ae08745Sheppo 
3990c86d1bbSrsmaeda 	count = req->num_records;
4001ae08745Sheppo 
4010c86d1bbSrsmaeda 	/*
4020c86d1bbSrsmaeda 	 * Extract all information that is specific
4030c86d1bbSrsmaeda 	 * to the various types of operations.
4040c86d1bbSrsmaeda 	 */
4050c86d1bbSrsmaeda 	switch (req->msg_type) {
4061d4b38e0Srsmaeda 	case DR_CPU_CONFIGURE:
4070c86d1bbSrsmaeda 		dr_fn = dr_cpu_configure;
4080c86d1bbSrsmaeda 		drctl_cmd = DRCTL_CPU_CONFIG_REQUEST;
4090c86d1bbSrsmaeda 		se_hint = SE_HINT_INSERT;
4101d4b38e0Srsmaeda 		break;
4111d4b38e0Srsmaeda 	case DR_CPU_FORCE_UNCONFIG:
4120c86d1bbSrsmaeda 		drctl_flags = DRCTL_FLAG_FORCE;
4131d4b38e0Srsmaeda 		force = B_TRUE;
414acdc433cSToomas Soome 		/* FALLTHROUGH */
4151d4b38e0Srsmaeda 	case DR_CPU_UNCONFIGURE:
4160c86d1bbSrsmaeda 		dr_fn = dr_cpu_unconfigure;
4170c86d1bbSrsmaeda 		drctl_cmd = DRCTL_CPU_UNCONFIG_REQUEST;
4180c86d1bbSrsmaeda 		se_hint = SE_HINT_REMOVE;
4191d4b38e0Srsmaeda 		break;
4201d4b38e0Srsmaeda 	default:
4211d4b38e0Srsmaeda 		/* Programming error if we reach this. */
42299c7e855SJames Marks - Sun Microsystems 		cmn_err(CE_NOTE,
42399c7e855SJames Marks - Sun Microsystems 		    "%s: bad msg_type %d\n", __func__, req->msg_type);
4241d4b38e0Srsmaeda 		ASSERT(0);
4251d4b38e0Srsmaeda 		return (-1);
4261ae08745Sheppo 	}
4271ae08745Sheppo 
4280c86d1bbSrsmaeda 	/* the incoming array of cpuids to operate on */
4290c86d1bbSrsmaeda 	req_cpus = DR_CPU_CMD_CPUIDS(req);
4301d4b38e0Srsmaeda 
4311d4b38e0Srsmaeda 	/* allocate drctl request msg based on incoming resource count */
4320c86d1bbSrsmaeda 	drctl_req_len = sizeof (drctl_rsrc_t) * count;
4330c86d1bbSrsmaeda 	drctl_req = kmem_zalloc(drctl_req_len, KM_SLEEP);
43499c7e855SJames Marks - Sun Microsystems 	DR_DBG_KMEM("%s: alloc addr %p size %ld\n",
43599c7e855SJames Marks - Sun Microsystems 	    __func__, (void *)drctl_req, drctl_req_len);
4361d4b38e0Srsmaeda 
4371d4b38e0Srsmaeda 	/* copy the cpuids for the drctl call from the incoming request msg */
4381d4b38e0Srsmaeda 	for (idx = 0; idx < count; idx++)
4390c86d1bbSrsmaeda 		drctl_req[idx].res_cpu_id = req_cpus[idx];
4401d4b38e0Srsmaeda 
44199c7e855SJames Marks - Sun Microsystems 	rv = drctl_config_init(drctl_cmd, drctl_flags, drctl_req,
44299c7e855SJames Marks - Sun Microsystems 	    count, &drctl_resp, &drctl_resp_len, &drctl_res_ck);
44399c7e855SJames Marks - Sun Microsystems 
44499c7e855SJames Marks - Sun Microsystems 	ASSERT((drctl_resp != NULL) && (drctl_resp_len != 0));
44599c7e855SJames Marks - Sun Microsystems 
44699c7e855SJames Marks - Sun Microsystems 	if (rv != 0) {
44799c7e855SJames Marks - Sun Microsystems 		DR_DBG_CPU("%s: drctl_config_init "
44899c7e855SJames Marks - Sun Microsystems 		    "returned: %d\n", __func__, rv);
44999c7e855SJames Marks - Sun Microsystems 
45099c7e855SJames Marks - Sun Microsystems 		if (DRCPU_VERS_EQ(1, 0)) {
45199c7e855SJames Marks - Sun Microsystems 			rv = -1;
45299c7e855SJames Marks - Sun Microsystems 		} else {
45399c7e855SJames Marks - Sun Microsystems 			ASSERT(DRCPU_VERS_GTEQ(1, 1));
45499c7e855SJames Marks - Sun Microsystems 			ASSERT(drctl_resp->resp_type == DRCTL_RESP_ERR);
45599c7e855SJames Marks - Sun Microsystems 
45699c7e855SJames Marks - Sun Microsystems 			*resp_len = dr_cpu_err_resp(req,
45799c7e855SJames Marks - Sun Microsystems 			    resp, drctl_resp->resp_err_msg);
4581d4b38e0Srsmaeda 		}
4591d4b38e0Srsmaeda 
46099c7e855SJames Marks - Sun Microsystems 		DR_DBG_KMEM("%s: free addr %p size %ld\n",
46199c7e855SJames Marks - Sun Microsystems 		    __func__, (void *)drctl_resp, drctl_resp_len);
46299c7e855SJames Marks - Sun Microsystems 		kmem_free(drctl_resp, drctl_resp_len);
46399c7e855SJames Marks - Sun Microsystems 		DR_DBG_KMEM("%s: free addr %p size %ld\n",
46499c7e855SJames Marks - Sun Microsystems 		    __func__, (void *)drctl_req, drctl_req_len);
46599c7e855SJames Marks - Sun Microsystems 		kmem_free(drctl_req, drctl_req_len);
46699c7e855SJames Marks - Sun Microsystems 
46799c7e855SJames Marks - Sun Microsystems 		return (rv);
46899c7e855SJames Marks - Sun Microsystems 	}
46999c7e855SJames Marks - Sun Microsystems 
47099c7e855SJames Marks - Sun Microsystems 	ASSERT(drctl_resp->resp_type == DRCTL_RESP_OK);
47199c7e855SJames Marks - Sun Microsystems 
47299c7e855SJames Marks - Sun Microsystems 	drctl_rsrc = drctl_resp->resp_resources;
4730c86d1bbSrsmaeda 
4740c86d1bbSrsmaeda 	/* create the result scratch array */
47599c7e855SJames Marks - Sun Microsystems 	res = dr_cpu_res_array_init(req, drctl_rsrc, count);
4761d4b38e0Srsmaeda 
4771d4b38e0Srsmaeda 	/*
4780c86d1bbSrsmaeda 	 * For unconfigure, check if there are any conditions
4790c86d1bbSrsmaeda 	 * that will cause the operation to fail. These are
4800c86d1bbSrsmaeda 	 * performed before the actual unconfigure attempt so
4810c86d1bbSrsmaeda 	 * that a meaningful error message can be generated.
4821d4b38e0Srsmaeda 	 */
4830c86d1bbSrsmaeda 	if (req->msg_type != DR_CPU_CONFIGURE)
4840c86d1bbSrsmaeda 		dr_cpu_check_cpus(req, res);
4851d4b38e0Srsmaeda 
4860c86d1bbSrsmaeda 	/* perform the specified operation on each of the CPUs */
4871d4b38e0Srsmaeda 	for (idx = 0; idx < count; idx++) {
4880c86d1bbSrsmaeda 		int result;
4890c86d1bbSrsmaeda 		int status;
4901d4b38e0Srsmaeda 
4911d4b38e0Srsmaeda 		/*
4920c86d1bbSrsmaeda 		 * If no action will be taken against the current
4930c86d1bbSrsmaeda 		 * CPU, update the drctl resource information to
4940c86d1bbSrsmaeda 		 * ensure that it gets recovered properly during
4950c86d1bbSrsmaeda 		 * the drctl fini() call.
4961d4b38e0Srsmaeda 		 */
4970c86d1bbSrsmaeda 		if (res[idx].result != DR_CPU_RES_OK) {
4980c86d1bbSrsmaeda 			drctl_req[idx].status = DRCTL_STATUS_CONFIG_FAILURE;
4990c86d1bbSrsmaeda 			continue;
5001d4b38e0Srsmaeda 		}
5011d4b38e0Srsmaeda 
5020c86d1bbSrsmaeda 		/* call the function to perform the actual operation */
5030c86d1bbSrsmaeda 		result = (*dr_fn)(req_cpus[idx], &status, force);
5041d4b38e0Srsmaeda 
5050c86d1bbSrsmaeda 		/* save off results of the operation */
5060c86d1bbSrsmaeda 		res[idx].result = result;
5070c86d1bbSrsmaeda 		res[idx].status = status;
5081d4b38e0Srsmaeda 
5090c86d1bbSrsmaeda 		/* save result for drctl fini() reusing init() msg memory */
5100c86d1bbSrsmaeda 		drctl_req[idx].status = (result != DR_CPU_RES_OK) ?
5110c86d1bbSrsmaeda 		    DRCTL_STATUS_CONFIG_FAILURE : DRCTL_STATUS_CONFIG_SUCCESS;
5121d4b38e0Srsmaeda 
5130c86d1bbSrsmaeda 		DR_DBG_CPU("%s: cpuid %d status %d result %d off '%s'\n",
51499c7e855SJames Marks - Sun Microsystems 		    __func__, req_cpus[idx], drctl_req[idx].status, result,
5150c86d1bbSrsmaeda 		    (res[idx].string) ? res[idx].string : "");
5160c86d1bbSrsmaeda 	}
5171d4b38e0Srsmaeda 
5180c86d1bbSrsmaeda 	if ((rv = drctl_config_fini(&drctl_res_ck, drctl_req, count)) != 0)
51999c7e855SJames Marks - Sun Microsystems 		DR_DBG_CPU("%s: drctl_config_fini "
52099c7e855SJames Marks - Sun Microsystems 		    "returned: %d\n", __func__, rv);
5211ae08745Sheppo 
5220c86d1bbSrsmaeda 	/*
5230c86d1bbSrsmaeda 	 * Operation completed without any fatal errors.
5240c86d1bbSrsmaeda 	 * Pack the response for transmission.
5250c86d1bbSrsmaeda 	 */
5260c86d1bbSrsmaeda 	*resp_len = dr_cpu_pack_response(req, res, resp);
5270c86d1bbSrsmaeda 
5280c86d1bbSrsmaeda 	/* notify interested parties about the operation */
5290c86d1bbSrsmaeda 	dr_generate_event(DR_TYPE_CPU, se_hint);
5300c86d1bbSrsmaeda 
5310c86d1bbSrsmaeda 	/*
5320c86d1bbSrsmaeda 	 * Deallocate any scratch memory.
5330c86d1bbSrsmaeda 	 */
53499c7e855SJames Marks - Sun Microsystems 	DR_DBG_KMEM("%s: free addr %p size %ld\n",
53599c7e855SJames Marks - Sun Microsystems 	    __func__, (void *)drctl_resp, drctl_resp_len);
53699c7e855SJames Marks - Sun Microsystems 	kmem_free(drctl_resp, drctl_resp_len);
53799c7e855SJames Marks - Sun Microsystems 	DR_DBG_KMEM("%s: free addr %p size %ld\n",
53899c7e855SJames Marks - Sun Microsystems 	    __func__, (void *)drctl_req, drctl_req_len);
5390c86d1bbSrsmaeda 	kmem_free(drctl_req, drctl_req_len);
5400c86d1bbSrsmaeda 
5410c86d1bbSrsmaeda 	dr_cpu_res_array_fini(res, count);
5421ae08745Sheppo 
5431ae08745Sheppo 	return (0);
5441ae08745Sheppo }
5451ae08745Sheppo 
5460c86d1bbSrsmaeda /*
5470c86d1bbSrsmaeda  * Allocate and initialize a result array based on the initial
5480c86d1bbSrsmaeda  * drctl operation. A valid result array is always returned.
5490c86d1bbSrsmaeda  */
5500c86d1bbSrsmaeda static dr_cpu_res_t *
dr_cpu_res_array_init(dr_cpu_hdr_t * req,drctl_rsrc_t * rsrc,int nrsrc)5510c86d1bbSrsmaeda dr_cpu_res_array_init(dr_cpu_hdr_t *req, drctl_rsrc_t *rsrc, int nrsrc)
5521ae08745Sheppo {
5531ae08745Sheppo 	int		idx;
5540c86d1bbSrsmaeda 	dr_cpu_res_t	*res;
5550c86d1bbSrsmaeda 	char		*err_str;
5560c86d1bbSrsmaeda 	size_t		err_len;
5570c86d1bbSrsmaeda 
5580c86d1bbSrsmaeda 	/* allocate zero filled buffer to initialize fields */
5590c86d1bbSrsmaeda 	res = kmem_zalloc(nrsrc * sizeof (dr_cpu_res_t), KM_SLEEP);
56099c7e855SJames Marks - Sun Microsystems 	DR_DBG_KMEM("%s: alloc addr %p size %ld\n",
56199c7e855SJames Marks - Sun Microsystems 	    __func__, (void *)res, nrsrc * sizeof (dr_cpu_res_t));
5620c86d1bbSrsmaeda 
5630c86d1bbSrsmaeda 	/*
5640c86d1bbSrsmaeda 	 * Fill in the result information for each resource.
5650c86d1bbSrsmaeda 	 */
5660c86d1bbSrsmaeda 	for (idx = 0; idx < nrsrc; idx++) {
5670c86d1bbSrsmaeda 		res[idx].cpuid = rsrc[idx].res_cpu_id;
5680c86d1bbSrsmaeda 		res[idx].result = DR_CPU_RES_OK;
5690c86d1bbSrsmaeda 
5700c86d1bbSrsmaeda 		if (rsrc[idx].status == DRCTL_STATUS_ALLOW)
5710c86d1bbSrsmaeda 			continue;
5720c86d1bbSrsmaeda 
5730c86d1bbSrsmaeda 		/*
5740c86d1bbSrsmaeda 		 * Update the state information for this CPU.
5750c86d1bbSrsmaeda 		 */
5760c86d1bbSrsmaeda 		res[idx].result = DR_CPU_RES_BLOCKED;
5770c86d1bbSrsmaeda 		res[idx].status = (req->msg_type == DR_CPU_CONFIGURE) ?
5780c86d1bbSrsmaeda 		    DR_CPU_STAT_UNCONFIGURED : DR_CPU_STAT_CONFIGURED;
5790c86d1bbSrsmaeda 
5800c86d1bbSrsmaeda 		/*
5810c86d1bbSrsmaeda 		 * If an error string exists, copy it out of the
5820c86d1bbSrsmaeda 		 * message buffer. This eliminates any dependency
5830c86d1bbSrsmaeda 		 * on the memory allocated for the message buffer
5840c86d1bbSrsmaeda 		 * itself.
5850c86d1bbSrsmaeda 		 */
586a6c18717SToomas Soome 		if (rsrc[idx].offset != 0) {
5870c86d1bbSrsmaeda 			err_str = (char *)rsrc + rsrc[idx].offset;
5880c86d1bbSrsmaeda 			err_len = strlen(err_str) + 1;
5890c86d1bbSrsmaeda 
5900c86d1bbSrsmaeda 			res[idx].string = kmem_alloc(err_len, KM_SLEEP);
59199c7e855SJames Marks - Sun Microsystems 			DR_DBG_KMEM("%s: alloc addr %p size %ld\n",
59299c7e855SJames Marks - Sun Microsystems 			    __func__, (void *)(res[idx].string), err_len);
5930c86d1bbSrsmaeda 			bcopy(err_str, res[idx].string, err_len);
5940c86d1bbSrsmaeda 		}
5950c86d1bbSrsmaeda 	}
5960c86d1bbSrsmaeda 
5970c86d1bbSrsmaeda 	return (res);
5980c86d1bbSrsmaeda }
5990c86d1bbSrsmaeda 
6000c86d1bbSrsmaeda static void
dr_cpu_res_array_fini(dr_cpu_res_t * res,int nres)6010c86d1bbSrsmaeda dr_cpu_res_array_fini(dr_cpu_res_t *res, int nres)
6020c86d1bbSrsmaeda {
6030c86d1bbSrsmaeda 	int	idx;
6040c86d1bbSrsmaeda 	size_t	str_len;
6050c86d1bbSrsmaeda 
6060c86d1bbSrsmaeda 	for (idx = 0; idx < nres; idx++) {
6070c86d1bbSrsmaeda 		/* deallocate the error string if present */
6080c86d1bbSrsmaeda 		if (res[idx].string) {
6090c86d1bbSrsmaeda 			str_len = strlen(res[idx].string) + 1;
61099c7e855SJames Marks - Sun Microsystems 			DR_DBG_KMEM("%s: free addr %p size %ld\n",
61199c7e855SJames Marks - Sun Microsystems 			    __func__, (void *)(res[idx].string), str_len);
6120c86d1bbSrsmaeda 			kmem_free(res[idx].string, str_len);
6130c86d1bbSrsmaeda 		}
6140c86d1bbSrsmaeda 	}
6150c86d1bbSrsmaeda 
6160c86d1bbSrsmaeda 	/* deallocate the result array itself */
61799c7e855SJames Marks - Sun Microsystems 	DR_DBG_KMEM("%s: free addr %p size %ld\n",
61899c7e855SJames Marks - Sun Microsystems 	    __func__, (void *)res, sizeof (dr_cpu_res_t) * nres);
6190c86d1bbSrsmaeda 	kmem_free(res, sizeof (dr_cpu_res_t) * nres);
6200c86d1bbSrsmaeda }
6210c86d1bbSrsmaeda 
6220c86d1bbSrsmaeda /*
6230c86d1bbSrsmaeda  * Allocate and pack a response message for transmission based
6240c86d1bbSrsmaeda  * on the specified result array. A valid response message and
6250c86d1bbSrsmaeda  * valid size information is always returned.
6260c86d1bbSrsmaeda  */
6270c86d1bbSrsmaeda static size_t
dr_cpu_pack_response(dr_cpu_hdr_t * req,dr_cpu_res_t * res,dr_cpu_hdr_t ** respp)6280c86d1bbSrsmaeda dr_cpu_pack_response(dr_cpu_hdr_t *req, dr_cpu_res_t *res, dr_cpu_hdr_t **respp)
6290c86d1bbSrsmaeda {
6300c86d1bbSrsmaeda 	int		idx;
6310c86d1bbSrsmaeda 	dr_cpu_hdr_t	*resp;
6320c86d1bbSrsmaeda 	dr_cpu_stat_t	*resp_stat;
6330c86d1bbSrsmaeda 	size_t		resp_len;
6340c86d1bbSrsmaeda 	uint32_t	curr_off;
6350c86d1bbSrsmaeda 	caddr_t		curr_str;
6360c86d1bbSrsmaeda 	size_t		str_len;
6370c86d1bbSrsmaeda 	size_t		stat_len;
6380c86d1bbSrsmaeda 	int		nstat = req->num_records;
6390c86d1bbSrsmaeda 
6400c86d1bbSrsmaeda 	/*
6410c86d1bbSrsmaeda 	 * Calculate the size of the response message
6420c86d1bbSrsmaeda 	 * and allocate an appropriately sized buffer.
6430c86d1bbSrsmaeda 	 */
6440c86d1bbSrsmaeda 	resp_len = 0;
6450c86d1bbSrsmaeda 
6460c86d1bbSrsmaeda 	/* add the header size */
6470c86d1bbSrsmaeda 	resp_len += sizeof (dr_cpu_hdr_t);
6480c86d1bbSrsmaeda 
6490c86d1bbSrsmaeda 	/* add the stat array size */
6500c86d1bbSrsmaeda 	stat_len = sizeof (dr_cpu_stat_t) * nstat;
6510c86d1bbSrsmaeda 	resp_len += stat_len;
6520c86d1bbSrsmaeda 
6530c86d1bbSrsmaeda 	/* add the size of any error strings */
6540c86d1bbSrsmaeda 	for (idx = 0; idx < nstat; idx++) {
6550c86d1bbSrsmaeda 		if (res[idx].string != NULL) {
6560c86d1bbSrsmaeda 			resp_len += strlen(res[idx].string) + 1;
6570c86d1bbSrsmaeda 		}
6580c86d1bbSrsmaeda 	}
6590c86d1bbSrsmaeda 
6600c86d1bbSrsmaeda 	/* allocate the message buffer */
6610c86d1bbSrsmaeda 	resp = kmem_zalloc(resp_len, KM_SLEEP);
66299c7e855SJames Marks - Sun Microsystems 	DR_DBG_KMEM("%s: alloc addr %p size %ld\n",
66399c7e855SJames Marks - Sun Microsystems 	    __func__, (void *)resp, resp_len);
6640c86d1bbSrsmaeda 
6650c86d1bbSrsmaeda 	/*
6660c86d1bbSrsmaeda 	 * Fill in the header information.
6670c86d1bbSrsmaeda 	 */
6680c86d1bbSrsmaeda 	resp->req_num = req->req_num;
6690c86d1bbSrsmaeda 	resp->msg_type = DR_CPU_OK;
6700c86d1bbSrsmaeda 	resp->num_records = nstat;
6710c86d1bbSrsmaeda 
6720c86d1bbSrsmaeda 	/*
6730c86d1bbSrsmaeda 	 * Fill in the stat information.
6740c86d1bbSrsmaeda 	 */
6750c86d1bbSrsmaeda 	resp_stat = DR_CPU_RESP_STATS(resp);
6760c86d1bbSrsmaeda 
6770c86d1bbSrsmaeda 	/* string offsets start immediately after stat array */
6780c86d1bbSrsmaeda 	curr_off = sizeof (dr_cpu_hdr_t) + stat_len;
6790c86d1bbSrsmaeda 	curr_str = (char *)resp_stat + stat_len;
6800c86d1bbSrsmaeda 
6810c86d1bbSrsmaeda 	for (idx = 0; idx < nstat; idx++) {
6820c86d1bbSrsmaeda 		resp_stat[idx].cpuid = res[idx].cpuid;
6830c86d1bbSrsmaeda 		resp_stat[idx].result = res[idx].result;
6840c86d1bbSrsmaeda 		resp_stat[idx].status = res[idx].status;
6850c86d1bbSrsmaeda 
6860c86d1bbSrsmaeda 		if (res[idx].string != NULL) {
6870c86d1bbSrsmaeda 			/* copy over the error string */
6880c86d1bbSrsmaeda 			str_len = strlen(res[idx].string) + 1;
6890c86d1bbSrsmaeda 			bcopy(res[idx].string, curr_str, str_len);
6900c86d1bbSrsmaeda 			resp_stat[idx].string_off = curr_off;
6910c86d1bbSrsmaeda 
6920c86d1bbSrsmaeda 			curr_off += str_len;
6930c86d1bbSrsmaeda 			curr_str += str_len;
6940c86d1bbSrsmaeda 		}
6950c86d1bbSrsmaeda 	}
6960c86d1bbSrsmaeda 
6970c86d1bbSrsmaeda 	/* buffer should be exactly filled */
6980c86d1bbSrsmaeda 	ASSERT(curr_off == resp_len);
6990c86d1bbSrsmaeda 
7000c86d1bbSrsmaeda 	*respp = resp;
7010c86d1bbSrsmaeda 	return (resp_len);
7020c86d1bbSrsmaeda }
7030c86d1bbSrsmaeda 
7040c86d1bbSrsmaeda /*
7050c86d1bbSrsmaeda  * Check for conditions that will prevent a CPU from being offlined.
7060c86d1bbSrsmaeda  * This provides the opportunity to generate useful information to
7070c86d1bbSrsmaeda  * help diagnose the failure rather than letting the offline attempt
7080c86d1bbSrsmaeda  * fail in a more generic way.
7090c86d1bbSrsmaeda  */
7100c86d1bbSrsmaeda static void
dr_cpu_check_cpus(dr_cpu_hdr_t * req,dr_cpu_res_t * res)7110c86d1bbSrsmaeda dr_cpu_check_cpus(dr_cpu_hdr_t *req, dr_cpu_res_t *res)
7120c86d1bbSrsmaeda {
7130c86d1bbSrsmaeda 	int		idx;
7140c86d1bbSrsmaeda 	cpu_t		*cp;
7150c86d1bbSrsmaeda 	uint32_t	*cpuids;
7160c86d1bbSrsmaeda 
7170c86d1bbSrsmaeda 	ASSERT((req->msg_type == DR_CPU_UNCONFIGURE) ||
7180c86d1bbSrsmaeda 	    (req->msg_type == DR_CPU_FORCE_UNCONFIG));
7191ae08745Sheppo 
7201ae08745Sheppo 	DR_DBG_CPU("dr_cpu_check_cpus...\n");
7211ae08745Sheppo 
7220c86d1bbSrsmaeda 	/* array of cpuids start just after the header */
7230c86d1bbSrsmaeda 	cpuids = DR_CPU_CMD_CPUIDS(req);
7240c86d1bbSrsmaeda 
7251ae08745Sheppo 	mutex_enter(&cpu_lock);
7261ae08745Sheppo 
7270c86d1bbSrsmaeda 	/*
7280c86d1bbSrsmaeda 	 * Always check processor set membership first. The
7290c86d1bbSrsmaeda 	 * last CPU in a processor set will fail to offline
7300c86d1bbSrsmaeda 	 * even if the operation if forced, so any failures
7310c86d1bbSrsmaeda 	 * should always be reported.
7320c86d1bbSrsmaeda 	 */
7330c86d1bbSrsmaeda 	dr_cpu_check_psrset(cpuids, res, req->num_records);
7341ae08745Sheppo 
7350c86d1bbSrsmaeda 	/* process each cpu that is part of the request */
7360c86d1bbSrsmaeda 	for (idx = 0; idx < req->num_records; idx++) {
7370c86d1bbSrsmaeda 
7380c86d1bbSrsmaeda 		/* nothing to check if the CPU has already failed */
7390c86d1bbSrsmaeda 		if (res[idx].result != DR_CPU_RES_OK)
7401ae08745Sheppo 			continue;
7411ae08745Sheppo 
7420c86d1bbSrsmaeda 		if ((cp = cpu_get(cpuids[idx])) == NULL)
7430c86d1bbSrsmaeda 			continue;
7441ae08745Sheppo 
7451ae08745Sheppo 		/*
7460c86d1bbSrsmaeda 		 * Only check if there are bound threads if the
7470c86d1bbSrsmaeda 		 * operation is not a forced unconfigure. In a
7480c86d1bbSrsmaeda 		 * forced request, threads are automatically
7490c86d1bbSrsmaeda 		 * unbound before they are offlined.
7501ae08745Sheppo 		 */
7510c86d1bbSrsmaeda 		if (req->msg_type == DR_CPU_UNCONFIGURE) {
7520c86d1bbSrsmaeda 			/*
7530c86d1bbSrsmaeda 			 * The return value is only interesting if other
7540c86d1bbSrsmaeda 			 * checks are added to this loop and a decision
7550c86d1bbSrsmaeda 			 * is needed on whether to continue checking.
7560c86d1bbSrsmaeda 			 */
7570c86d1bbSrsmaeda 			(void) dr_cpu_check_bound_thr(cp, &res[idx]);
7581ae08745Sheppo 		}
7591ae08745Sheppo 	}
7601ae08745Sheppo 
7611ae08745Sheppo 	mutex_exit(&cpu_lock);
7621ae08745Sheppo }
7631ae08745Sheppo 
7640c86d1bbSrsmaeda /*
7650c86d1bbSrsmaeda  * Examine the processor set configuration for the specified
7660c86d1bbSrsmaeda  * CPUs and see if the unconfigure operation would result in
7670c86d1bbSrsmaeda  * trying to remove the last CPU in any processor set.
7680c86d1bbSrsmaeda  */
7690c86d1bbSrsmaeda static void
dr_cpu_check_psrset(uint32_t * cpuids,dr_cpu_res_t * res,int nres)7700c86d1bbSrsmaeda dr_cpu_check_psrset(uint32_t *cpuids, dr_cpu_res_t *res, int nres)
7710c86d1bbSrsmaeda {
7720c86d1bbSrsmaeda 	int		cpu_idx;
7730c86d1bbSrsmaeda 	int		set_idx;
7740c86d1bbSrsmaeda 	cpu_t		*cp;
7750c86d1bbSrsmaeda 	cpupart_t	*cpp;
7760c86d1bbSrsmaeda 	char		err_str[DR_CPU_MAX_ERR_LEN];
7770c86d1bbSrsmaeda 	size_t		err_len;
7780c86d1bbSrsmaeda 	struct {
7790c86d1bbSrsmaeda 		cpupart_t	*cpp;
7800c86d1bbSrsmaeda 		int		ncpus;
7810c86d1bbSrsmaeda 	} *psrset;
7820c86d1bbSrsmaeda 
7830c86d1bbSrsmaeda 	ASSERT(MUTEX_HELD(&cpu_lock));
7840c86d1bbSrsmaeda 
7850c86d1bbSrsmaeda 	/*
7860c86d1bbSrsmaeda 	 * Allocate a scratch array to count the CPUs in
7870c86d1bbSrsmaeda 	 * the various processor sets. A CPU always belongs
7880c86d1bbSrsmaeda 	 * to exactly one processor set, so by definition,
7890c86d1bbSrsmaeda 	 * the scratch array never needs to be larger than
7900c86d1bbSrsmaeda 	 * the number of CPUs.
7910c86d1bbSrsmaeda 	 */
7920c86d1bbSrsmaeda 	psrset = kmem_zalloc(sizeof (*psrset) * nres, KM_SLEEP);
79399c7e855SJames Marks - Sun Microsystems 	DR_DBG_KMEM("%s: alloc addr %p size %ld\n",
79499c7e855SJames Marks - Sun Microsystems 	    __func__, (void *)psrset, sizeof (*psrset) * nres);
7950c86d1bbSrsmaeda 
7960c86d1bbSrsmaeda 	for (cpu_idx = 0; cpu_idx < nres; cpu_idx++) {
7970c86d1bbSrsmaeda 
7980c86d1bbSrsmaeda 		/* skip any CPUs that have already failed */
7990c86d1bbSrsmaeda 		if (res[cpu_idx].result != DR_CPU_RES_OK)
8000c86d1bbSrsmaeda 			continue;
8010c86d1bbSrsmaeda 
8020c86d1bbSrsmaeda 		if ((cp = cpu_get(cpuids[cpu_idx])) == NULL)
8030c86d1bbSrsmaeda 			continue;
8040c86d1bbSrsmaeda 
8050c86d1bbSrsmaeda 		cpp = cp->cpu_part;
8060c86d1bbSrsmaeda 
8070c86d1bbSrsmaeda 		/* lookup the set this CPU belongs to */
8080c86d1bbSrsmaeda 		for (set_idx = 0; set_idx < nres; set_idx++) {
8090c86d1bbSrsmaeda 
8100c86d1bbSrsmaeda 			/* matching set found */
8110c86d1bbSrsmaeda 			if (cpp == psrset[set_idx].cpp)
8120c86d1bbSrsmaeda 				break;
8130c86d1bbSrsmaeda 
8140c86d1bbSrsmaeda 			/* set not found, start a new entry */
8150c86d1bbSrsmaeda 			if (psrset[set_idx].cpp == NULL) {
8160c86d1bbSrsmaeda 				psrset[set_idx].cpp = cpp;
8170c86d1bbSrsmaeda 				psrset[set_idx].ncpus = cpp->cp_ncpus;
8180c86d1bbSrsmaeda 				break;
8190c86d1bbSrsmaeda 			}
8200c86d1bbSrsmaeda 		}
8210c86d1bbSrsmaeda 
8220c86d1bbSrsmaeda 		ASSERT(set_idx != nres);
8230c86d1bbSrsmaeda 
8240c86d1bbSrsmaeda 		/*
8250c86d1bbSrsmaeda 		 * Remove the current CPU from the set total but only
8260c86d1bbSrsmaeda 		 * generate an error for the last CPU. The correct CPU
8270c86d1bbSrsmaeda 		 * will get the error because the unconfigure attempts
8280c86d1bbSrsmaeda 		 * will occur in the same order in which the CPUs are
8295f1655c9SJames Marks - Sun Microsystems 		 * examined in this loop.  The cp_ncpus field of a
8305f1655c9SJames Marks - Sun Microsystems 		 * cpupart_t counts only online cpus, so it is safe
8315f1655c9SJames Marks - Sun Microsystems 		 * to remove an offline cpu without testing ncpus.
8320c86d1bbSrsmaeda 		 */
8337de586caSJames Marks - Sun Microsystems 		if (cpu_is_offline(cp))
8345f1655c9SJames Marks - Sun Microsystems 			continue;
8355f1655c9SJames Marks - Sun Microsystems 
8360c86d1bbSrsmaeda 		if (--psrset[set_idx].ncpus == 0) {
8370c86d1bbSrsmaeda 			/*
8380c86d1bbSrsmaeda 			 * Fill in the various pieces of information
8390c86d1bbSrsmaeda 			 * to report that the operation will fail.
8400c86d1bbSrsmaeda 			 */
8410c86d1bbSrsmaeda 			res[cpu_idx].result = DR_CPU_RES_BLOCKED;
8420c86d1bbSrsmaeda 			res[cpu_idx].status = DR_CPU_STAT_CONFIGURED;
8430c86d1bbSrsmaeda 
8440c86d1bbSrsmaeda 			(void) snprintf(err_str, DR_CPU_MAX_ERR_LEN,
8450c86d1bbSrsmaeda 			    "last online cpu in processor set %d", cpp->cp_id);
8460c86d1bbSrsmaeda 
8470c86d1bbSrsmaeda 			err_len = strlen(err_str) + 1;
8480c86d1bbSrsmaeda 
8490c86d1bbSrsmaeda 			res[cpu_idx].string = kmem_alloc(err_len, KM_SLEEP);
85099c7e855SJames Marks - Sun Microsystems 			DR_DBG_KMEM("%s: alloc addr %p size %ld\n",
85199c7e855SJames Marks - Sun Microsystems 			    __func__, (void *)(res[cpu_idx].string), err_len);
8520c86d1bbSrsmaeda 			bcopy(err_str, res[cpu_idx].string, err_len);
8530c86d1bbSrsmaeda 
8540c86d1bbSrsmaeda 			DR_DBG_CPU("cpu %d: %s\n", cpuids[cpu_idx], err_str);
8550c86d1bbSrsmaeda 		}
8560c86d1bbSrsmaeda 	}
8570c86d1bbSrsmaeda 
85899c7e855SJames Marks - Sun Microsystems 	DR_DBG_KMEM("%s: free addr %p size %ld\n",
85999c7e855SJames Marks - Sun Microsystems 	    __func__, (void *)psrset, sizeof (*psrset) * nres);
8600c86d1bbSrsmaeda 	kmem_free(psrset, sizeof (*psrset) * nres);
8610c86d1bbSrsmaeda }
8620c86d1bbSrsmaeda 
8630c86d1bbSrsmaeda /*
8640c86d1bbSrsmaeda  * Check if any threads are bound to the specified CPU. If the
8650c86d1bbSrsmaeda  * condition is true, DR_CPU_RES_BLOCKED is returned and an error
8660c86d1bbSrsmaeda  * string is generated and placed in the specified result structure.
8670c86d1bbSrsmaeda  * Otherwise, DR_CPU_RES_OK is returned.
8680c86d1bbSrsmaeda  */
8690c86d1bbSrsmaeda static int
dr_cpu_check_bound_thr(cpu_t * cp,dr_cpu_res_t * res)8700c86d1bbSrsmaeda dr_cpu_check_bound_thr(cpu_t *cp, dr_cpu_res_t *res)
8710c86d1bbSrsmaeda {
8720c86d1bbSrsmaeda 	int		nbound;
8730c86d1bbSrsmaeda 	proc_t		*pp;
8740c86d1bbSrsmaeda 	kthread_t	*tp;
8750c86d1bbSrsmaeda 	char		err_str[DR_CPU_MAX_ERR_LEN];
8760c86d1bbSrsmaeda 	size_t		err_len;
8770c86d1bbSrsmaeda 
8780c86d1bbSrsmaeda 	/*
8790c86d1bbSrsmaeda 	 * Error string allocation makes an assumption
8800c86d1bbSrsmaeda 	 * that no blocking condition has been identified.
8810c86d1bbSrsmaeda 	 */
8820c86d1bbSrsmaeda 	ASSERT(res->result == DR_CPU_RES_OK);
8830c86d1bbSrsmaeda 	ASSERT(res->string == NULL);
8840c86d1bbSrsmaeda 
8850c86d1bbSrsmaeda 	ASSERT(MUTEX_HELD(&cpu_lock));
8860c86d1bbSrsmaeda 
8870c86d1bbSrsmaeda 	mutex_enter(&pidlock);
8880c86d1bbSrsmaeda 
8890c86d1bbSrsmaeda 	nbound = 0;
8900c86d1bbSrsmaeda 
8910c86d1bbSrsmaeda 	/*
8920c86d1bbSrsmaeda 	 * Walk the active processes, checking if each
8930c86d1bbSrsmaeda 	 * thread belonging to the process is bound.
8940c86d1bbSrsmaeda 	 */
8950c86d1bbSrsmaeda 	for (pp = practive; (pp != NULL) && (nbound <= 1); pp = pp->p_next) {
8960c86d1bbSrsmaeda 		mutex_enter(&pp->p_lock);
8970c86d1bbSrsmaeda 
8980c86d1bbSrsmaeda 		tp = pp->p_tlist;
8990c86d1bbSrsmaeda 
9000c86d1bbSrsmaeda 		if ((tp == NULL) || (pp->p_flag & SSYS)) {
9010c86d1bbSrsmaeda 			mutex_exit(&pp->p_lock);
9020c86d1bbSrsmaeda 			continue;
9030c86d1bbSrsmaeda 		}
9040c86d1bbSrsmaeda 
9050c86d1bbSrsmaeda 		do {
9060c86d1bbSrsmaeda 			if (tp->t_bind_cpu != cp->cpu_id)
9070c86d1bbSrsmaeda 				continue;
9080c86d1bbSrsmaeda 
9090c86d1bbSrsmaeda 			/*
9100c86d1bbSrsmaeda 			 * Update the running total of bound
9110c86d1bbSrsmaeda 			 * threads. Continue the search until
9120c86d1bbSrsmaeda 			 * it can be determined if more than
9130c86d1bbSrsmaeda 			 * one thread is bound to the CPU.
9140c86d1bbSrsmaeda 			 */
9150c86d1bbSrsmaeda 			if (++nbound > 1)
9160c86d1bbSrsmaeda 				break;
9170c86d1bbSrsmaeda 
9180c86d1bbSrsmaeda 		} while ((tp = tp->t_forw) != pp->p_tlist);
9190c86d1bbSrsmaeda 
9200c86d1bbSrsmaeda 		mutex_exit(&pp->p_lock);
9210c86d1bbSrsmaeda 	}
9220c86d1bbSrsmaeda 
9230c86d1bbSrsmaeda 	mutex_exit(&pidlock);
9240c86d1bbSrsmaeda 
9250c86d1bbSrsmaeda 	if (nbound) {
9260c86d1bbSrsmaeda 		/*
9270c86d1bbSrsmaeda 		 * Threads are bound to the CPU. Fill in
9280c86d1bbSrsmaeda 		 * various pieces of information to report
9290c86d1bbSrsmaeda 		 * that the operation will fail.
9300c86d1bbSrsmaeda 		 */
9310c86d1bbSrsmaeda 		res->result = DR_CPU_RES_BLOCKED;
9320c86d1bbSrsmaeda 		res->status = DR_CPU_STAT_CONFIGURED;
9330c86d1bbSrsmaeda 
9340c86d1bbSrsmaeda 		(void) snprintf(err_str, DR_CPU_MAX_ERR_LEN, "cpu has bound "
9350c86d1bbSrsmaeda 		    "thread%s", (nbound > 1) ? "s" : "");
9360c86d1bbSrsmaeda 
9370c86d1bbSrsmaeda 		err_len = strlen(err_str) + 1;
9380c86d1bbSrsmaeda 
9390c86d1bbSrsmaeda 		res->string = kmem_alloc(err_len, KM_SLEEP);
94099c7e855SJames Marks - Sun Microsystems 		DR_DBG_KMEM("%s: alloc addr %p size %ld\n",
94199c7e855SJames Marks - Sun Microsystems 		    __func__, (void *)(res->string), err_len);
9420c86d1bbSrsmaeda 		bcopy(err_str, res->string, err_len);
9430c86d1bbSrsmaeda 
9440c86d1bbSrsmaeda 		DR_DBG_CPU("cpu %d: %s\n", cp->cpu_id, err_str);
9450c86d1bbSrsmaeda 	}
9460c86d1bbSrsmaeda 
9470c86d1bbSrsmaeda 	return (res->result);
9480c86d1bbSrsmaeda }
9491ae08745Sheppo 
9501ae08745Sheppo /*
9511ae08745Sheppo  * Do not modify result buffer or length on error.
9521ae08745Sheppo  */
9531ae08745Sheppo static int
dr_cpu_list_status(dr_cpu_hdr_t * req,dr_cpu_hdr_t ** resp,int * resp_len)9541ae08745Sheppo dr_cpu_list_status(dr_cpu_hdr_t *req, dr_cpu_hdr_t **resp, int *resp_len)
9551ae08745Sheppo {
9561ae08745Sheppo 	int		idx;
9571ae08745Sheppo 	int		result;
9581ae08745Sheppo 	int		status;
9591ae08745Sheppo 	int		rlen;
9601ae08745Sheppo 	uint32_t	*cpuids;
9611ae08745Sheppo 	dr_cpu_hdr_t	*rp;
9621ae08745Sheppo 	dr_cpu_stat_t	*stat;
9631ae08745Sheppo 	md_t		*mdp = NULL;
9641ae08745Sheppo 	int		num_nodes;
9651ae08745Sheppo 	int		listsz;
9661ae08745Sheppo 	mde_cookie_t	*listp = NULL;
9671ae08745Sheppo 	mde_cookie_t	cpunode;
9681ae08745Sheppo 	boolean_t	walk_md = B_FALSE;
9691ae08745Sheppo 
9701ae08745Sheppo 	/* the incoming array of cpuids to configure */
9710c86d1bbSrsmaeda 	cpuids = DR_CPU_CMD_CPUIDS(req);
9721ae08745Sheppo 
9731ae08745Sheppo 	/* allocate a response message */
9741ae08745Sheppo 	rlen = sizeof (dr_cpu_hdr_t);
9751ae08745Sheppo 	rlen += req->num_records * sizeof (dr_cpu_stat_t);
9761ae08745Sheppo 	rp = kmem_zalloc(rlen, KM_SLEEP);
97799c7e855SJames Marks - Sun Microsystems 	DR_DBG_KMEM("%s: alloc addr %p size %d\n", __func__, (void *)rp, rlen);
9781ae08745Sheppo 
9791ae08745Sheppo 	/* fill in the known data */
9801ae08745Sheppo 	rp->req_num = req->req_num;
9811ae08745Sheppo 	rp->msg_type = DR_CPU_STATUS;
9821ae08745Sheppo 	rp->num_records = req->num_records;
9831ae08745Sheppo 
9841ae08745Sheppo 	/* stat array for the response */
9850c86d1bbSrsmaeda 	stat = DR_CPU_RESP_STATS(rp);
9861ae08745Sheppo 
9871ae08745Sheppo 	/* get the status for each of the CPUs */
9881ae08745Sheppo 	for (idx = 0; idx < req->num_records; idx++) {
9891ae08745Sheppo 
9901ae08745Sheppo 		result = dr_cpu_status(cpuids[idx], &status);
9911ae08745Sheppo 
9921ae08745Sheppo 		if (result == DR_CPU_RES_FAILURE)
9931ae08745Sheppo 			walk_md = B_TRUE;
9941ae08745Sheppo 
9951ae08745Sheppo 		/* save off results of the status */
9961ae08745Sheppo 		stat[idx].cpuid = cpuids[idx];
9971ae08745Sheppo 		stat[idx].result = result;
9981ae08745Sheppo 		stat[idx].status = status;
9991ae08745Sheppo 	}
10001ae08745Sheppo 
10011ae08745Sheppo 	if (walk_md == B_FALSE)
10021ae08745Sheppo 		goto done;
10031ae08745Sheppo 
10041ae08745Sheppo 	/*
10051ae08745Sheppo 	 * At least one of the cpus did not have a CPU
10061ae08745Sheppo 	 * structure. So, consult the MD to determine if
10071ae08745Sheppo 	 * they are present.
10081ae08745Sheppo 	 */
10091ae08745Sheppo 
10101ae08745Sheppo 	if ((mdp = md_get_handle()) == NULL) {
10111ae08745Sheppo 		DR_DBG_CPU("unable to initialize MD\n");
10121ae08745Sheppo 		goto done;
10131ae08745Sheppo 	}
10141ae08745Sheppo 
10151ae08745Sheppo 	num_nodes = md_node_count(mdp);
10161ae08745Sheppo 	ASSERT(num_nodes > 0);
10171ae08745Sheppo 
10181ae08745Sheppo 	listsz = num_nodes * sizeof (mde_cookie_t);
10191ae08745Sheppo 	listp = kmem_zalloc(listsz, KM_SLEEP);
102099c7e855SJames Marks - Sun Microsystems 	DR_DBG_KMEM("%s: alloc addr %p size %d\n",
102199c7e855SJames Marks - Sun Microsystems 	    __func__, (void *)listp, listsz);
10221ae08745Sheppo 
10231ae08745Sheppo 	for (idx = 0; idx < req->num_records; idx++) {
10241ae08745Sheppo 
10251ae08745Sheppo 		if (stat[idx].result != DR_CPU_RES_FAILURE)
10261ae08745Sheppo 			continue;
10271ae08745Sheppo 
10281ae08745Sheppo 		/* check the MD for the current cpuid */
10291ae08745Sheppo 		cpunode = dr_cpu_find_node_md(stat[idx].cpuid, mdp, listp);
10301ae08745Sheppo 
10311ae08745Sheppo 		stat[idx].result = DR_CPU_RES_OK;
10321ae08745Sheppo 
10331ae08745Sheppo 		if (cpunode == MDE_INVAL_ELEM_COOKIE) {
10341ae08745Sheppo 			stat[idx].status = DR_CPU_STAT_NOT_PRESENT;
10351ae08745Sheppo 		} else {
10361ae08745Sheppo 			stat[idx].status = DR_CPU_STAT_UNCONFIGURED;
10371ae08745Sheppo 		}
10381ae08745Sheppo 	}
10391ae08745Sheppo 
104099c7e855SJames Marks - Sun Microsystems 	DR_DBG_KMEM("%s: free addr %p size %d\n",
104199c7e855SJames Marks - Sun Microsystems 	    __func__, (void *)listp, listsz);
10421ae08745Sheppo 	kmem_free(listp, listsz);
10431ae08745Sheppo 
10441ae08745Sheppo 	(void) md_fini_handle(mdp);
10451ae08745Sheppo 
10461ae08745Sheppo done:
10471ae08745Sheppo 	*resp = rp;
10481ae08745Sheppo 	*resp_len = rlen;
10491ae08745Sheppo 
10501ae08745Sheppo 	return (0);
10511ae08745Sheppo }
10521ae08745Sheppo 
10531ae08745Sheppo static int
dr_cpu_configure(processorid_t cpuid,int * status,boolean_t force)10541d4b38e0Srsmaeda dr_cpu_configure(processorid_t cpuid, int *status, boolean_t force)
10551ae08745Sheppo {
10561d4b38e0Srsmaeda 	 _NOTE(ARGUNUSED(force))
10571ae08745Sheppo 	struct cpu	*cp;
10581ae08745Sheppo 	int		rv = 0;
10591ae08745Sheppo 
10601ae08745Sheppo 	DR_DBG_CPU("dr_cpu_configure...\n");
10611ae08745Sheppo 
10621ae08745Sheppo 	/*
10631ae08745Sheppo 	 * Build device tree node for the CPU
10641ae08745Sheppo 	 */
10651ae08745Sheppo 	if ((rv = dr_cpu_probe(cpuid)) != 0) {
10661ae08745Sheppo 		DR_DBG_CPU("failed to probe CPU %d (%d)\n", cpuid, rv);
10671ae08745Sheppo 		if (rv == EINVAL) {
10681ae08745Sheppo 			*status = DR_CPU_STAT_NOT_PRESENT;
10691ae08745Sheppo 			return (DR_CPU_RES_NOT_IN_MD);
10701ae08745Sheppo 		}
10711ae08745Sheppo 		*status = DR_CPU_STAT_UNCONFIGURED;
10721ae08745Sheppo 		return (DR_CPU_RES_FAILURE);
10731ae08745Sheppo 	}
10741ae08745Sheppo 
10751ae08745Sheppo 	mutex_enter(&cpu_lock);
10761ae08745Sheppo 
10771ae08745Sheppo 	/*
10781ae08745Sheppo 	 * Configure the CPU
10791ae08745Sheppo 	 */
10801ae08745Sheppo 	if ((cp = cpu_get(cpuid)) == NULL) {
10811ae08745Sheppo 
10821ae08745Sheppo 		if ((rv = cpu_configure(cpuid)) != 0) {
10831ae08745Sheppo 			DR_DBG_CPU("failed to configure CPU %d (%d)\n",
10841ae08745Sheppo 			    cpuid, rv);
10851ae08745Sheppo 			rv = DR_CPU_RES_FAILURE;
10861ae08745Sheppo 			*status = DR_CPU_STAT_UNCONFIGURED;
10871ae08745Sheppo 			goto done;
10881ae08745Sheppo 		}
10891ae08745Sheppo 
10901ae08745Sheppo 		DR_DBG_CPU("CPU %d configured\n", cpuid);
10911ae08745Sheppo 
10921ae08745Sheppo 		/* CPU struct should exist now */
10931ae08745Sheppo 		cp = cpu_get(cpuid);
10941ae08745Sheppo 	}
10951ae08745Sheppo 
10961ae08745Sheppo 	ASSERT(cp);
10971ae08745Sheppo 
10981ae08745Sheppo 	/*
10991ae08745Sheppo 	 * Power on the CPU. In sun4v, this brings the stopped
11001ae08745Sheppo 	 * CPU into the guest from the Hypervisor.
11011ae08745Sheppo 	 */
11021ae08745Sheppo 	if (cpu_is_poweredoff(cp)) {
11031ae08745Sheppo 
11041ae08745Sheppo 		if ((rv = cpu_poweron(cp)) != 0) {
11051ae08745Sheppo 			DR_DBG_CPU("failed to power on CPU %d (%d)\n",
11061ae08745Sheppo 			    cpuid, rv);
11071ae08745Sheppo 			rv = DR_CPU_RES_FAILURE;
11081ae08745Sheppo 			*status = DR_CPU_STAT_UNCONFIGURED;
11091ae08745Sheppo 			goto done;
11101ae08745Sheppo 		}
11111ae08745Sheppo 
11121ae08745Sheppo 		DR_DBG_CPU("CPU %d powered on\n", cpuid);
11131ae08745Sheppo 	}
11141ae08745Sheppo 
11151ae08745Sheppo 	/*
11161ae08745Sheppo 	 * Online the CPU
11171ae08745Sheppo 	 */
11181ae08745Sheppo 	if (cpu_is_offline(cp)) {
11191ae08745Sheppo 
1120c3377ee9SJohn Levon 		if ((rv = cpu_online(cp, 0)) != 0) {
11211ae08745Sheppo 			DR_DBG_CPU("failed to online CPU %d (%d)\n",
11221ae08745Sheppo 			    cpuid, rv);
11231ae08745Sheppo 			rv = DR_CPU_RES_FAILURE;
11241ae08745Sheppo 			/* offline is still configured */
11251ae08745Sheppo 			*status = DR_CPU_STAT_CONFIGURED;
11261ae08745Sheppo 			goto done;
11271ae08745Sheppo 		}
11281ae08745Sheppo 
11291ae08745Sheppo 		DR_DBG_CPU("CPU %d online\n", cpuid);
11301ae08745Sheppo 	}
11311ae08745Sheppo 
11321ae08745Sheppo 	rv = DR_CPU_RES_OK;
11331ae08745Sheppo 	*status = DR_CPU_STAT_CONFIGURED;
11341ae08745Sheppo 
11351ae08745Sheppo done:
11361ae08745Sheppo 	mutex_exit(&cpu_lock);
11371ae08745Sheppo 
11381ae08745Sheppo 	return (rv);
11391ae08745Sheppo }
11401ae08745Sheppo 
11411ae08745Sheppo static int
dr_cpu_unconfigure(processorid_t cpuid,int * status,boolean_t force)11421ae08745Sheppo dr_cpu_unconfigure(processorid_t cpuid, int *status, boolean_t force)
11431ae08745Sheppo {
11441ae08745Sheppo 	struct cpu	*cp;
11451ae08745Sheppo 	int		rv = 0;
11461ae08745Sheppo 	int		cpu_flags;
11471ae08745Sheppo 
11481ae08745Sheppo 	DR_DBG_CPU("dr_cpu_unconfigure%s...\n", (force) ? " (force)" : "");
11491ae08745Sheppo 
11501ae08745Sheppo 	mutex_enter(&cpu_lock);
11511ae08745Sheppo 
11521ae08745Sheppo 	cp = cpu_get(cpuid);
11531ae08745Sheppo 
11541ae08745Sheppo 	if (cp == NULL) {
11551ae08745Sheppo 		/*
11564a9fd251SVijay Balakrishna, SG-RPE 		 * As OS CPU structures are already torn down proceed
11574a9fd251SVijay Balakrishna, SG-RPE 		 * to deprobe device tree to make sure the device tree
11584a9fd251SVijay Balakrishna, SG-RPE 		 * is up do date.
11591ae08745Sheppo 		 */
11604a9fd251SVijay Balakrishna, SG-RPE 		goto deprobe;
11611ae08745Sheppo 	}
11621ae08745Sheppo 
11631ae08745Sheppo 	ASSERT(cp->cpu_id == cpuid);
11641ae08745Sheppo 
11651ae08745Sheppo 	/*
11661ae08745Sheppo 	 * Offline the CPU
11671ae08745Sheppo 	 */
11681ae08745Sheppo 	if (cpu_is_active(cp)) {
11691ae08745Sheppo 
11701ae08745Sheppo 		/* set the force flag correctly */
11711ae08745Sheppo 		cpu_flags = (force) ? CPU_FORCED : 0;
11721ae08745Sheppo 
11737de586caSJames Marks - Sun Microsystems 		/*
11747de586caSJames Marks - Sun Microsystems 		 * Before we take the CPU offline, we first enable interrupts.
11757de586caSJames Marks - Sun Microsystems 		 * Otherwise, cpu_offline() might reject the request.  Note:
11767de586caSJames Marks - Sun Microsystems 		 * if the offline subsequently fails, the target cpu will be
11777de586caSJames Marks - Sun Microsystems 		 * left with interrupts enabled.  This is consistent with the
1178*bbf21555SRichard Lowe 		 * behavior of psradm(8) and p_online(2).
11797de586caSJames Marks - Sun Microsystems 		 */
11807de586caSJames Marks - Sun Microsystems 		cpu_intr_enable(cp);
11817de586caSJames Marks - Sun Microsystems 
11821ae08745Sheppo 		if ((rv = cpu_offline(cp, cpu_flags)) != 0) {
11831ae08745Sheppo 			DR_DBG_CPU("failed to offline CPU %d (%d)\n",
11841ae08745Sheppo 			    cpuid, rv);
11851ae08745Sheppo 
11861ae08745Sheppo 			rv = DR_CPU_RES_FAILURE;
11871ae08745Sheppo 			*status = DR_CPU_STAT_CONFIGURED;
11884a9fd251SVijay Balakrishna, SG-RPE 			mutex_exit(&cpu_lock);
11894a9fd251SVijay Balakrishna, SG-RPE 			return (rv);
11901ae08745Sheppo 		}
11911ae08745Sheppo 
11921ae08745Sheppo 		DR_DBG_CPU("CPU %d offline\n", cpuid);
11931ae08745Sheppo 	}
11941ae08745Sheppo 
11951ae08745Sheppo 	/*
11961ae08745Sheppo 	 * Power off the CPU. In sun4v, this puts the running
11971ae08745Sheppo 	 * CPU into the stopped state in the Hypervisor.
11981ae08745Sheppo 	 */
11991ae08745Sheppo 	if (!cpu_is_poweredoff(cp)) {
12001ae08745Sheppo 
12011ae08745Sheppo 		if ((rv = cpu_poweroff(cp)) != 0) {
12021ae08745Sheppo 			DR_DBG_CPU("failed to power off CPU %d (%d)\n",
12031ae08745Sheppo 			    cpuid, rv);
12041ae08745Sheppo 			rv = DR_CPU_RES_FAILURE;
12051ae08745Sheppo 			*status = DR_CPU_STAT_CONFIGURED;
12064a9fd251SVijay Balakrishna, SG-RPE 			mutex_exit(&cpu_lock);
12074a9fd251SVijay Balakrishna, SG-RPE 			return (rv);
12081ae08745Sheppo 		}
12091ae08745Sheppo 
12101ae08745Sheppo 		DR_DBG_CPU("CPU %d powered off\n", cpuid);
12111ae08745Sheppo 	}
12121ae08745Sheppo 
12131ae08745Sheppo 	/*
12141ae08745Sheppo 	 * Unconfigure the CPU
12151ae08745Sheppo 	 */
12161ae08745Sheppo 	if ((rv = cpu_unconfigure(cpuid)) != 0) {
12171ae08745Sheppo 		DR_DBG_CPU("failed to unconfigure CPU %d (%d)\n", cpuid, rv);
12181ae08745Sheppo 		rv = DR_CPU_RES_FAILURE;
12191ae08745Sheppo 		*status = DR_CPU_STAT_UNCONFIGURED;
12204a9fd251SVijay Balakrishna, SG-RPE 		mutex_exit(&cpu_lock);
12214a9fd251SVijay Balakrishna, SG-RPE 		return (rv);
12221ae08745Sheppo 	}
12231ae08745Sheppo 
12241ae08745Sheppo 	DR_DBG_CPU("CPU %d unconfigured\n", cpuid);
12251ae08745Sheppo 
12264a9fd251SVijay Balakrishna, SG-RPE deprobe:
12274a9fd251SVijay Balakrishna, SG-RPE 	mutex_exit(&cpu_lock);
12281ae08745Sheppo 	/*
12291ae08745Sheppo 	 * Tear down device tree.
12301ae08745Sheppo 	 */
12311ae08745Sheppo 	if ((rv = dr_cpu_deprobe(cpuid)) != 0) {
12321ae08745Sheppo 		DR_DBG_CPU("failed to deprobe CPU %d (%d)\n", cpuid, rv);
12331ae08745Sheppo 		rv = DR_CPU_RES_FAILURE;
12341ae08745Sheppo 		*status = DR_CPU_STAT_UNCONFIGURED;
12354a9fd251SVijay Balakrishna, SG-RPE 		return (rv);
12361ae08745Sheppo 	}
12371ae08745Sheppo 
12381ae08745Sheppo 	rv = DR_CPU_RES_OK;
12391ae08745Sheppo 	*status = DR_CPU_STAT_UNCONFIGURED;
12401ae08745Sheppo 
12411ae08745Sheppo 	return (rv);
12421ae08745Sheppo }
12431ae08745Sheppo 
12441ae08745Sheppo /*
12451ae08745Sheppo  * Determine the state of a CPU. If the CPU structure is not present,
12461ae08745Sheppo  * it does not attempt to determine whether or not the CPU is in the
12471ae08745Sheppo  * MD. It is more efficient to do this at the higher level for all
12481ae08745Sheppo  * CPUs since it may not even be necessary to search the MD if all
12491ae08745Sheppo  * the CPUs are accounted for. Returns DR_CPU_RES_OK if the CPU
12501ae08745Sheppo  * structure is present, and DR_CPU_RES_FAILURE otherwise as a signal
12511ae08745Sheppo  * that an MD walk is necessary.
12521ae08745Sheppo  */
12531ae08745Sheppo static int
dr_cpu_status(processorid_t cpuid,int * status)12541ae08745Sheppo dr_cpu_status(processorid_t cpuid, int *status)
12551ae08745Sheppo {
12561ae08745Sheppo 	int		rv;
12571ae08745Sheppo 	struct cpu	*cp;
12581ae08745Sheppo 
12591ae08745Sheppo 	DR_DBG_CPU("dr_cpu_status...\n");
12601ae08745Sheppo 
12611ae08745Sheppo 	mutex_enter(&cpu_lock);
12621ae08745Sheppo 
12631ae08745Sheppo 	if ((cp = cpu_get(cpuid)) == NULL) {
12641ae08745Sheppo 		/* need to check if cpu is in the MD */
12651ae08745Sheppo 		rv = DR_CPU_RES_FAILURE;
12661ae08745Sheppo 		goto done;
12671ae08745Sheppo 	}
12681ae08745Sheppo 
12691ae08745Sheppo 	if (cpu_is_poweredoff(cp)) {
12701ae08745Sheppo 		/*
12711ae08745Sheppo 		 * The CPU is powered off, so it is considered
12721ae08745Sheppo 		 * unconfigured from the service entity point of
12731ae08745Sheppo 		 * view. The CPU is not available to the system
12741ae08745Sheppo 		 * and intervention by the service entity would
12751ae08745Sheppo 		 * be required to change that.
12761ae08745Sheppo 		 */
12771ae08745Sheppo 		*status = DR_CPU_STAT_UNCONFIGURED;
12781ae08745Sheppo 	} else {
12791ae08745Sheppo 		/*
12801ae08745Sheppo 		 * The CPU is powered on, so it is considered
12811ae08745Sheppo 		 * configured from the service entity point of
12821ae08745Sheppo 		 * view. It is available for use by the system
12831ae08745Sheppo 		 * and service entities are not concerned about
12841ae08745Sheppo 		 * the operational status (offline, online, etc.)
12851ae08745Sheppo 		 * of the CPU in terms of DR.
12861ae08745Sheppo 		 */
12871ae08745Sheppo 		*status = DR_CPU_STAT_CONFIGURED;
12881ae08745Sheppo 	}
12891ae08745Sheppo 
12901ae08745Sheppo 	rv = DR_CPU_RES_OK;
12911ae08745Sheppo 
12921ae08745Sheppo done:
12931ae08745Sheppo 	mutex_exit(&cpu_lock);
12941ae08745Sheppo 
12951ae08745Sheppo 	return (rv);
12961ae08745Sheppo }
12971ae08745Sheppo 
12981ae08745Sheppo typedef struct {
12991ae08745Sheppo 	md_t		*mdp;
13001ae08745Sheppo 	mde_cookie_t	cpunode;
13011ae08745Sheppo 	dev_info_t	*dip;
13021ae08745Sheppo } cb_arg_t;
13031ae08745Sheppo 
13041ae08745Sheppo #define	STR_ARR_LEN	5
13051ae08745Sheppo 
13061ae08745Sheppo static int
new_cpu_node(dev_info_t * new_node,void * arg,uint_t flags)13071ae08745Sheppo new_cpu_node(dev_info_t *new_node, void *arg, uint_t flags)
13081ae08745Sheppo {
13091ae08745Sheppo 	_NOTE(ARGUNUSED(flags))
13101ae08745Sheppo 
13111ae08745Sheppo 	char		*compat;
13121ae08745Sheppo 	uint64_t	freq;
13131ae08745Sheppo 	uint64_t	cpuid = 0;
13141ae08745Sheppo 	int		regbuf[4];
13151ae08745Sheppo 	int		len = 0;
13161ae08745Sheppo 	cb_arg_t	*cba;
13171ae08745Sheppo 	char		*str_arr[STR_ARR_LEN];
13181ae08745Sheppo 	char		*curr;
13191ae08745Sheppo 	int		idx = 0;
13201ae08745Sheppo 
13211ae08745Sheppo 	DR_DBG_CPU("new_cpu_node...\n");
13221ae08745Sheppo 
13231ae08745Sheppo 	cba = (cb_arg_t *)arg;
13241ae08745Sheppo 
13251ae08745Sheppo 	/*
13261ae08745Sheppo 	 * Add 'name' property
13271ae08745Sheppo 	 */
13281ae08745Sheppo 	if (ndi_prop_update_string(DDI_DEV_T_NONE, new_node,
13291ae08745Sheppo 	    "name", "cpu") != DDI_SUCCESS) {
13301ae08745Sheppo 		DR_DBG_CPU("new_cpu_node: failed to create 'name' property\n");
13311ae08745Sheppo 		return (DDI_WALK_ERROR);
13321ae08745Sheppo 	}
13331ae08745Sheppo 
13341ae08745Sheppo 	/*
13351ae08745Sheppo 	 * Add 'compatible' property
13361ae08745Sheppo 	 */
13371ae08745Sheppo 	if (md_get_prop_data(cba->mdp, cba->cpunode, "compatible",
13381ae08745Sheppo 	    (uint8_t **)(&compat), &len)) {
13391ae08745Sheppo 		DR_DBG_CPU("new_cpu_node: failed to read 'compatible' property "
13401ae08745Sheppo 		    "from MD\n");
13411ae08745Sheppo 		return (DDI_WALK_ERROR);
13421ae08745Sheppo 	}
13431ae08745Sheppo 
13441ae08745Sheppo 	DR_DBG_CPU("'compatible' len is %d\n", len);
13451ae08745Sheppo 
13461ae08745Sheppo 	/* parse the MD string array */
13471ae08745Sheppo 	curr = compat;
13481ae08745Sheppo 	while (curr < (compat + len)) {
13491ae08745Sheppo 
13501ae08745Sheppo 		DR_DBG_CPU("adding '%s' to 'compatible' property\n", curr);
13511ae08745Sheppo 
13521ae08745Sheppo 		str_arr[idx++] = curr;
13531ae08745Sheppo 		curr += strlen(curr) + 1;
13541ae08745Sheppo 
13551ae08745Sheppo 		if (idx == STR_ARR_LEN) {
13561ae08745Sheppo 			DR_DBG_CPU("exceeded str_arr len (%d)\n", STR_ARR_LEN);
13571ae08745Sheppo 			break;
13581ae08745Sheppo 		}
13591ae08745Sheppo 	}
13601ae08745Sheppo 
13611ae08745Sheppo 	if (ndi_prop_update_string_array(DDI_DEV_T_NONE, new_node,
13621ae08745Sheppo 	    "compatible", str_arr, idx) != DDI_SUCCESS) {
13631ae08745Sheppo 		DR_DBG_CPU("new_cpu_node: failed to create 'compatible' "
13641ae08745Sheppo 		    "property\n");
13651ae08745Sheppo 		return (DDI_WALK_ERROR);
13661ae08745Sheppo 	}
13671ae08745Sheppo 
13681ae08745Sheppo 	/*
13691ae08745Sheppo 	 * Add 'device_type' property
13701ae08745Sheppo 	 */
13711ae08745Sheppo 	if (ndi_prop_update_string(DDI_DEV_T_NONE, new_node,
13721ae08745Sheppo 	    "device_type", "cpu") != DDI_SUCCESS) {
13731ae08745Sheppo 		DR_DBG_CPU("new_cpu_node: failed to create 'device_type' "
13741ae08745Sheppo 		    "property\n");
13751ae08745Sheppo 		return (DDI_WALK_ERROR);
13761ae08745Sheppo 	}
13771ae08745Sheppo 
13781ae08745Sheppo 	/*
13791ae08745Sheppo 	 * Add 'clock-frequency' property
13801ae08745Sheppo 	 */
13811ae08745Sheppo 	if (md_get_prop_val(cba->mdp, cba->cpunode, "clock-frequency", &freq)) {
13821ae08745Sheppo 		DR_DBG_CPU("new_cpu_node: failed to read 'clock-frequency' "
13831ae08745Sheppo 		    "property from MD\n");
13841ae08745Sheppo 		return (DDI_WALK_ERROR);
13851ae08745Sheppo 	}
13861ae08745Sheppo 
13871ae08745Sheppo 	if (ndi_prop_update_int(DDI_DEV_T_NONE, new_node,
13881ae08745Sheppo 	    "clock-frequency", freq) != DDI_SUCCESS) {
13891ae08745Sheppo 		DR_DBG_CPU("new_cpu_node: failed to create 'clock-frequency' "
13901ae08745Sheppo 		    "property\n");
13911ae08745Sheppo 		return (DDI_WALK_ERROR);
13921ae08745Sheppo 	}
13931ae08745Sheppo 
13941ae08745Sheppo 	/*
13951ae08745Sheppo 	 * Add 'reg' (cpuid) property
13961ae08745Sheppo 	 */
13971ae08745Sheppo 	if (md_get_prop_val(cba->mdp, cba->cpunode, "id", &cpuid)) {
13981ae08745Sheppo 		DR_DBG_CPU("new_cpu_node: failed to read 'id' property "
13991ae08745Sheppo 		    "from MD\n");
14001ae08745Sheppo 		return (DDI_WALK_ERROR);
14011ae08745Sheppo 	}
14021ae08745Sheppo 
14031ae08745Sheppo 	DR_DBG_CPU("new cpuid=0x%lx\n", cpuid);
14041ae08745Sheppo 
14051ae08745Sheppo 	bzero(regbuf, 4 * sizeof (int));
14061ae08745Sheppo 	regbuf[0] = 0xc0000000 | cpuid;
14071ae08745Sheppo 
14081ae08745Sheppo 	if (ndi_prop_update_int_array(DDI_DEV_T_NONE, new_node,
14091ae08745Sheppo 	    "reg", regbuf, 4) != DDI_SUCCESS) {
14101ae08745Sheppo 		DR_DBG_CPU("new_cpu_node: failed to create 'reg' property\n");
14111ae08745Sheppo 		return (DDI_WALK_ERROR);
14121ae08745Sheppo 	}
14131ae08745Sheppo 
14141ae08745Sheppo 	cba->dip = new_node;
14151ae08745Sheppo 
14161ae08745Sheppo 	return (DDI_WALK_TERMINATE);
14171ae08745Sheppo }
14181ae08745Sheppo 
14191ae08745Sheppo static int
dr_cpu_probe(processorid_t cpuid)14201ae08745Sheppo dr_cpu_probe(processorid_t cpuid)
14211ae08745Sheppo {
14221ae08745Sheppo 	dev_info_t	*pdip;
14231ae08745Sheppo 	dev_info_t	*dip;
14241ae08745Sheppo 	devi_branch_t	br;
14251ae08745Sheppo 	md_t		*mdp = NULL;
14261ae08745Sheppo 	int		num_nodes;
14271ae08745Sheppo 	int		rv = 0;
14281ae08745Sheppo 	int		listsz;
14291ae08745Sheppo 	mde_cookie_t	*listp = NULL;
14301ae08745Sheppo 	cb_arg_t	cba;
14311ae08745Sheppo 	mde_cookie_t	cpunode;
14321ae08745Sheppo 
14331ae08745Sheppo 	if ((dip = dr_cpu_find_node(cpuid)) != NULL) {
14341ae08745Sheppo 		/* nothing to do */
14351ae08745Sheppo 		e_ddi_branch_rele(dip);
14361ae08745Sheppo 		return (0);
14371ae08745Sheppo 	}
14381ae08745Sheppo 
14391ae08745Sheppo 	if ((mdp = md_get_handle()) == NULL) {
14401ae08745Sheppo 		DR_DBG_CPU("unable to initialize machine description\n");
14411ae08745Sheppo 		return (-1);
14421ae08745Sheppo 	}
14431ae08745Sheppo 
14441ae08745Sheppo 	num_nodes = md_node_count(mdp);
14451ae08745Sheppo 	ASSERT(num_nodes > 0);
14461ae08745Sheppo 
14471ae08745Sheppo 	listsz = num_nodes * sizeof (mde_cookie_t);
14481ae08745Sheppo 	listp = kmem_zalloc(listsz, KM_SLEEP);
144999c7e855SJames Marks - Sun Microsystems 	DR_DBG_KMEM("%s: alloc addr %p size %d\n",
145099c7e855SJames Marks - Sun Microsystems 	    __func__, (void *)listp, listsz);
14511ae08745Sheppo 
14521ae08745Sheppo 	cpunode = dr_cpu_find_node_md(cpuid, mdp, listp);
14531ae08745Sheppo 
14541ae08745Sheppo 	if (cpunode == MDE_INVAL_ELEM_COOKIE) {
14551ae08745Sheppo 		rv = EINVAL;
14561ae08745Sheppo 		goto done;
14571ae08745Sheppo 	}
14581ae08745Sheppo 
14591ae08745Sheppo 	/* pass in MD cookie for CPU */
14601ae08745Sheppo 	cba.mdp = mdp;
14611ae08745Sheppo 	cba.cpunode = cpunode;
14621ae08745Sheppo 
14631ae08745Sheppo 	br.arg = (void *)&cba;
14641ae08745Sheppo 	br.type = DEVI_BRANCH_SID;
14651ae08745Sheppo 	br.create.sid_branch_create = new_cpu_node;
14661ae08745Sheppo 	br.devi_branch_callback = NULL;
14671ae08745Sheppo 	pdip = ddi_root_node();
14681ae08745Sheppo 
14691ae08745Sheppo 	if ((rv = e_ddi_branch_create(pdip, &br, NULL, 0))) {
14701ae08745Sheppo 		DR_DBG_CPU("e_ddi_branch_create failed: %d\n", rv);
14711ae08745Sheppo 		rv = -1;
14721ae08745Sheppo 		goto done;
14731ae08745Sheppo 	}
14741ae08745Sheppo 
14751ae08745Sheppo 	DR_DBG_CPU("CPU %d probed\n", cpuid);
14761ae08745Sheppo 
14771ae08745Sheppo 	rv = 0;
14781ae08745Sheppo 
14791ae08745Sheppo done:
148099c7e855SJames Marks - Sun Microsystems 	if (listp) {
148199c7e855SJames Marks - Sun Microsystems 		DR_DBG_KMEM("%s: free addr %p size %d\n",
148299c7e855SJames Marks - Sun Microsystems 		    __func__, (void *)listp, listsz);
14831ae08745Sheppo 		kmem_free(listp, listsz);
148499c7e855SJames Marks - Sun Microsystems 	}
14851ae08745Sheppo 
14861ae08745Sheppo 	if (mdp)
14871ae08745Sheppo 		(void) md_fini_handle(mdp);
14881ae08745Sheppo 
14891ae08745Sheppo 	return (rv);
14901ae08745Sheppo }
14911ae08745Sheppo 
14921ae08745Sheppo static int
dr_cpu_deprobe(processorid_t cpuid)14931ae08745Sheppo dr_cpu_deprobe(processorid_t cpuid)
14941ae08745Sheppo {
14951ae08745Sheppo 	dev_info_t	*fdip = NULL;
14961ae08745Sheppo 	dev_info_t	*dip;
14971ae08745Sheppo 
14981ae08745Sheppo 	if ((dip = dr_cpu_find_node(cpuid)) == NULL) {
14991ae08745Sheppo 		DR_DBG_CPU("cpuid %d already deprobed\n", cpuid);
15001ae08745Sheppo 		return (0);
15011ae08745Sheppo 	}
15021ae08745Sheppo 
15031ae08745Sheppo 	ASSERT(e_ddi_branch_held(dip));
15041ae08745Sheppo 
15051ae08745Sheppo 	if (e_ddi_branch_destroy(dip, &fdip, 0)) {
15061ae08745Sheppo 		char *path = kmem_alloc(MAXPATHLEN, KM_SLEEP);
15071ae08745Sheppo 
150899c7e855SJames Marks - Sun Microsystems 		DR_DBG_KMEM("%s: alloc addr %p size %d\n",
150999c7e855SJames Marks - Sun Microsystems 		    __func__, (void *)path, MAXPATHLEN);
15101ae08745Sheppo 		/*
15111ae08745Sheppo 		 * If non-NULL, fdip is held and must be released.
15121ae08745Sheppo 		 */
15131ae08745Sheppo 		if (fdip != NULL) {
15141ae08745Sheppo 			(void) ddi_pathname(fdip, path);
15151ae08745Sheppo 			ddi_release_devi(fdip);
15161ae08745Sheppo 		} else {
15171ae08745Sheppo 			(void) ddi_pathname(dip, path);
15181ae08745Sheppo 		}
15191ae08745Sheppo 		cmn_err(CE_NOTE, "node removal failed: %s (%p)",
15201ae08745Sheppo 		    path, (fdip) ? (void *)fdip : (void *)dip);
15211ae08745Sheppo 
152299c7e855SJames Marks - Sun Microsystems 		DR_DBG_KMEM("%s: free addr %p size %d\n",
152399c7e855SJames Marks - Sun Microsystems 		    __func__, (void *)path, MAXPATHLEN);
15241ae08745Sheppo 		kmem_free(path, MAXPATHLEN);
15251ae08745Sheppo 
15261ae08745Sheppo 		return (-1);
15271ae08745Sheppo 	}
15281ae08745Sheppo 
15291ae08745Sheppo 	DR_DBG_CPU("CPU %d deprobed\n", cpuid);
15301ae08745Sheppo 
15311ae08745Sheppo 	return (0);
15321ae08745Sheppo }
15331ae08745Sheppo 
15341ae08745Sheppo typedef struct {
15351ae08745Sheppo 	processorid_t	cpuid;
15361ae08745Sheppo 	dev_info_t	*dip;
15371ae08745Sheppo } dr_search_arg_t;
15381ae08745Sheppo 
15391ae08745Sheppo static int
dr_cpu_check_node(dev_info_t * dip,void * arg)15401ae08745Sheppo dr_cpu_check_node(dev_info_t *dip, void *arg)
15411ae08745Sheppo {
15421ae08745Sheppo 	char		*name;
15431ae08745Sheppo 	processorid_t	cpuid;
15441ae08745Sheppo 	dr_search_arg_t	*sarg = (dr_search_arg_t *)arg;
15451ae08745Sheppo 
15461ae08745Sheppo 	if (dip == ddi_root_node()) {
15471ae08745Sheppo 		return (DDI_WALK_CONTINUE);
15481ae08745Sheppo 	}
15491ae08745Sheppo 
15501ae08745Sheppo 	name = ddi_node_name(dip);
15511ae08745Sheppo 
15521ae08745Sheppo 	if (strcmp(name, "cpu") != 0) {
15531ae08745Sheppo 		return (DDI_WALK_PRUNECHILD);
15541ae08745Sheppo 	}
15551ae08745Sheppo 
15561ae08745Sheppo 	cpuid = ddi_prop_get_int(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS,
15571ae08745Sheppo 	    "reg", -1);
15581ae08745Sheppo 
15591ae08745Sheppo 	cpuid = PROM_CFGHDL_TO_CPUID(cpuid);
15601ae08745Sheppo 
15611ae08745Sheppo 	DR_DBG_CPU("found cpuid=0x%x, looking for 0x%x\n", cpuid, sarg->cpuid);
15621ae08745Sheppo 
15631ae08745Sheppo 	if (cpuid == sarg->cpuid) {
15641ae08745Sheppo 		DR_DBG_CPU("matching node\n");
15651ae08745Sheppo 
15661ae08745Sheppo 		/* matching node must be returned held */
15671ae08745Sheppo 		if (!e_ddi_branch_held(dip))
15681ae08745Sheppo 			e_ddi_branch_hold(dip);
15691ae08745Sheppo 
15701ae08745Sheppo 		sarg->dip = dip;
15711ae08745Sheppo 		return (DDI_WALK_TERMINATE);
15721ae08745Sheppo 	}
15731ae08745Sheppo 
15741ae08745Sheppo 	return (DDI_WALK_CONTINUE);
15751ae08745Sheppo }
15761ae08745Sheppo 
15771ae08745Sheppo /*
15781ae08745Sheppo  * Walk the device tree to find the dip corresponding to the cpuid
15791ae08745Sheppo  * passed in. If present, the dip is returned held. The caller must
15801ae08745Sheppo  * release the hold on the dip once it is no longer required. If no
15811ae08745Sheppo  * matching node if found, NULL is returned.
15821ae08745Sheppo  */
15831ae08745Sheppo static dev_info_t *
dr_cpu_find_node(processorid_t cpuid)15841ae08745Sheppo dr_cpu_find_node(processorid_t cpuid)
15851ae08745Sheppo {
15861ae08745Sheppo 	dr_search_arg_t	arg;
15871ae08745Sheppo 
15881ae08745Sheppo 	DR_DBG_CPU("dr_cpu_find_node...\n");
15891ae08745Sheppo 
15901ae08745Sheppo 	arg.cpuid = cpuid;
15911ae08745Sheppo 	arg.dip = NULL;
15921ae08745Sheppo 
15931ae08745Sheppo 	ddi_walk_devs(ddi_root_node(), dr_cpu_check_node, &arg);
15941ae08745Sheppo 
15951ae08745Sheppo 	ASSERT((arg.dip == NULL) || (e_ddi_branch_held(arg.dip)));
15961ae08745Sheppo 
15971ae08745Sheppo 	return ((arg.dip) ? arg.dip : NULL);
15981ae08745Sheppo }
15991ae08745Sheppo 
16001ae08745Sheppo /*
16011ae08745Sheppo  * Look up a particular cpuid in the MD. Returns the mde_cookie_t
16021ae08745Sheppo  * representing that CPU if present, and MDE_INVAL_ELEM_COOKIE
16031ae08745Sheppo  * otherwise. It is assumed the scratch array has already been
16041ae08745Sheppo  * allocated so that it can accommodate the worst case scenario,
16051ae08745Sheppo  * every node in the MD.
16061ae08745Sheppo  */
16071ae08745Sheppo static mde_cookie_t
dr_cpu_find_node_md(processorid_t cpuid,md_t * mdp,mde_cookie_t * listp)16081ae08745Sheppo dr_cpu_find_node_md(processorid_t cpuid, md_t *mdp, mde_cookie_t *listp)
16091ae08745Sheppo {
16101ae08745Sheppo 	int		idx;
16111ae08745Sheppo 	int		nnodes;
16121ae08745Sheppo 	mde_cookie_t	rootnode;
16131ae08745Sheppo 	uint64_t	cpuid_prop;
16141ae08745Sheppo 	mde_cookie_t	result = MDE_INVAL_ELEM_COOKIE;
16151ae08745Sheppo 
16161ae08745Sheppo 	rootnode = md_root_node(mdp);
16171ae08745Sheppo 	ASSERT(rootnode != MDE_INVAL_ELEM_COOKIE);
16181ae08745Sheppo 
16191ae08745Sheppo 	/*
16201ae08745Sheppo 	 * Scan the DAG for all the CPU nodes
16211ae08745Sheppo 	 */
16221ae08745Sheppo 	nnodes = md_scan_dag(mdp, rootnode, md_find_name(mdp, "cpu"),
16231ae08745Sheppo 	    md_find_name(mdp, "fwd"), listp);
16241ae08745Sheppo 
16251ae08745Sheppo 	if (nnodes < 0) {
16261ae08745Sheppo 		DR_DBG_CPU("Scan for CPUs failed\n");
16271ae08745Sheppo 		return (result);
16281ae08745Sheppo 	}
16291ae08745Sheppo 
16301ae08745Sheppo 	DR_DBG_CPU("dr_cpu_find_node_md: found %d CPUs in the MD\n", nnodes);
16311ae08745Sheppo 
16321ae08745Sheppo 	/*
16331ae08745Sheppo 	 * Find the CPU of interest
16341ae08745Sheppo 	 */
16351ae08745Sheppo 	for (idx = 0; idx < nnodes; idx++) {
16361ae08745Sheppo 
16371ae08745Sheppo 		if (md_get_prop_val(mdp, listp[idx], "id", &cpuid_prop)) {
16381ae08745Sheppo 			DR_DBG_CPU("Missing 'id' property for CPU node %d\n",
16391ae08745Sheppo 			    idx);
16401ae08745Sheppo 			break;
16411ae08745Sheppo 		}
16421ae08745Sheppo 
16431ae08745Sheppo 		if (cpuid_prop == cpuid) {
16441ae08745Sheppo 			/* found a match */
16451ae08745Sheppo 			DR_DBG_CPU("dr_cpu_find_node_md: found CPU %d "
16461ae08745Sheppo 			    "in MD\n", cpuid);
16471ae08745Sheppo 			result = listp[idx];
16481ae08745Sheppo 			break;
16491ae08745Sheppo 		}
16501ae08745Sheppo 	}
16511ae08745Sheppo 
16521ae08745Sheppo 	if (result == MDE_INVAL_ELEM_COOKIE) {
16531ae08745Sheppo 		DR_DBG_CPU("CPU %d not in MD\n", cpuid);
16541ae08745Sheppo 	}
16551ae08745Sheppo 
16561ae08745Sheppo 	return (result);
16571ae08745Sheppo }
1658