xref: /onnv-gate/usr/src/uts/sun4v/io/dr_cpu.c (revision 9255:2e981b22694e)
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 /*
238616SJames.Marks@Sun.COM  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
241991Sheppo  * Use is subject to license terms.
251991Sheppo  */
261991Sheppo 
271991Sheppo /*
281991Sheppo  * sun4v CPU DR Module
291991Sheppo  */
301991Sheppo 
311991Sheppo #include <sys/modctl.h>
321991Sheppo #include <sys/processor.h>
331991Sheppo #include <sys/cpuvar.h>
343414Srsmaeda #include <sys/cpupart.h>
351991Sheppo #include <sys/sunddi.h>
361991Sheppo #include <sys/sunndi.h>
371991Sheppo #include <sys/note.h>
381991Sheppo #include <sys/sysevent/dr.h>
391991Sheppo #include <sys/hypervisor_api.h>
401991Sheppo #include <sys/mach_descrip.h>
411991Sheppo #include <sys/mdesc.h>
421991Sheppo #include <sys/ds.h>
432309Srsmaeda #include <sys/drctl.h>
441991Sheppo #include <sys/dr_util.h>
451991Sheppo #include <sys/dr_cpu.h>
461991Sheppo #include <sys/promif.h>
471991Sheppo #include <sys/machsystm.h>
481991Sheppo 
491991Sheppo 
501991Sheppo static struct modlmisc modlmisc = {
511991Sheppo 	&mod_miscops,
527799SRichard.Bean@Sun.COM 	"sun4v CPU DR"
531991Sheppo };
541991Sheppo 
551991Sheppo static struct modlinkage modlinkage = {
561991Sheppo 	MODREV_1,
571991Sheppo 	(void *)&modlmisc,
581991Sheppo 	NULL
591991Sheppo };
601991Sheppo 
612309Srsmaeda typedef int (*fn_t)(processorid_t, int *, boolean_t);
622309Srsmaeda 
631991Sheppo /*
641991Sheppo  * Global DS Handle
651991Sheppo  */
661991Sheppo static ds_svc_hdl_t ds_handle;
671991Sheppo 
681991Sheppo /*
691991Sheppo  * Supported DS Capability Versions
701991Sheppo  */
717899SJames.Marks@Sun.COM static ds_ver_t		dr_cpu_vers[] = { { 1, 1 }, { 1, 0 } };
721991Sheppo #define	DR_CPU_NVERS	(sizeof (dr_cpu_vers) / sizeof (dr_cpu_vers[0]))
731991Sheppo 
747899SJames.Marks@Sun.COM static ds_ver_t		version;
757899SJames.Marks@Sun.COM 
761991Sheppo /*
771991Sheppo  * DS Capability Description
781991Sheppo  */
791991Sheppo static ds_capability_t dr_cpu_cap = {
801991Sheppo 	DR_CPU_DS_ID,		/* svc_id */
811991Sheppo 	dr_cpu_vers,		/* vers */
821991Sheppo 	DR_CPU_NVERS		/* nvers */
831991Sheppo };
841991Sheppo 
857899SJames.Marks@Sun.COM #define	DRCPU_VERS_EQ(_maj, _min) \
867899SJames.Marks@Sun.COM 	((version.major == (_maj)) && (version.minor == (_min)))
877899SJames.Marks@Sun.COM 
887899SJames.Marks@Sun.COM #define	DRCPU_VERS_GTEQ(_maj, _min) \
897899SJames.Marks@Sun.COM 	((version.major > (_maj)) ||					\
907899SJames.Marks@Sun.COM 	((version.major == (_maj)) && (version.minor >= (_min))))
917899SJames.Marks@Sun.COM 
921991Sheppo /*
931991Sheppo  * DS Callbacks
941991Sheppo  */
951991Sheppo static void dr_cpu_reg_handler(ds_cb_arg_t, ds_ver_t *, ds_svc_hdl_t);
961991Sheppo static void dr_cpu_unreg_handler(ds_cb_arg_t arg);
971991Sheppo static void dr_cpu_data_handler(ds_cb_arg_t arg, void *buf, size_t buflen);
981991Sheppo 
991991Sheppo /*
1001991Sheppo  * DS Client Ops Vector
1011991Sheppo  */
1021991Sheppo static ds_clnt_ops_t dr_cpu_ops = {
1031991Sheppo 	dr_cpu_reg_handler,	/* ds_reg_cb */
1041991Sheppo 	dr_cpu_unreg_handler,	/* ds_unreg_cb */
1051991Sheppo 	dr_cpu_data_handler,	/* ds_data_cb */
1061991Sheppo 	NULL			/* cb_arg */
1071991Sheppo };
1081991Sheppo 
1091991Sheppo /*
1103414Srsmaeda  * Operation Results
1113414Srsmaeda  *
1123414Srsmaeda  * Used internally to gather results while an operation on a
1133414Srsmaeda  * list of CPUs is in progress. In particular, it is used to
1143414Srsmaeda  * keep track of which CPUs have already failed so that they are
1153414Srsmaeda  * not processed further, and the manner in which they failed.
1163414Srsmaeda  */
1173414Srsmaeda typedef struct {
1183414Srsmaeda 	uint32_t	cpuid;
1193414Srsmaeda 	uint32_t	result;
1203414Srsmaeda 	uint32_t	status;
1213414Srsmaeda 	char		*string;
1223414Srsmaeda } dr_cpu_res_t;
1233414Srsmaeda 
1243414Srsmaeda #define	DR_CPU_MAX_ERR_LEN	64	/* maximum error string length */
1253414Srsmaeda 
1263414Srsmaeda /*
1271991Sheppo  * Internal Functions
1281991Sheppo  */
1291991Sheppo static int dr_cpu_init(void);
1301991Sheppo static int dr_cpu_fini(void);
1311991Sheppo 
1323414Srsmaeda static int dr_cpu_list_wrk(dr_cpu_hdr_t *, dr_cpu_hdr_t **, int *);
1331991Sheppo static int dr_cpu_list_status(dr_cpu_hdr_t *, dr_cpu_hdr_t **, int *);
1341991Sheppo 
1351991Sheppo static int dr_cpu_unconfigure(processorid_t, int *status, boolean_t force);
1362309Srsmaeda static int dr_cpu_configure(processorid_t, int *status, boolean_t force);
1371991Sheppo static int dr_cpu_status(processorid_t, int *status);
1381991Sheppo 
1393414Srsmaeda static void dr_cpu_check_cpus(dr_cpu_hdr_t *req, dr_cpu_res_t *res);
1403414Srsmaeda static void dr_cpu_check_psrset(uint32_t *cpuids, dr_cpu_res_t *res, int nres);
1413414Srsmaeda static int dr_cpu_check_bound_thr(cpu_t *cp, dr_cpu_res_t *res);
1423414Srsmaeda 
1433414Srsmaeda static dr_cpu_res_t *dr_cpu_res_array_init(dr_cpu_hdr_t *, drctl_rsrc_t *, int);
1443414Srsmaeda static void dr_cpu_res_array_fini(dr_cpu_res_t *res, int nres);
1453414Srsmaeda static size_t dr_cpu_pack_response(dr_cpu_hdr_t *req, dr_cpu_res_t *res,
1463414Srsmaeda     dr_cpu_hdr_t **respp);
1473414Srsmaeda 
1481991Sheppo static int dr_cpu_probe(processorid_t newcpuid);
1491991Sheppo static int dr_cpu_deprobe(processorid_t cpuid);
1501991Sheppo 
1511991Sheppo static dev_info_t *dr_cpu_find_node(processorid_t cpuid);
1521991Sheppo static mde_cookie_t dr_cpu_find_node_md(processorid_t, md_t *, mde_cookie_t *);
1531991Sheppo 
1541991Sheppo int
_init(void)1551991Sheppo _init(void)
1561991Sheppo {
1571991Sheppo 	int	status;
1581991Sheppo 
1591991Sheppo 	/* check that CPU DR is enabled */
1601991Sheppo 	if (dr_is_disabled(DR_TYPE_CPU)) {
1611991Sheppo 		cmn_err(CE_CONT, "!CPU DR is disabled\n");
1621991Sheppo 		return (-1);
1631991Sheppo 	}
1641991Sheppo 
1651991Sheppo 	if ((status = dr_cpu_init()) != 0) {
1661991Sheppo 		cmn_err(CE_NOTE, "CPU DR initialization failed");
1671991Sheppo 		return (status);
1681991Sheppo 	}
1691991Sheppo 
1701991Sheppo 	if ((status = mod_install(&modlinkage)) != 0) {
1711991Sheppo 		(void) dr_cpu_fini();
1721991Sheppo 	}
1731991Sheppo 
1741991Sheppo 	return (status);
1751991Sheppo }
1761991Sheppo 
1771991Sheppo int
_info(struct modinfo * modinfop)1781991Sheppo _info(struct modinfo *modinfop)
1791991Sheppo {
1801991Sheppo 	return (mod_info(&modlinkage, modinfop));
1811991Sheppo }
1821991Sheppo 
1831991Sheppo int dr_cpu_allow_unload;
1841991Sheppo 
1851991Sheppo int
_fini(void)1861991Sheppo _fini(void)
1871991Sheppo {
1881991Sheppo 	int	status;
1891991Sheppo 
1901991Sheppo 	if (dr_cpu_allow_unload == 0)
1911991Sheppo 		return (EBUSY);
1921991Sheppo 
1931991Sheppo 	if ((status = mod_remove(&modlinkage)) == 0) {
1941991Sheppo 		(void) dr_cpu_fini();
1951991Sheppo 	}
1961991Sheppo 
1971991Sheppo 	return (status);
1981991Sheppo }
1991991Sheppo 
2001991Sheppo static int
dr_cpu_init(void)2011991Sheppo dr_cpu_init(void)
2021991Sheppo {
2031991Sheppo 	int	rv;
2041991Sheppo 
2051991Sheppo 	if ((rv = ds_cap_init(&dr_cpu_cap, &dr_cpu_ops)) != 0) {
2061991Sheppo 		cmn_err(CE_NOTE, "ds_cap_init failed: %d", rv);
2071991Sheppo 		return (-1);
2081991Sheppo 	}
2091991Sheppo 
2101991Sheppo 	return (0);
2111991Sheppo }
2121991Sheppo 
2131991Sheppo static int
dr_cpu_fini(void)2141991Sheppo dr_cpu_fini(void)
2151991Sheppo {
2161991Sheppo 	int	rv;
2171991Sheppo 
2181991Sheppo 	if ((rv = ds_cap_fini(&dr_cpu_cap)) != 0) {
2191991Sheppo 		cmn_err(CE_NOTE, "ds_cap_fini failed: %d", rv);
2201991Sheppo 		return (-1);
2211991Sheppo 	}
2221991Sheppo 
2231991Sheppo 	return (0);
2241991Sheppo }
2251991Sheppo 
2261991Sheppo static void
dr_cpu_reg_handler(ds_cb_arg_t arg,ds_ver_t * ver,ds_svc_hdl_t hdl)2271991Sheppo dr_cpu_reg_handler(ds_cb_arg_t arg, ds_ver_t *ver, ds_svc_hdl_t hdl)
2281991Sheppo {
2291991Sheppo 	DR_DBG_CPU("reg_handler: arg=0x%p, ver=%d.%d, hdl=0x%lx\n", arg,
2301991Sheppo 	    ver->major, ver->minor, hdl);
2311991Sheppo 
2327899SJames.Marks@Sun.COM 	version.major = ver->major;
2337899SJames.Marks@Sun.COM 	version.minor = ver->minor;
2341991Sheppo 	ds_handle = hdl;
2351991Sheppo }
2361991Sheppo 
2371991Sheppo static void
dr_cpu_unreg_handler(ds_cb_arg_t arg)2381991Sheppo dr_cpu_unreg_handler(ds_cb_arg_t arg)
2391991Sheppo {
2401991Sheppo 	DR_DBG_CPU("unreg_handler: arg=0x%p\n", arg);
2411991Sheppo 
2421991Sheppo 	ds_handle = DS_INVALID_HDL;
2431991Sheppo }
2441991Sheppo 
2451991Sheppo static void
dr_cpu_data_handler(ds_cb_arg_t arg,void * buf,size_t buflen)2461991Sheppo dr_cpu_data_handler(ds_cb_arg_t arg, void *buf, size_t buflen)
2471991Sheppo {
2481991Sheppo 	_NOTE(ARGUNUSED(arg))
2491991Sheppo 
2501991Sheppo 	dr_cpu_hdr_t	*req = buf;
2511991Sheppo 	dr_cpu_hdr_t	err_resp;
2521991Sheppo 	dr_cpu_hdr_t	*resp = &err_resp;
2531991Sheppo 	int		resp_len = 0;
2541991Sheppo 	int		rv;
2551991Sheppo 
2561991Sheppo 	/*
2571991Sheppo 	 * Sanity check the message
2581991Sheppo 	 */
2591991Sheppo 	if (buflen < sizeof (dr_cpu_hdr_t)) {
2601991Sheppo 		DR_DBG_CPU("incoming message short: expected at least %ld "
2611991Sheppo 		    "bytes, received %ld\n", sizeof (dr_cpu_hdr_t), buflen);
2621991Sheppo 		goto done;
2631991Sheppo 	}
2641991Sheppo 
2651991Sheppo 	if (req == NULL) {
2661991Sheppo 		DR_DBG_CPU("empty message: expected at least %ld bytes\n",
2671991Sheppo 		    sizeof (dr_cpu_hdr_t));
2681991Sheppo 		goto done;
2691991Sheppo 	}
2701991Sheppo 
2711991Sheppo 	DR_DBG_CPU("incoming request:\n");
2721991Sheppo 	DR_DBG_DUMP_MSG(buf, buflen);
2731991Sheppo 
2741991Sheppo 	if (req->num_records > NCPU) {
2751991Sheppo 		DR_DBG_CPU("CPU list too long: %d when %d is the maximum\n",
2761991Sheppo 		    req->num_records, NCPU);
2771991Sheppo 		goto done;
2781991Sheppo 	}
2791991Sheppo 
2801991Sheppo 	if (req->num_records == 0) {
2811991Sheppo 		DR_DBG_CPU("No CPU specified for operation\n");
2821991Sheppo 		goto done;
2831991Sheppo 	}
2841991Sheppo 
2851991Sheppo 	/*
2861991Sheppo 	 * Process the command
2871991Sheppo 	 */
2881991Sheppo 	switch (req->msg_type) {
2891991Sheppo 	case DR_CPU_CONFIGURE:
2901991Sheppo 	case DR_CPU_UNCONFIGURE:
2911991Sheppo 	case DR_CPU_FORCE_UNCONFIG:
2923414Srsmaeda 		if ((rv = dr_cpu_list_wrk(req, &resp, &resp_len)) != 0) {
2933414Srsmaeda 			DR_DBG_CPU("%s%s failed (%d)\n",
2943414Srsmaeda 			    (req->msg_type == DR_CPU_CONFIGURE) ?
2953414Srsmaeda 			    "CPU configure" : "CPU unconfigure",
2963414Srsmaeda 			    (req->msg_type == DR_CPU_FORCE_UNCONFIG) ?
2973414Srsmaeda 			    " (forced)" : "", rv);
2983414Srsmaeda 		}
2991991Sheppo 		break;
3001991Sheppo 
3011991Sheppo 	case DR_CPU_STATUS:
3021991Sheppo 		if ((rv = dr_cpu_list_status(req, &resp, &resp_len)) != 0)
3033414Srsmaeda 			DR_DBG_CPU("CPU status failed (%d)\n", rv);
3041991Sheppo 		break;
3051991Sheppo 
3061991Sheppo 	default:
3071991Sheppo 		cmn_err(CE_NOTE, "unsupported DR operation (%d)",
3081991Sheppo 		    req->msg_type);
3091991Sheppo 		break;
3101991Sheppo 	}
3111991Sheppo 
3121991Sheppo done:
3131991Sheppo 	/* check if an error occurred */
3141991Sheppo 	if (resp == &err_resp) {
3151991Sheppo 		resp->req_num = (req) ? req->req_num : 0;
3161991Sheppo 		resp->msg_type = DR_CPU_ERROR;
3171991Sheppo 		resp->num_records = 0;
3181991Sheppo 		resp_len = sizeof (dr_cpu_hdr_t);
3191991Sheppo 	}
3201991Sheppo 
3213414Srsmaeda 	DR_DBG_CPU("outgoing response:\n");
3223414Srsmaeda 	DR_DBG_DUMP_MSG(resp, resp_len);
3233414Srsmaeda 
3241991Sheppo 	/* send back the response */
3251991Sheppo 	if (ds_cap_send(ds_handle, resp, resp_len) != 0) {
3261991Sheppo 		DR_DBG_CPU("ds_send failed\n");
3271991Sheppo 	}
3281991Sheppo 
3291991Sheppo 	/* free any allocated memory */
3307899SJames.Marks@Sun.COM 	if (DRCPU_VERS_GTEQ(1, 1) || (resp != &err_resp)) {
3317899SJames.Marks@Sun.COM 		DR_DBG_KMEM("%s: free addr %p size %d\n",
3327899SJames.Marks@Sun.COM 		    __func__, (void *)resp, resp_len);
3331991Sheppo 		kmem_free(resp, resp_len);
3341991Sheppo 	}
3351991Sheppo }
3361991Sheppo 
3371991Sheppo /*
3387899SJames.Marks@Sun.COM  * Create a response message which consists of a header followed
3397899SJames.Marks@Sun.COM  * by the error string passed in.
3407899SJames.Marks@Sun.COM  */
3417899SJames.Marks@Sun.COM static size_t
dr_cpu_err_resp(dr_cpu_hdr_t * req,dr_cpu_hdr_t ** respp,char * msg)3427899SJames.Marks@Sun.COM dr_cpu_err_resp(dr_cpu_hdr_t *req, dr_cpu_hdr_t **respp, char *msg)
3437899SJames.Marks@Sun.COM {
3447899SJames.Marks@Sun.COM 	size_t size;
3457899SJames.Marks@Sun.COM 	dr_cpu_hdr_t *resp;
3467899SJames.Marks@Sun.COM 
3477899SJames.Marks@Sun.COM 	ASSERT((msg != NULL) && (strlen(msg) > 0));
3487899SJames.Marks@Sun.COM 
3497899SJames.Marks@Sun.COM 	size = sizeof (*req) + strlen(msg) + 1;
3507899SJames.Marks@Sun.COM 	resp = kmem_alloc(size, KM_SLEEP);
3517899SJames.Marks@Sun.COM 	DR_DBG_KMEM("%s: alloc addr %p size %ld\n",
3527899SJames.Marks@Sun.COM 	    __func__, (void *)resp, size);
3537899SJames.Marks@Sun.COM 
3547899SJames.Marks@Sun.COM 	resp->req_num = req->req_num;
3557899SJames.Marks@Sun.COM 	resp->msg_type = DR_CPU_ERROR;
3567899SJames.Marks@Sun.COM 	resp->num_records = 0;
3577899SJames.Marks@Sun.COM 
3587899SJames.Marks@Sun.COM 	(void) strcpy((char *)(resp) + sizeof (*resp), msg);
3597899SJames.Marks@Sun.COM 
3607899SJames.Marks@Sun.COM 	*respp = resp;
3617899SJames.Marks@Sun.COM 
3627899SJames.Marks@Sun.COM 	return (size);
3637899SJames.Marks@Sun.COM }
3647899SJames.Marks@Sun.COM 
3657899SJames.Marks@Sun.COM /*
3662309Srsmaeda  * Common routine to config or unconfig multiple cpus.  The unconfig
3672309Srsmaeda  * case checks with the OS to see if the removal of cpus will be
3682309Srsmaeda  * permitted, but can be overridden by the "force" version of the
3692309Srsmaeda  * command.  Otherwise, the logic for both cases is identical.
3702309Srsmaeda  *
3712309Srsmaeda  * Note: Do not modify result buffer or length on error.
3721991Sheppo  */
3731991Sheppo static int
dr_cpu_list_wrk(dr_cpu_hdr_t * req,dr_cpu_hdr_t ** resp,int * resp_len)3743414Srsmaeda dr_cpu_list_wrk(dr_cpu_hdr_t *req, dr_cpu_hdr_t **resp, int *resp_len)
3751991Sheppo {
3763414Srsmaeda 	int		rv;
3773414Srsmaeda 	int		idx;
3783414Srsmaeda 	int		count;
3793414Srsmaeda 	fn_t		dr_fn;
3803414Srsmaeda 	int		se_hint;
3813414Srsmaeda 	boolean_t	force = B_FALSE;
3823414Srsmaeda 	uint32_t	*req_cpus;
3833414Srsmaeda 	dr_cpu_res_t	*res;
3843414Srsmaeda 	int		drctl_cmd;
3853414Srsmaeda 	int		drctl_flags = 0;
3863414Srsmaeda 	drctl_rsrc_t	*drctl_req;
3873414Srsmaeda 	size_t		drctl_req_len;
3887899SJames.Marks@Sun.COM 	drctl_resp_t	*drctl_resp;
3897899SJames.Marks@Sun.COM 	drctl_rsrc_t	*drctl_rsrc;
3907899SJames.Marks@Sun.COM 	size_t		drctl_resp_len = 0;
3913414Srsmaeda 	drctl_cookie_t	drctl_res_ck;
3922309Srsmaeda 
3933414Srsmaeda 	ASSERT((req != NULL) && (req->num_records != 0));
3942309Srsmaeda 
3953414Srsmaeda 	count = req->num_records;
3962309Srsmaeda 
3973414Srsmaeda 	/*
3983414Srsmaeda 	 * Extract all information that is specific
3993414Srsmaeda 	 * to the various types of operations.
4003414Srsmaeda 	 */
4013414Srsmaeda 	switch (req->msg_type) {
4022309Srsmaeda 	case DR_CPU_CONFIGURE:
4033414Srsmaeda 		dr_fn = dr_cpu_configure;
4043414Srsmaeda 		drctl_cmd = DRCTL_CPU_CONFIG_REQUEST;
4053414Srsmaeda 		se_hint = SE_HINT_INSERT;
4062309Srsmaeda 		break;
4072309Srsmaeda 	case DR_CPU_FORCE_UNCONFIG:
4083414Srsmaeda 		drctl_flags = DRCTL_FLAG_FORCE;
4092309Srsmaeda 		force = B_TRUE;
4102309Srsmaeda 		_NOTE(FALLTHROUGH)
4112309Srsmaeda 	case DR_CPU_UNCONFIGURE:
4123414Srsmaeda 		dr_fn = dr_cpu_unconfigure;
4133414Srsmaeda 		drctl_cmd = DRCTL_CPU_UNCONFIG_REQUEST;
4143414Srsmaeda 		se_hint = SE_HINT_REMOVE;
4152309Srsmaeda 		break;
4162309Srsmaeda 	default:
4172309Srsmaeda 		/* Programming error if we reach this. */
4187899SJames.Marks@Sun.COM 		cmn_err(CE_NOTE,
4197899SJames.Marks@Sun.COM 		    "%s: bad msg_type %d\n", __func__, req->msg_type);
4202309Srsmaeda 		ASSERT(0);
4212309Srsmaeda 		return (-1);
4222309Srsmaeda 	}
4231991Sheppo 
4243414Srsmaeda 	/* the incoming array of cpuids to operate on */
4253414Srsmaeda 	req_cpus = DR_CPU_CMD_CPUIDS(req);
4262309Srsmaeda 
4272309Srsmaeda 	/* allocate drctl request msg based on incoming resource count */
4283414Srsmaeda 	drctl_req_len = sizeof (drctl_rsrc_t) * count;
4293414Srsmaeda 	drctl_req = kmem_zalloc(drctl_req_len, KM_SLEEP);
4307899SJames.Marks@Sun.COM 	DR_DBG_KMEM("%s: alloc addr %p size %ld\n",
4317899SJames.Marks@Sun.COM 	    __func__, (void *)drctl_req, drctl_req_len);
4322309Srsmaeda 
4332309Srsmaeda 	/* copy the cpuids for the drctl call from the incoming request msg */
4342309Srsmaeda 	for (idx = 0; idx < count; idx++)
4353414Srsmaeda 		drctl_req[idx].res_cpu_id = req_cpus[idx];
4361991Sheppo 
4377899SJames.Marks@Sun.COM 	rv = drctl_config_init(drctl_cmd, drctl_flags, drctl_req,
4387899SJames.Marks@Sun.COM 	    count, &drctl_resp, &drctl_resp_len, &drctl_res_ck);
4397899SJames.Marks@Sun.COM 
4407899SJames.Marks@Sun.COM 	ASSERT((drctl_resp != NULL) && (drctl_resp_len != 0));
4417899SJames.Marks@Sun.COM 
4427899SJames.Marks@Sun.COM 	if (rv != 0) {
4437899SJames.Marks@Sun.COM 		DR_DBG_CPU("%s: drctl_config_init "
4447899SJames.Marks@Sun.COM 		    "returned: %d\n", __func__, rv);
4457899SJames.Marks@Sun.COM 
4467899SJames.Marks@Sun.COM 		if (DRCPU_VERS_EQ(1, 0)) {
4477899SJames.Marks@Sun.COM 			rv = -1;
4487899SJames.Marks@Sun.COM 		} else {
4497899SJames.Marks@Sun.COM 			ASSERT(DRCPU_VERS_GTEQ(1, 1));
4507899SJames.Marks@Sun.COM 			ASSERT(drctl_resp->resp_type == DRCTL_RESP_ERR);
4517899SJames.Marks@Sun.COM 
4527899SJames.Marks@Sun.COM 			*resp_len = dr_cpu_err_resp(req,
4537899SJames.Marks@Sun.COM 			    resp, drctl_resp->resp_err_msg);
4547899SJames.Marks@Sun.COM 		}
4557899SJames.Marks@Sun.COM 
4567899SJames.Marks@Sun.COM 		DR_DBG_KMEM("%s: free addr %p size %ld\n",
4577899SJames.Marks@Sun.COM 		    __func__, (void *)drctl_resp, drctl_resp_len);
4587899SJames.Marks@Sun.COM 		kmem_free(drctl_resp, drctl_resp_len);
4597899SJames.Marks@Sun.COM 		DR_DBG_KMEM("%s: free addr %p size %ld\n",
4607899SJames.Marks@Sun.COM 		    __func__, (void *)drctl_req, drctl_req_len);
4613414Srsmaeda 		kmem_free(drctl_req, drctl_req_len);
4627899SJames.Marks@Sun.COM 
4637899SJames.Marks@Sun.COM 		return (rv);
4642309Srsmaeda 	}
4652309Srsmaeda 
4667899SJames.Marks@Sun.COM 	ASSERT(drctl_resp->resp_type == DRCTL_RESP_OK);
4677899SJames.Marks@Sun.COM 
4687899SJames.Marks@Sun.COM 	drctl_rsrc = drctl_resp->resp_resources;
4693414Srsmaeda 
4703414Srsmaeda 	/* create the result scratch array */
4717899SJames.Marks@Sun.COM 	res = dr_cpu_res_array_init(req, drctl_rsrc, count);
4722309Srsmaeda 
4732309Srsmaeda 	/*
4743414Srsmaeda 	 * For unconfigure, check if there are any conditions
4753414Srsmaeda 	 * that will cause the operation to fail. These are
4763414Srsmaeda 	 * performed before the actual unconfigure attempt so
4773414Srsmaeda 	 * that a meaningful error message can be generated.
4782309Srsmaeda 	 */
4793414Srsmaeda 	if (req->msg_type != DR_CPU_CONFIGURE)
4803414Srsmaeda 		dr_cpu_check_cpus(req, res);
4811991Sheppo 
4823414Srsmaeda 	/* perform the specified operation on each of the CPUs */
4833414Srsmaeda 	for (idx = 0; idx < count; idx++) {
4843414Srsmaeda 		int result;
4853414Srsmaeda 		int status;
4862309Srsmaeda 
4873414Srsmaeda 		/*
4883414Srsmaeda 		 * If no action will be taken against the current
4893414Srsmaeda 		 * CPU, update the drctl resource information to
4903414Srsmaeda 		 * ensure that it gets recovered properly during
4913414Srsmaeda 		 * the drctl fini() call.
4923414Srsmaeda 		 */
4933414Srsmaeda 		if (res[idx].result != DR_CPU_RES_OK) {
4943414Srsmaeda 			drctl_req[idx].status = DRCTL_STATUS_CONFIG_FAILURE;
4953414Srsmaeda 			continue;
4962309Srsmaeda 		}
4971991Sheppo 
4983414Srsmaeda 		/* call the function to perform the actual operation */
4993414Srsmaeda 		result = (*dr_fn)(req_cpus[idx], &status, force);
5003414Srsmaeda 
5013414Srsmaeda 		/* save off results of the operation */
5023414Srsmaeda 		res[idx].result = result;
5033414Srsmaeda 		res[idx].status = status;
5042309Srsmaeda 
5053414Srsmaeda 		/* save result for drctl fini() reusing init() msg memory */
5063414Srsmaeda 		drctl_req[idx].status = (result != DR_CPU_RES_OK) ?
5073414Srsmaeda 		    DRCTL_STATUS_CONFIG_FAILURE : DRCTL_STATUS_CONFIG_SUCCESS;
5082309Srsmaeda 
5093414Srsmaeda 		DR_DBG_CPU("%s: cpuid %d status %d result %d off '%s'\n",
5107899SJames.Marks@Sun.COM 		    __func__, req_cpus[idx], drctl_req[idx].status, result,
5113414Srsmaeda 		    (res[idx].string) ? res[idx].string : "");
5121991Sheppo 	}
5131991Sheppo 
5143414Srsmaeda 	if ((rv = drctl_config_fini(&drctl_res_ck, drctl_req, count)) != 0)
5157899SJames.Marks@Sun.COM 		DR_DBG_CPU("%s: drctl_config_fini "
5167899SJames.Marks@Sun.COM 		    "returned: %d\n", __func__, rv);
5173414Srsmaeda 
5183414Srsmaeda 	/*
5193414Srsmaeda 	 * Operation completed without any fatal errors.
5203414Srsmaeda 	 * Pack the response for transmission.
5213414Srsmaeda 	 */
5223414Srsmaeda 	*resp_len = dr_cpu_pack_response(req, res, resp);
5232309Srsmaeda 
5243414Srsmaeda 	/* notify interested parties about the operation */
5253414Srsmaeda 	dr_generate_event(DR_TYPE_CPU, se_hint);
5262309Srsmaeda 
5273414Srsmaeda 	/*
5283414Srsmaeda 	 * Deallocate any scratch memory.
5293414Srsmaeda 	 */
5307899SJames.Marks@Sun.COM 	DR_DBG_KMEM("%s: free addr %p size %ld\n",
5317899SJames.Marks@Sun.COM 	    __func__, (void *)drctl_resp, drctl_resp_len);
5327899SJames.Marks@Sun.COM 	kmem_free(drctl_resp, drctl_resp_len);
5337899SJames.Marks@Sun.COM 	DR_DBG_KMEM("%s: free addr %p size %ld\n",
5347899SJames.Marks@Sun.COM 	    __func__, (void *)drctl_req, drctl_req_len);
5353414Srsmaeda 	kmem_free(drctl_req, drctl_req_len);
5362309Srsmaeda 
5373414Srsmaeda 	dr_cpu_res_array_fini(res, count);
5381991Sheppo 
5391991Sheppo 	return (0);
5401991Sheppo }
5411991Sheppo 
5423414Srsmaeda /*
5433414Srsmaeda  * Allocate and initialize a result array based on the initial
5443414Srsmaeda  * drctl operation. A valid result array is always returned.
5453414Srsmaeda  */
5463414Srsmaeda static dr_cpu_res_t *
dr_cpu_res_array_init(dr_cpu_hdr_t * req,drctl_rsrc_t * rsrc,int nrsrc)5473414Srsmaeda dr_cpu_res_array_init(dr_cpu_hdr_t *req, drctl_rsrc_t *rsrc, int nrsrc)
5483414Srsmaeda {
5493414Srsmaeda 	int		idx;
5503414Srsmaeda 	dr_cpu_res_t	*res;
5513414Srsmaeda 	char		*err_str;
5523414Srsmaeda 	size_t		err_len;
5533414Srsmaeda 
5543414Srsmaeda 	/* allocate zero filled buffer to initialize fields */
5553414Srsmaeda 	res = kmem_zalloc(nrsrc * sizeof (dr_cpu_res_t), KM_SLEEP);
5567899SJames.Marks@Sun.COM 	DR_DBG_KMEM("%s: alloc addr %p size %ld\n",
5577899SJames.Marks@Sun.COM 	    __func__, (void *)res, nrsrc * sizeof (dr_cpu_res_t));
5583414Srsmaeda 
5593414Srsmaeda 	/*
5603414Srsmaeda 	 * Fill in the result information for each resource.
5613414Srsmaeda 	 */
5623414Srsmaeda 	for (idx = 0; idx < nrsrc; idx++) {
5633414Srsmaeda 		res[idx].cpuid = rsrc[idx].res_cpu_id;
5643414Srsmaeda 		res[idx].result = DR_CPU_RES_OK;
5653414Srsmaeda 
5663414Srsmaeda 		if (rsrc[idx].status == DRCTL_STATUS_ALLOW)
5673414Srsmaeda 			continue;
5683414Srsmaeda 
5693414Srsmaeda 		/*
5703414Srsmaeda 		 * Update the state information for this CPU.
5713414Srsmaeda 		 */
5723414Srsmaeda 		res[idx].result = DR_CPU_RES_BLOCKED;
5733414Srsmaeda 		res[idx].status = (req->msg_type == DR_CPU_CONFIGURE) ?
5743414Srsmaeda 		    DR_CPU_STAT_UNCONFIGURED : DR_CPU_STAT_CONFIGURED;
5753414Srsmaeda 
5763414Srsmaeda 		/*
5773414Srsmaeda 		 * If an error string exists, copy it out of the
5783414Srsmaeda 		 * message buffer. This eliminates any dependency
5793414Srsmaeda 		 * on the memory allocated for the message buffer
5803414Srsmaeda 		 * itself.
5813414Srsmaeda 		 */
5823414Srsmaeda 		if (rsrc[idx].offset != NULL) {
5833414Srsmaeda 			err_str = (char *)rsrc + rsrc[idx].offset;
5843414Srsmaeda 			err_len = strlen(err_str) + 1;
5853414Srsmaeda 
5863414Srsmaeda 			res[idx].string = kmem_alloc(err_len, KM_SLEEP);
5877899SJames.Marks@Sun.COM 			DR_DBG_KMEM("%s: alloc addr %p size %ld\n",
5887899SJames.Marks@Sun.COM 			    __func__, (void *)(res[idx].string), err_len);
5893414Srsmaeda 			bcopy(err_str, res[idx].string, err_len);
5903414Srsmaeda 		}
5913414Srsmaeda 	}
5923414Srsmaeda 
5933414Srsmaeda 	return (res);
5943414Srsmaeda }
5953414Srsmaeda 
5961991Sheppo static void
dr_cpu_res_array_fini(dr_cpu_res_t * res,int nres)5973414Srsmaeda dr_cpu_res_array_fini(dr_cpu_res_t *res, int nres)
5983414Srsmaeda {
5993414Srsmaeda 	int	idx;
6003414Srsmaeda 	size_t	str_len;
6013414Srsmaeda 
6023414Srsmaeda 	for (idx = 0; idx < nres; idx++) {
6033414Srsmaeda 		/* deallocate the error string if present */
6043414Srsmaeda 		if (res[idx].string) {
6053414Srsmaeda 			str_len = strlen(res[idx].string) + 1;
6067899SJames.Marks@Sun.COM 			DR_DBG_KMEM("%s: free addr %p size %ld\n",
6077899SJames.Marks@Sun.COM 			    __func__, (void *)(res[idx].string), str_len);
6083414Srsmaeda 			kmem_free(res[idx].string, str_len);
6093414Srsmaeda 		}
6103414Srsmaeda 	}
6113414Srsmaeda 
6123414Srsmaeda 	/* deallocate the result array itself */
6137899SJames.Marks@Sun.COM 	DR_DBG_KMEM("%s: free addr %p size %ld\n",
6147899SJames.Marks@Sun.COM 	    __func__, (void *)res, sizeof (dr_cpu_res_t) * nres);
6153414Srsmaeda 	kmem_free(res, sizeof (dr_cpu_res_t) * nres);
6163414Srsmaeda }
6173414Srsmaeda 
6183414Srsmaeda /*
6193414Srsmaeda  * Allocate and pack a response message for transmission based
6203414Srsmaeda  * on the specified result array. A valid response message and
6213414Srsmaeda  * valid size information is always returned.
6223414Srsmaeda  */
6233414Srsmaeda static size_t
dr_cpu_pack_response(dr_cpu_hdr_t * req,dr_cpu_res_t * res,dr_cpu_hdr_t ** respp)6243414Srsmaeda dr_cpu_pack_response(dr_cpu_hdr_t *req, dr_cpu_res_t *res, dr_cpu_hdr_t **respp)
6251991Sheppo {
6261991Sheppo 	int		idx;
6273414Srsmaeda 	dr_cpu_hdr_t	*resp;
6283414Srsmaeda 	dr_cpu_stat_t	*resp_stat;
6293414Srsmaeda 	size_t		resp_len;
6303414Srsmaeda 	uint32_t	curr_off;
6313414Srsmaeda 	caddr_t		curr_str;
6323414Srsmaeda 	size_t		str_len;
6333414Srsmaeda 	size_t		stat_len;
6343414Srsmaeda 	int		nstat = req->num_records;
6353414Srsmaeda 
6363414Srsmaeda 	/*
6373414Srsmaeda 	 * Calculate the size of the response message
6383414Srsmaeda 	 * and allocate an appropriately sized buffer.
6393414Srsmaeda 	 */
6403414Srsmaeda 	resp_len = 0;
6413414Srsmaeda 
6423414Srsmaeda 	/* add the header size */
6433414Srsmaeda 	resp_len += sizeof (dr_cpu_hdr_t);
6443414Srsmaeda 
6453414Srsmaeda 	/* add the stat array size */
6463414Srsmaeda 	stat_len = sizeof (dr_cpu_stat_t) * nstat;
6473414Srsmaeda 	resp_len += stat_len;
6483414Srsmaeda 
6493414Srsmaeda 	/* add the size of any error strings */
6503414Srsmaeda 	for (idx = 0; idx < nstat; idx++) {
6513414Srsmaeda 		if (res[idx].string != NULL) {
6523414Srsmaeda 			resp_len += strlen(res[idx].string) + 1;
6533414Srsmaeda 		}
6543414Srsmaeda 	}
6553414Srsmaeda 
6563414Srsmaeda 	/* allocate the message buffer */
6573414Srsmaeda 	resp = kmem_zalloc(resp_len, KM_SLEEP);
6587899SJames.Marks@Sun.COM 	DR_DBG_KMEM("%s: alloc addr %p size %ld\n",
6597899SJames.Marks@Sun.COM 	    __func__, (void *)resp, resp_len);
6603414Srsmaeda 
6613414Srsmaeda 	/*
6623414Srsmaeda 	 * Fill in the header information.
6633414Srsmaeda 	 */
6643414Srsmaeda 	resp->req_num = req->req_num;
6653414Srsmaeda 	resp->msg_type = DR_CPU_OK;
6663414Srsmaeda 	resp->num_records = nstat;
6673414Srsmaeda 
6683414Srsmaeda 	/*
6693414Srsmaeda 	 * Fill in the stat information.
6703414Srsmaeda 	 */
6713414Srsmaeda 	resp_stat = DR_CPU_RESP_STATS(resp);
6723414Srsmaeda 
6733414Srsmaeda 	/* string offsets start immediately after stat array */
6743414Srsmaeda 	curr_off = sizeof (dr_cpu_hdr_t) + stat_len;
6753414Srsmaeda 	curr_str = (char *)resp_stat + stat_len;
6763414Srsmaeda 
6773414Srsmaeda 	for (idx = 0; idx < nstat; idx++) {
6783414Srsmaeda 		resp_stat[idx].cpuid = res[idx].cpuid;
6793414Srsmaeda 		resp_stat[idx].result = res[idx].result;
6803414Srsmaeda 		resp_stat[idx].status = res[idx].status;
6813414Srsmaeda 
6823414Srsmaeda 		if (res[idx].string != NULL) {
6833414Srsmaeda 			/* copy over the error string */
6843414Srsmaeda 			str_len = strlen(res[idx].string) + 1;
6853414Srsmaeda 			bcopy(res[idx].string, curr_str, str_len);
6863414Srsmaeda 			resp_stat[idx].string_off = curr_off;
6873414Srsmaeda 
6883414Srsmaeda 			curr_off += str_len;
6893414Srsmaeda 			curr_str += str_len;
6903414Srsmaeda 		}
6913414Srsmaeda 	}
6923414Srsmaeda 
6933414Srsmaeda 	/* buffer should be exactly filled */
6943414Srsmaeda 	ASSERT(curr_off == resp_len);
6953414Srsmaeda 
6963414Srsmaeda 	*respp = resp;
6973414Srsmaeda 	return (resp_len);
6983414Srsmaeda }
6993414Srsmaeda 
7003414Srsmaeda /*
7013414Srsmaeda  * Check for conditions that will prevent a CPU from being offlined.
7023414Srsmaeda  * This provides the opportunity to generate useful information to
7033414Srsmaeda  * help diagnose the failure rather than letting the offline attempt
7043414Srsmaeda  * fail in a more generic way.
7053414Srsmaeda  */
7063414Srsmaeda static void
dr_cpu_check_cpus(dr_cpu_hdr_t * req,dr_cpu_res_t * res)7073414Srsmaeda dr_cpu_check_cpus(dr_cpu_hdr_t *req, dr_cpu_res_t *res)
7083414Srsmaeda {
7093414Srsmaeda 	int		idx;
7103414Srsmaeda 	cpu_t		*cp;
7113414Srsmaeda 	uint32_t	*cpuids;
7123414Srsmaeda 
7133414Srsmaeda 	ASSERT((req->msg_type == DR_CPU_UNCONFIGURE) ||
7143414Srsmaeda 	    (req->msg_type == DR_CPU_FORCE_UNCONFIG));
7151991Sheppo 
7161991Sheppo 	DR_DBG_CPU("dr_cpu_check_cpus...\n");
7171991Sheppo 
7183414Srsmaeda 	/* array of cpuids start just after the header */
7193414Srsmaeda 	cpuids = DR_CPU_CMD_CPUIDS(req);
7203414Srsmaeda 
7211991Sheppo 	mutex_enter(&cpu_lock);
7221991Sheppo 
7233414Srsmaeda 	/*
7243414Srsmaeda 	 * Always check processor set membership first. The
7253414Srsmaeda 	 * last CPU in a processor set will fail to offline
7263414Srsmaeda 	 * even if the operation if forced, so any failures
7273414Srsmaeda 	 * should always be reported.
7283414Srsmaeda 	 */
7293414Srsmaeda 	dr_cpu_check_psrset(cpuids, res, req->num_records);
7303414Srsmaeda 
7311991Sheppo 	/* process each cpu that is part of the request */
7323414Srsmaeda 	for (idx = 0; idx < req->num_records; idx++) {
7331991Sheppo 
7343414Srsmaeda 		/* nothing to check if the CPU has already failed */
7353414Srsmaeda 		if (res[idx].result != DR_CPU_RES_OK)
7361991Sheppo 			continue;
7371991Sheppo 
7383414Srsmaeda 		if ((cp = cpu_get(cpuids[idx])) == NULL)
7393414Srsmaeda 			continue;
7401991Sheppo 
7411991Sheppo 		/*
7423414Srsmaeda 		 * Only check if there are bound threads if the
7433414Srsmaeda 		 * operation is not a forced unconfigure. In a
7443414Srsmaeda 		 * forced request, threads are automatically
7453414Srsmaeda 		 * unbound before they are offlined.
7461991Sheppo 		 */
7473414Srsmaeda 		if (req->msg_type == DR_CPU_UNCONFIGURE) {
7483414Srsmaeda 			/*
7493414Srsmaeda 			 * The return value is only interesting if other
7503414Srsmaeda 			 * checks are added to this loop and a decision
7513414Srsmaeda 			 * is needed on whether to continue checking.
7523414Srsmaeda 			 */
7533414Srsmaeda 			(void) dr_cpu_check_bound_thr(cp, &res[idx]);
7541991Sheppo 		}
7551991Sheppo 	}
7561991Sheppo 
7571991Sheppo 	mutex_exit(&cpu_lock);
7581991Sheppo }
7591991Sheppo 
7603414Srsmaeda /*
7613414Srsmaeda  * Examine the processor set configuration for the specified
7623414Srsmaeda  * CPUs and see if the unconfigure operation would result in
7633414Srsmaeda  * trying to remove the last CPU in any processor set.
7643414Srsmaeda  */
7653414Srsmaeda static void
dr_cpu_check_psrset(uint32_t * cpuids,dr_cpu_res_t * res,int nres)7663414Srsmaeda dr_cpu_check_psrset(uint32_t *cpuids, dr_cpu_res_t *res, int nres)
7673414Srsmaeda {
7683414Srsmaeda 	int		cpu_idx;
7693414Srsmaeda 	int		set_idx;
7703414Srsmaeda 	cpu_t		*cp;
7713414Srsmaeda 	cpupart_t	*cpp;
7723414Srsmaeda 	char		err_str[DR_CPU_MAX_ERR_LEN];
7733414Srsmaeda 	size_t		err_len;
7743414Srsmaeda 	struct {
7753414Srsmaeda 		cpupart_t	*cpp;
7763414Srsmaeda 		int		ncpus;
7773414Srsmaeda 	} *psrset;
7783414Srsmaeda 
7793414Srsmaeda 	ASSERT(MUTEX_HELD(&cpu_lock));
7803414Srsmaeda 
7813414Srsmaeda 	/*
7823414Srsmaeda 	 * Allocate a scratch array to count the CPUs in
7833414Srsmaeda 	 * the various processor sets. A CPU always belongs
7843414Srsmaeda 	 * to exactly one processor set, so by definition,
7853414Srsmaeda 	 * the scratch array never needs to be larger than
7863414Srsmaeda 	 * the number of CPUs.
7873414Srsmaeda 	 */
7883414Srsmaeda 	psrset = kmem_zalloc(sizeof (*psrset) * nres, KM_SLEEP);
7897899SJames.Marks@Sun.COM 	DR_DBG_KMEM("%s: alloc addr %p size %ld\n",
7907899SJames.Marks@Sun.COM 	    __func__, (void *)psrset, sizeof (*psrset) * nres);
7913414Srsmaeda 
7923414Srsmaeda 	for (cpu_idx = 0; cpu_idx < nres; cpu_idx++) {
7933414Srsmaeda 
7943414Srsmaeda 		/* skip any CPUs that have already failed */
7953414Srsmaeda 		if (res[cpu_idx].result != DR_CPU_RES_OK)
7963414Srsmaeda 			continue;
7973414Srsmaeda 
7983414Srsmaeda 		if ((cp = cpu_get(cpuids[cpu_idx])) == NULL)
7993414Srsmaeda 			continue;
8003414Srsmaeda 
8013414Srsmaeda 		cpp = cp->cpu_part;
8023414Srsmaeda 
8033414Srsmaeda 		/* lookup the set this CPU belongs to */
8043414Srsmaeda 		for (set_idx = 0; set_idx < nres; set_idx++) {
8053414Srsmaeda 
8063414Srsmaeda 			/* matching set found */
8073414Srsmaeda 			if (cpp == psrset[set_idx].cpp)
8083414Srsmaeda 				break;
8093414Srsmaeda 
8103414Srsmaeda 			/* set not found, start a new entry */
8113414Srsmaeda 			if (psrset[set_idx].cpp == NULL) {
8123414Srsmaeda 				psrset[set_idx].cpp = cpp;
8133414Srsmaeda 				psrset[set_idx].ncpus = cpp->cp_ncpus;
8143414Srsmaeda 				break;
8153414Srsmaeda 			}
8163414Srsmaeda 		}
8173414Srsmaeda 
8183414Srsmaeda 		ASSERT(set_idx != nres);
8193414Srsmaeda 
8203414Srsmaeda 		/*
8213414Srsmaeda 		 * Remove the current CPU from the set total but only
8223414Srsmaeda 		 * generate an error for the last CPU. The correct CPU
8233414Srsmaeda 		 * will get the error because the unconfigure attempts
8243414Srsmaeda 		 * will occur in the same order in which the CPUs are
8258054SJames.Marks@Sun.COM 		 * examined in this loop.  The cp_ncpus field of a
8268054SJames.Marks@Sun.COM 		 * cpupart_t counts only online cpus, so it is safe
8278054SJames.Marks@Sun.COM 		 * to remove an offline cpu without testing ncpus.
8283414Srsmaeda 		 */
8298616SJames.Marks@Sun.COM 		if (cpu_is_offline(cp))
8308054SJames.Marks@Sun.COM 			continue;
8318054SJames.Marks@Sun.COM 
8323414Srsmaeda 		if (--psrset[set_idx].ncpus == 0) {
8333414Srsmaeda 			/*
8343414Srsmaeda 			 * Fill in the various pieces of information
8353414Srsmaeda 			 * to report that the operation will fail.
8363414Srsmaeda 			 */
8373414Srsmaeda 			res[cpu_idx].result = DR_CPU_RES_BLOCKED;
8383414Srsmaeda 			res[cpu_idx].status = DR_CPU_STAT_CONFIGURED;
8393414Srsmaeda 
8403414Srsmaeda 			(void) snprintf(err_str, DR_CPU_MAX_ERR_LEN,
8413414Srsmaeda 			    "last online cpu in processor set %d", cpp->cp_id);
8423414Srsmaeda 
8433414Srsmaeda 			err_len = strlen(err_str) + 1;
8443414Srsmaeda 
8453414Srsmaeda 			res[cpu_idx].string = kmem_alloc(err_len, KM_SLEEP);
8467899SJames.Marks@Sun.COM 			DR_DBG_KMEM("%s: alloc addr %p size %ld\n",
8477899SJames.Marks@Sun.COM 			    __func__, (void *)(res[cpu_idx].string), err_len);
8483414Srsmaeda 			bcopy(err_str, res[cpu_idx].string, err_len);
8493414Srsmaeda 
8503414Srsmaeda 			DR_DBG_CPU("cpu %d: %s\n", cpuids[cpu_idx], err_str);
8513414Srsmaeda 		}
8523414Srsmaeda 	}
8533414Srsmaeda 
8547899SJames.Marks@Sun.COM 	DR_DBG_KMEM("%s: free addr %p size %ld\n",
8557899SJames.Marks@Sun.COM 	    __func__, (void *)psrset, sizeof (*psrset) * nres);
8563414Srsmaeda 	kmem_free(psrset, sizeof (*psrset) * nres);
8573414Srsmaeda }
8583414Srsmaeda 
8593414Srsmaeda /*
8603414Srsmaeda  * Check if any threads are bound to the specified CPU. If the
8613414Srsmaeda  * condition is true, DR_CPU_RES_BLOCKED is returned and an error
8623414Srsmaeda  * string is generated and placed in the specified result structure.
8633414Srsmaeda  * Otherwise, DR_CPU_RES_OK is returned.
8643414Srsmaeda  */
8653414Srsmaeda static int
dr_cpu_check_bound_thr(cpu_t * cp,dr_cpu_res_t * res)8663414Srsmaeda dr_cpu_check_bound_thr(cpu_t *cp, dr_cpu_res_t *res)
8673414Srsmaeda {
8683414Srsmaeda 	int		nbound;
8693414Srsmaeda 	proc_t		*pp;
8703414Srsmaeda 	kthread_t	*tp;
8713414Srsmaeda 	char		err_str[DR_CPU_MAX_ERR_LEN];
8723414Srsmaeda 	size_t		err_len;
8733414Srsmaeda 
8743414Srsmaeda 	/*
8753414Srsmaeda 	 * Error string allocation makes an assumption
8763414Srsmaeda 	 * that no blocking condition has been identified.
8773414Srsmaeda 	 */
8783414Srsmaeda 	ASSERT(res->result == DR_CPU_RES_OK);
8793414Srsmaeda 	ASSERT(res->string == NULL);
8803414Srsmaeda 
8813414Srsmaeda 	ASSERT(MUTEX_HELD(&cpu_lock));
8823414Srsmaeda 
8833414Srsmaeda 	mutex_enter(&pidlock);
8843414Srsmaeda 
8853414Srsmaeda 	nbound = 0;
8863414Srsmaeda 
8873414Srsmaeda 	/*
8883414Srsmaeda 	 * Walk the active processes, checking if each
8893414Srsmaeda 	 * thread belonging to the process is bound.
8903414Srsmaeda 	 */
8913414Srsmaeda 	for (pp = practive; (pp != NULL) && (nbound <= 1); pp = pp->p_next) {
8923414Srsmaeda 		mutex_enter(&pp->p_lock);
8933414Srsmaeda 
8943414Srsmaeda 		tp = pp->p_tlist;
8953414Srsmaeda 
8963414Srsmaeda 		if ((tp == NULL) || (pp->p_flag & SSYS)) {
8973414Srsmaeda 			mutex_exit(&pp->p_lock);
8983414Srsmaeda 			continue;
8993414Srsmaeda 		}
9003414Srsmaeda 
9013414Srsmaeda 		do {
9023414Srsmaeda 			if (tp->t_bind_cpu != cp->cpu_id)
9033414Srsmaeda 				continue;
9043414Srsmaeda 
9053414Srsmaeda 			/*
9063414Srsmaeda 			 * Update the running total of bound
9073414Srsmaeda 			 * threads. Continue the search until
9083414Srsmaeda 			 * it can be determined if more than
9093414Srsmaeda 			 * one thread is bound to the CPU.
9103414Srsmaeda 			 */
9113414Srsmaeda 			if (++nbound > 1)
9123414Srsmaeda 				break;
9133414Srsmaeda 
9143414Srsmaeda 		} while ((tp = tp->t_forw) != pp->p_tlist);
9153414Srsmaeda 
9163414Srsmaeda 		mutex_exit(&pp->p_lock);
9173414Srsmaeda 	}
9183414Srsmaeda 
9193414Srsmaeda 	mutex_exit(&pidlock);
9203414Srsmaeda 
9213414Srsmaeda 	if (nbound) {
9223414Srsmaeda 		/*
9233414Srsmaeda 		 * Threads are bound to the CPU. Fill in
9243414Srsmaeda 		 * various pieces of information to report
9253414Srsmaeda 		 * that the operation will fail.
9263414Srsmaeda 		 */
9273414Srsmaeda 		res->result = DR_CPU_RES_BLOCKED;
9283414Srsmaeda 		res->status = DR_CPU_STAT_CONFIGURED;
9293414Srsmaeda 
9303414Srsmaeda 		(void) snprintf(err_str, DR_CPU_MAX_ERR_LEN, "cpu has bound "
9313414Srsmaeda 		    "thread%s", (nbound > 1) ? "s" : "");
9323414Srsmaeda 
9333414Srsmaeda 		err_len = strlen(err_str) + 1;
9343414Srsmaeda 
9353414Srsmaeda 		res->string = kmem_alloc(err_len, KM_SLEEP);
9367899SJames.Marks@Sun.COM 		DR_DBG_KMEM("%s: alloc addr %p size %ld\n",
9377899SJames.Marks@Sun.COM 		    __func__, (void *)(res->string), err_len);
9383414Srsmaeda 		bcopy(err_str, res->string, err_len);
9393414Srsmaeda 
9403414Srsmaeda 		DR_DBG_CPU("cpu %d: %s\n", cp->cpu_id, err_str);
9413414Srsmaeda 	}
9423414Srsmaeda 
9433414Srsmaeda 	return (res->result);
9443414Srsmaeda }
9451991Sheppo 
9461991Sheppo /*
9471991Sheppo  * Do not modify result buffer or length on error.
9481991Sheppo  */
9491991Sheppo static int
dr_cpu_list_status(dr_cpu_hdr_t * req,dr_cpu_hdr_t ** resp,int * resp_len)9501991Sheppo dr_cpu_list_status(dr_cpu_hdr_t *req, dr_cpu_hdr_t **resp, int *resp_len)
9511991Sheppo {
9521991Sheppo 	int		idx;
9531991Sheppo 	int		result;
9541991Sheppo 	int		status;
9551991Sheppo 	int		rlen;
9561991Sheppo 	uint32_t	*cpuids;
9571991Sheppo 	dr_cpu_hdr_t	*rp;
9581991Sheppo 	dr_cpu_stat_t	*stat;
9591991Sheppo 	md_t		*mdp = NULL;
9601991Sheppo 	int		num_nodes;
9611991Sheppo 	int		listsz;
9621991Sheppo 	mde_cookie_t	*listp = NULL;
9631991Sheppo 	mde_cookie_t	cpunode;
9641991Sheppo 	boolean_t	walk_md = B_FALSE;
9651991Sheppo 
9661991Sheppo 	/* the incoming array of cpuids to configure */
9673414Srsmaeda 	cpuids = DR_CPU_CMD_CPUIDS(req);
9681991Sheppo 
9691991Sheppo 	/* allocate a response message */
9701991Sheppo 	rlen = sizeof (dr_cpu_hdr_t);
9711991Sheppo 	rlen += req->num_records * sizeof (dr_cpu_stat_t);
9721991Sheppo 	rp = kmem_zalloc(rlen, KM_SLEEP);
9737899SJames.Marks@Sun.COM 	DR_DBG_KMEM("%s: alloc addr %p size %d\n", __func__, (void *)rp, rlen);
9741991Sheppo 
9751991Sheppo 	/* fill in the known data */
9761991Sheppo 	rp->req_num = req->req_num;
9771991Sheppo 	rp->msg_type = DR_CPU_STATUS;
9781991Sheppo 	rp->num_records = req->num_records;
9791991Sheppo 
9801991Sheppo 	/* stat array for the response */
9813414Srsmaeda 	stat = DR_CPU_RESP_STATS(rp);
9821991Sheppo 
9831991Sheppo 	/* get the status for each of the CPUs */
9841991Sheppo 	for (idx = 0; idx < req->num_records; idx++) {
9851991Sheppo 
9861991Sheppo 		result = dr_cpu_status(cpuids[idx], &status);
9871991Sheppo 
9881991Sheppo 		if (result == DR_CPU_RES_FAILURE)
9891991Sheppo 			walk_md = B_TRUE;
9901991Sheppo 
9911991Sheppo 		/* save off results of the status */
9921991Sheppo 		stat[idx].cpuid = cpuids[idx];
9931991Sheppo 		stat[idx].result = result;
9941991Sheppo 		stat[idx].status = status;
9951991Sheppo 	}
9961991Sheppo 
9971991Sheppo 	if (walk_md == B_FALSE)
9981991Sheppo 		goto done;
9991991Sheppo 
10001991Sheppo 	/*
10011991Sheppo 	 * At least one of the cpus did not have a CPU
10021991Sheppo 	 * structure. So, consult the MD to determine if
10031991Sheppo 	 * they are present.
10041991Sheppo 	 */
10051991Sheppo 
10061991Sheppo 	if ((mdp = md_get_handle()) == NULL) {
10071991Sheppo 		DR_DBG_CPU("unable to initialize MD\n");
10081991Sheppo 		goto done;
10091991Sheppo 	}
10101991Sheppo 
10111991Sheppo 	num_nodes = md_node_count(mdp);
10121991Sheppo 	ASSERT(num_nodes > 0);
10131991Sheppo 
10141991Sheppo 	listsz = num_nodes * sizeof (mde_cookie_t);
10151991Sheppo 	listp = kmem_zalloc(listsz, KM_SLEEP);
10167899SJames.Marks@Sun.COM 	DR_DBG_KMEM("%s: alloc addr %p size %d\n",
10177899SJames.Marks@Sun.COM 	    __func__, (void *)listp, listsz);
10181991Sheppo 
10191991Sheppo 	for (idx = 0; idx < req->num_records; idx++) {
10201991Sheppo 
10211991Sheppo 		if (stat[idx].result != DR_CPU_RES_FAILURE)
10221991Sheppo 			continue;
10231991Sheppo 
10241991Sheppo 		/* check the MD for the current cpuid */
10251991Sheppo 		cpunode = dr_cpu_find_node_md(stat[idx].cpuid, mdp, listp);
10261991Sheppo 
10271991Sheppo 		stat[idx].result = DR_CPU_RES_OK;
10281991Sheppo 
10291991Sheppo 		if (cpunode == MDE_INVAL_ELEM_COOKIE) {
10301991Sheppo 			stat[idx].status = DR_CPU_STAT_NOT_PRESENT;
10311991Sheppo 		} else {
10321991Sheppo 			stat[idx].status = DR_CPU_STAT_UNCONFIGURED;
10331991Sheppo 		}
10341991Sheppo 	}
10351991Sheppo 
10367899SJames.Marks@Sun.COM 	DR_DBG_KMEM("%s: free addr %p size %d\n",
10377899SJames.Marks@Sun.COM 	    __func__, (void *)listp, listsz);
10381991Sheppo 	kmem_free(listp, listsz);
10391991Sheppo 
10401991Sheppo 	(void) md_fini_handle(mdp);
10411991Sheppo 
10421991Sheppo done:
10431991Sheppo 	*resp = rp;
10441991Sheppo 	*resp_len = rlen;
10451991Sheppo 
10461991Sheppo 	return (0);
10471991Sheppo }
10481991Sheppo 
10491991Sheppo static int
dr_cpu_configure(processorid_t cpuid,int * status,boolean_t force)10502309Srsmaeda dr_cpu_configure(processorid_t cpuid, int *status, boolean_t force)
10511991Sheppo {
10522309Srsmaeda 	 _NOTE(ARGUNUSED(force))
10531991Sheppo 	struct cpu	*cp;
10541991Sheppo 	int		rv = 0;
10551991Sheppo 
10561991Sheppo 	DR_DBG_CPU("dr_cpu_configure...\n");
10571991Sheppo 
10581991Sheppo 	/*
10591991Sheppo 	 * Build device tree node for the CPU
10601991Sheppo 	 */
10611991Sheppo 	if ((rv = dr_cpu_probe(cpuid)) != 0) {
10621991Sheppo 		DR_DBG_CPU("failed to probe CPU %d (%d)\n", cpuid, rv);
10631991Sheppo 		if (rv == EINVAL) {
10641991Sheppo 			*status = DR_CPU_STAT_NOT_PRESENT;
10651991Sheppo 			return (DR_CPU_RES_NOT_IN_MD);
10661991Sheppo 		}
10671991Sheppo 		*status = DR_CPU_STAT_UNCONFIGURED;
10681991Sheppo 		return (DR_CPU_RES_FAILURE);
10691991Sheppo 	}
10701991Sheppo 
10711991Sheppo 	mutex_enter(&cpu_lock);
10721991Sheppo 
10731991Sheppo 	/*
10741991Sheppo 	 * Configure the CPU
10751991Sheppo 	 */
10761991Sheppo 	if ((cp = cpu_get(cpuid)) == NULL) {
10771991Sheppo 
10781991Sheppo 		if ((rv = cpu_configure(cpuid)) != 0) {
10791991Sheppo 			DR_DBG_CPU("failed to configure CPU %d (%d)\n",
10801991Sheppo 			    cpuid, rv);
10811991Sheppo 			rv = DR_CPU_RES_FAILURE;
10821991Sheppo 			*status = DR_CPU_STAT_UNCONFIGURED;
10831991Sheppo 			goto done;
10841991Sheppo 		}
10851991Sheppo 
10861991Sheppo 		DR_DBG_CPU("CPU %d configured\n", cpuid);
10871991Sheppo 
10881991Sheppo 		/* CPU struct should exist now */
10891991Sheppo 		cp = cpu_get(cpuid);
10901991Sheppo 	}
10911991Sheppo 
10921991Sheppo 	ASSERT(cp);
10931991Sheppo 
10941991Sheppo 	/*
10951991Sheppo 	 * Power on the CPU. In sun4v, this brings the stopped
10961991Sheppo 	 * CPU into the guest from the Hypervisor.
10971991Sheppo 	 */
10981991Sheppo 	if (cpu_is_poweredoff(cp)) {
10991991Sheppo 
11001991Sheppo 		if ((rv = cpu_poweron(cp)) != 0) {
11011991Sheppo 			DR_DBG_CPU("failed to power on CPU %d (%d)\n",
11021991Sheppo 			    cpuid, rv);
11031991Sheppo 			rv = DR_CPU_RES_FAILURE;
11041991Sheppo 			*status = DR_CPU_STAT_UNCONFIGURED;
11051991Sheppo 			goto done;
11061991Sheppo 		}
11071991Sheppo 
11081991Sheppo 		DR_DBG_CPU("CPU %d powered on\n", cpuid);
11091991Sheppo 	}
11101991Sheppo 
11111991Sheppo 	/*
11121991Sheppo 	 * Online the CPU
11131991Sheppo 	 */
11141991Sheppo 	if (cpu_is_offline(cp)) {
11151991Sheppo 
11161991Sheppo 		if ((rv = cpu_online(cp)) != 0) {
11171991Sheppo 			DR_DBG_CPU("failed to online CPU %d (%d)\n",
11181991Sheppo 			    cpuid, rv);
11191991Sheppo 			rv = DR_CPU_RES_FAILURE;
11201991Sheppo 			/* offline is still configured */
11211991Sheppo 			*status = DR_CPU_STAT_CONFIGURED;
11221991Sheppo 			goto done;
11231991Sheppo 		}
11241991Sheppo 
11251991Sheppo 		DR_DBG_CPU("CPU %d online\n", cpuid);
11261991Sheppo 	}
11271991Sheppo 
11281991Sheppo 	rv = DR_CPU_RES_OK;
11291991Sheppo 	*status = DR_CPU_STAT_CONFIGURED;
11301991Sheppo 
11311991Sheppo done:
11321991Sheppo 	mutex_exit(&cpu_lock);
11331991Sheppo 
11341991Sheppo 	return (rv);
11351991Sheppo }
11361991Sheppo 
11371991Sheppo static int
dr_cpu_unconfigure(processorid_t cpuid,int * status,boolean_t force)11381991Sheppo dr_cpu_unconfigure(processorid_t cpuid, int *status, boolean_t force)
11391991Sheppo {
11401991Sheppo 	struct cpu	*cp;
11411991Sheppo 	int		rv = 0;
11421991Sheppo 	int		cpu_flags;
11431991Sheppo 
11441991Sheppo 	DR_DBG_CPU("dr_cpu_unconfigure%s...\n", (force) ? " (force)" : "");
11451991Sheppo 
11461991Sheppo 	mutex_enter(&cpu_lock);
11471991Sheppo 
11481991Sheppo 	cp = cpu_get(cpuid);
11491991Sheppo 
11501991Sheppo 	if (cp == NULL) {
11511991Sheppo 		/*
1152*9255SVijay.Balakrishna@Sun.COM 		 * As OS CPU structures are already torn down proceed
1153*9255SVijay.Balakrishna@Sun.COM 		 * to deprobe device tree to make sure the device tree
1154*9255SVijay.Balakrishna@Sun.COM 		 * is up do date.
11551991Sheppo 		 */
1156*9255SVijay.Balakrishna@Sun.COM 		goto deprobe;
11571991Sheppo 	}
11581991Sheppo 
11591991Sheppo 	ASSERT(cp->cpu_id == cpuid);
11601991Sheppo 
11611991Sheppo 	/*
11621991Sheppo 	 * Offline the CPU
11631991Sheppo 	 */
11641991Sheppo 	if (cpu_is_active(cp)) {
11651991Sheppo 
11661991Sheppo 		/* set the force flag correctly */
11671991Sheppo 		cpu_flags = (force) ? CPU_FORCED : 0;
11681991Sheppo 
11698616SJames.Marks@Sun.COM 		/*
11708616SJames.Marks@Sun.COM 		 * Before we take the CPU offline, we first enable interrupts.
11718616SJames.Marks@Sun.COM 		 * Otherwise, cpu_offline() might reject the request.  Note:
11728616SJames.Marks@Sun.COM 		 * if the offline subsequently fails, the target cpu will be
11738616SJames.Marks@Sun.COM 		 * left with interrupts enabled.  This is consistent with the
11748616SJames.Marks@Sun.COM 		 * behavior of psradm(1M) and p_online(2).
11758616SJames.Marks@Sun.COM 		 */
11768616SJames.Marks@Sun.COM 		cpu_intr_enable(cp);
11778616SJames.Marks@Sun.COM 
11781991Sheppo 		if ((rv = cpu_offline(cp, cpu_flags)) != 0) {
11791991Sheppo 			DR_DBG_CPU("failed to offline CPU %d (%d)\n",
11801991Sheppo 			    cpuid, rv);
11811991Sheppo 
11821991Sheppo 			rv = DR_CPU_RES_FAILURE;
11831991Sheppo 			*status = DR_CPU_STAT_CONFIGURED;
1184*9255SVijay.Balakrishna@Sun.COM 			mutex_exit(&cpu_lock);
1185*9255SVijay.Balakrishna@Sun.COM 			return (rv);
11861991Sheppo 		}
11871991Sheppo 
11881991Sheppo 		DR_DBG_CPU("CPU %d offline\n", cpuid);
11891991Sheppo 	}
11901991Sheppo 
11911991Sheppo 	/*
11921991Sheppo 	 * Power off the CPU. In sun4v, this puts the running
11931991Sheppo 	 * CPU into the stopped state in the Hypervisor.
11941991Sheppo 	 */
11951991Sheppo 	if (!cpu_is_poweredoff(cp)) {
11961991Sheppo 
11971991Sheppo 		if ((rv = cpu_poweroff(cp)) != 0) {
11981991Sheppo 			DR_DBG_CPU("failed to power off CPU %d (%d)\n",
11991991Sheppo 			    cpuid, rv);
12001991Sheppo 			rv = DR_CPU_RES_FAILURE;
12011991Sheppo 			*status = DR_CPU_STAT_CONFIGURED;
1202*9255SVijay.Balakrishna@Sun.COM 			mutex_exit(&cpu_lock);
1203*9255SVijay.Balakrishna@Sun.COM 			return (rv);
12041991Sheppo 		}
12051991Sheppo 
12061991Sheppo 		DR_DBG_CPU("CPU %d powered off\n", cpuid);
12071991Sheppo 	}
12081991Sheppo 
12091991Sheppo 	/*
12101991Sheppo 	 * Unconfigure the CPU
12111991Sheppo 	 */
12121991Sheppo 	if ((rv = cpu_unconfigure(cpuid)) != 0) {
12131991Sheppo 		DR_DBG_CPU("failed to unconfigure CPU %d (%d)\n", cpuid, rv);
12141991Sheppo 		rv = DR_CPU_RES_FAILURE;
12151991Sheppo 		*status = DR_CPU_STAT_UNCONFIGURED;
1216*9255SVijay.Balakrishna@Sun.COM 		mutex_exit(&cpu_lock);
1217*9255SVijay.Balakrishna@Sun.COM 		return (rv);
12181991Sheppo 	}
12191991Sheppo 
12201991Sheppo 	DR_DBG_CPU("CPU %d unconfigured\n", cpuid);
12211991Sheppo 
1222*9255SVijay.Balakrishna@Sun.COM deprobe:
1223*9255SVijay.Balakrishna@Sun.COM 	mutex_exit(&cpu_lock);
12241991Sheppo 	/*
12251991Sheppo 	 * Tear down device tree.
12261991Sheppo 	 */
12271991Sheppo 	if ((rv = dr_cpu_deprobe(cpuid)) != 0) {
12281991Sheppo 		DR_DBG_CPU("failed to deprobe CPU %d (%d)\n", cpuid, rv);
12291991Sheppo 		rv = DR_CPU_RES_FAILURE;
12301991Sheppo 		*status = DR_CPU_STAT_UNCONFIGURED;
1231*9255SVijay.Balakrishna@Sun.COM 		return (rv);
12321991Sheppo 	}
12331991Sheppo 
12341991Sheppo 	rv = DR_CPU_RES_OK;
12351991Sheppo 	*status = DR_CPU_STAT_UNCONFIGURED;
12361991Sheppo 
12371991Sheppo 	return (rv);
12381991Sheppo }
12391991Sheppo 
12401991Sheppo /*
12411991Sheppo  * Determine the state of a CPU. If the CPU structure is not present,
12421991Sheppo  * it does not attempt to determine whether or not the CPU is in the
12431991Sheppo  * MD. It is more efficient to do this at the higher level for all
12441991Sheppo  * CPUs since it may not even be necessary to search the MD if all
12451991Sheppo  * the CPUs are accounted for. Returns DR_CPU_RES_OK if the CPU
12461991Sheppo  * structure is present, and DR_CPU_RES_FAILURE otherwise as a signal
12471991Sheppo  * that an MD walk is necessary.
12481991Sheppo  */
12491991Sheppo static int
dr_cpu_status(processorid_t cpuid,int * status)12501991Sheppo dr_cpu_status(processorid_t cpuid, int *status)
12511991Sheppo {
12521991Sheppo 	int		rv;
12531991Sheppo 	struct cpu	*cp;
12541991Sheppo 
12551991Sheppo 	DR_DBG_CPU("dr_cpu_status...\n");
12561991Sheppo 
12571991Sheppo 	mutex_enter(&cpu_lock);
12581991Sheppo 
12591991Sheppo 	if ((cp = cpu_get(cpuid)) == NULL) {
12601991Sheppo 		/* need to check if cpu is in the MD */
12611991Sheppo 		rv = DR_CPU_RES_FAILURE;
12621991Sheppo 		goto done;
12631991Sheppo 	}
12641991Sheppo 
12651991Sheppo 	if (cpu_is_poweredoff(cp)) {
12661991Sheppo 		/*
12671991Sheppo 		 * The CPU is powered off, so it is considered
12681991Sheppo 		 * unconfigured from the service entity point of
12691991Sheppo 		 * view. The CPU is not available to the system
12701991Sheppo 		 * and intervention by the service entity would
12711991Sheppo 		 * be required to change that.
12721991Sheppo 		 */
12731991Sheppo 		*status = DR_CPU_STAT_UNCONFIGURED;
12741991Sheppo 	} else {
12751991Sheppo 		/*
12761991Sheppo 		 * The CPU is powered on, so it is considered
12771991Sheppo 		 * configured from the service entity point of
12781991Sheppo 		 * view. It is available for use by the system
12791991Sheppo 		 * and service entities are not concerned about
12801991Sheppo 		 * the operational status (offline, online, etc.)
12811991Sheppo 		 * of the CPU in terms of DR.
12821991Sheppo 		 */
12831991Sheppo 		*status = DR_CPU_STAT_CONFIGURED;
12841991Sheppo 	}
12851991Sheppo 
12861991Sheppo 	rv = DR_CPU_RES_OK;
12871991Sheppo 
12881991Sheppo done:
12891991Sheppo 	mutex_exit(&cpu_lock);
12901991Sheppo 
12911991Sheppo 	return (rv);
12921991Sheppo }
12931991Sheppo 
12941991Sheppo typedef struct {
12951991Sheppo 	md_t		*mdp;
12961991Sheppo 	mde_cookie_t	cpunode;
12971991Sheppo 	dev_info_t	*dip;
12981991Sheppo } cb_arg_t;
12991991Sheppo 
13001991Sheppo #define	STR_ARR_LEN	5
13011991Sheppo 
13021991Sheppo static int
new_cpu_node(dev_info_t * new_node,void * arg,uint_t flags)13031991Sheppo new_cpu_node(dev_info_t *new_node, void *arg, uint_t flags)
13041991Sheppo {
13051991Sheppo 	_NOTE(ARGUNUSED(flags))
13061991Sheppo 
13071991Sheppo 	char		*compat;
13081991Sheppo 	uint64_t	freq;
13091991Sheppo 	uint64_t	cpuid = 0;
13101991Sheppo 	int		regbuf[4];
13111991Sheppo 	int		len = 0;
13121991Sheppo 	cb_arg_t	*cba;
13131991Sheppo 	char		*str_arr[STR_ARR_LEN];
13141991Sheppo 	char		*curr;
13151991Sheppo 	int		idx = 0;
13161991Sheppo 
13171991Sheppo 	DR_DBG_CPU("new_cpu_node...\n");
13181991Sheppo 
13191991Sheppo 	cba = (cb_arg_t *)arg;
13201991Sheppo 
13211991Sheppo 	/*
13221991Sheppo 	 * Add 'name' property
13231991Sheppo 	 */
13241991Sheppo 	if (ndi_prop_update_string(DDI_DEV_T_NONE, new_node,
13251991Sheppo 	    "name", "cpu") != DDI_SUCCESS) {
13261991Sheppo 		DR_DBG_CPU("new_cpu_node: failed to create 'name' property\n");
13271991Sheppo 		return (DDI_WALK_ERROR);
13281991Sheppo 	}
13291991Sheppo 
13301991Sheppo 	/*
13311991Sheppo 	 * Add 'compatible' property
13321991Sheppo 	 */
13331991Sheppo 	if (md_get_prop_data(cba->mdp, cba->cpunode, "compatible",
13341991Sheppo 	    (uint8_t **)(&compat), &len)) {
13351991Sheppo 		DR_DBG_CPU("new_cpu_node: failed to read 'compatible' property "
13361991Sheppo 		    "from MD\n");
13371991Sheppo 		return (DDI_WALK_ERROR);
13381991Sheppo 	}
13391991Sheppo 
13401991Sheppo 	DR_DBG_CPU("'compatible' len is %d\n", len);
13411991Sheppo 
13421991Sheppo 	/* parse the MD string array */
13431991Sheppo 	curr = compat;
13441991Sheppo 	while (curr < (compat + len)) {
13451991Sheppo 
13461991Sheppo 		DR_DBG_CPU("adding '%s' to 'compatible' property\n", curr);
13471991Sheppo 
13481991Sheppo 		str_arr[idx++] = curr;
13491991Sheppo 		curr += strlen(curr) + 1;
13501991Sheppo 
13511991Sheppo 		if (idx == STR_ARR_LEN) {
13521991Sheppo 			DR_DBG_CPU("exceeded str_arr len (%d)\n", STR_ARR_LEN);
13531991Sheppo 			break;
13541991Sheppo 		}
13551991Sheppo 	}
13561991Sheppo 
13571991Sheppo 	if (ndi_prop_update_string_array(DDI_DEV_T_NONE, new_node,
13581991Sheppo 	    "compatible", str_arr, idx) != DDI_SUCCESS) {
13591991Sheppo 		DR_DBG_CPU("new_cpu_node: failed to create 'compatible' "
13601991Sheppo 		    "property\n");
13611991Sheppo 		return (DDI_WALK_ERROR);
13621991Sheppo 	}
13631991Sheppo 
13641991Sheppo 	/*
13651991Sheppo 	 * Add 'device_type' property
13661991Sheppo 	 */
13671991Sheppo 	if (ndi_prop_update_string(DDI_DEV_T_NONE, new_node,
13681991Sheppo 	    "device_type", "cpu") != DDI_SUCCESS) {
13691991Sheppo 		DR_DBG_CPU("new_cpu_node: failed to create 'device_type' "
13701991Sheppo 		    "property\n");
13711991Sheppo 		return (DDI_WALK_ERROR);
13721991Sheppo 	}
13731991Sheppo 
13741991Sheppo 	/*
13751991Sheppo 	 * Add 'clock-frequency' property
13761991Sheppo 	 */
13771991Sheppo 	if (md_get_prop_val(cba->mdp, cba->cpunode, "clock-frequency", &freq)) {
13781991Sheppo 		DR_DBG_CPU("new_cpu_node: failed to read 'clock-frequency' "
13791991Sheppo 		    "property from MD\n");
13801991Sheppo 		return (DDI_WALK_ERROR);
13811991Sheppo 	}
13821991Sheppo 
13831991Sheppo 	if (ndi_prop_update_int(DDI_DEV_T_NONE, new_node,
13841991Sheppo 	    "clock-frequency", freq) != DDI_SUCCESS) {
13851991Sheppo 		DR_DBG_CPU("new_cpu_node: failed to create 'clock-frequency' "
13861991Sheppo 		    "property\n");
13871991Sheppo 		return (DDI_WALK_ERROR);
13881991Sheppo 	}
13891991Sheppo 
13901991Sheppo 	/*
13911991Sheppo 	 * Add 'reg' (cpuid) property
13921991Sheppo 	 */
13931991Sheppo 	if (md_get_prop_val(cba->mdp, cba->cpunode, "id", &cpuid)) {
13941991Sheppo 		DR_DBG_CPU("new_cpu_node: failed to read 'id' property "
13951991Sheppo 		    "from MD\n");
13961991Sheppo 		return (DDI_WALK_ERROR);
13971991Sheppo 	}
13981991Sheppo 
13991991Sheppo 	DR_DBG_CPU("new cpuid=0x%lx\n", cpuid);
14001991Sheppo 
14011991Sheppo 	bzero(regbuf, 4 * sizeof (int));
14021991Sheppo 	regbuf[0] = 0xc0000000 | cpuid;
14031991Sheppo 
14041991Sheppo 	if (ndi_prop_update_int_array(DDI_DEV_T_NONE, new_node,
14051991Sheppo 	    "reg", regbuf, 4) != DDI_SUCCESS) {
14061991Sheppo 		DR_DBG_CPU("new_cpu_node: failed to create 'reg' property\n");
14071991Sheppo 		return (DDI_WALK_ERROR);
14081991Sheppo 	}
14091991Sheppo 
14101991Sheppo 	cba->dip = new_node;
14111991Sheppo 
14121991Sheppo 	return (DDI_WALK_TERMINATE);
14131991Sheppo }
14141991Sheppo 
14151991Sheppo static int
dr_cpu_probe(processorid_t cpuid)14161991Sheppo dr_cpu_probe(processorid_t cpuid)
14171991Sheppo {
14181991Sheppo 	dev_info_t	*pdip;
14191991Sheppo 	dev_info_t	*dip;
14201991Sheppo 	devi_branch_t	br;
14211991Sheppo 	md_t		*mdp = NULL;
14221991Sheppo 	int		num_nodes;
14231991Sheppo 	int		rv = 0;
14241991Sheppo 	int		listsz;
14251991Sheppo 	mde_cookie_t	*listp = NULL;
14261991Sheppo 	cb_arg_t	cba;
14271991Sheppo 	mde_cookie_t	cpunode;
14281991Sheppo 
14291991Sheppo 	if ((dip = dr_cpu_find_node(cpuid)) != NULL) {
14301991Sheppo 		/* nothing to do */
14311991Sheppo 		e_ddi_branch_rele(dip);
14321991Sheppo 		return (0);
14331991Sheppo 	}
14341991Sheppo 
14351991Sheppo 	if ((mdp = md_get_handle()) == NULL) {
14361991Sheppo 		DR_DBG_CPU("unable to initialize machine description\n");
14371991Sheppo 		return (-1);
14381991Sheppo 	}
14391991Sheppo 
14401991Sheppo 	num_nodes = md_node_count(mdp);
14411991Sheppo 	ASSERT(num_nodes > 0);
14421991Sheppo 
14431991Sheppo 	listsz = num_nodes * sizeof (mde_cookie_t);
14441991Sheppo 	listp = kmem_zalloc(listsz, KM_SLEEP);
14457899SJames.Marks@Sun.COM 	DR_DBG_KMEM("%s: alloc addr %p size %d\n",
14467899SJames.Marks@Sun.COM 	    __func__, (void *)listp, listsz);
14471991Sheppo 
14481991Sheppo 	cpunode = dr_cpu_find_node_md(cpuid, mdp, listp);
14491991Sheppo 
14501991Sheppo 	if (cpunode == MDE_INVAL_ELEM_COOKIE) {
14511991Sheppo 		rv = EINVAL;
14521991Sheppo 		goto done;
14531991Sheppo 	}
14541991Sheppo 
14551991Sheppo 	/* pass in MD cookie for CPU */
14561991Sheppo 	cba.mdp = mdp;
14571991Sheppo 	cba.cpunode = cpunode;
14581991Sheppo 
14591991Sheppo 	br.arg = (void *)&cba;
14601991Sheppo 	br.type = DEVI_BRANCH_SID;
14611991Sheppo 	br.create.sid_branch_create = new_cpu_node;
14621991Sheppo 	br.devi_branch_callback = NULL;
14631991Sheppo 	pdip = ddi_root_node();
14641991Sheppo 
14651991Sheppo 	if ((rv = e_ddi_branch_create(pdip, &br, NULL, 0))) {
14661991Sheppo 		DR_DBG_CPU("e_ddi_branch_create failed: %d\n", rv);
14671991Sheppo 		rv = -1;
14681991Sheppo 		goto done;
14691991Sheppo 	}
14701991Sheppo 
14711991Sheppo 	DR_DBG_CPU("CPU %d probed\n", cpuid);
14721991Sheppo 
14731991Sheppo 	rv = 0;
14741991Sheppo 
14751991Sheppo done:
14767899SJames.Marks@Sun.COM 	if (listp) {
14777899SJames.Marks@Sun.COM 		DR_DBG_KMEM("%s: free addr %p size %d\n",
14787899SJames.Marks@Sun.COM 		    __func__, (void *)listp, listsz);
14791991Sheppo 		kmem_free(listp, listsz);
14807899SJames.Marks@Sun.COM 	}
14811991Sheppo 
14821991Sheppo 	if (mdp)
14831991Sheppo 		(void) md_fini_handle(mdp);
14841991Sheppo 
14851991Sheppo 	return (rv);
14861991Sheppo }
14871991Sheppo 
14881991Sheppo static int
dr_cpu_deprobe(processorid_t cpuid)14891991Sheppo dr_cpu_deprobe(processorid_t cpuid)
14901991Sheppo {
14911991Sheppo 	dev_info_t	*fdip = NULL;
14921991Sheppo 	dev_info_t	*dip;
14931991Sheppo 
14941991Sheppo 	if ((dip = dr_cpu_find_node(cpuid)) == NULL) {
14951991Sheppo 		DR_DBG_CPU("cpuid %d already deprobed\n", cpuid);
14961991Sheppo 		return (0);
14971991Sheppo 	}
14981991Sheppo 
14991991Sheppo 	ASSERT(e_ddi_branch_held(dip));
15001991Sheppo 
15011991Sheppo 	if (e_ddi_branch_destroy(dip, &fdip, 0)) {
15021991Sheppo 		char *path = kmem_alloc(MAXPATHLEN, KM_SLEEP);
15031991Sheppo 
15047899SJames.Marks@Sun.COM 		DR_DBG_KMEM("%s: alloc addr %p size %d\n",
15057899SJames.Marks@Sun.COM 		    __func__, (void *)path, MAXPATHLEN);
15061991Sheppo 		/*
15071991Sheppo 		 * If non-NULL, fdip is held and must be released.
15081991Sheppo 		 */
15091991Sheppo 		if (fdip != NULL) {
15101991Sheppo 			(void) ddi_pathname(fdip, path);
15111991Sheppo 			ddi_release_devi(fdip);
15121991Sheppo 		} else {
15131991Sheppo 			(void) ddi_pathname(dip, path);
15141991Sheppo 		}
15151991Sheppo 		cmn_err(CE_NOTE, "node removal failed: %s (%p)",
15161991Sheppo 		    path, (fdip) ? (void *)fdip : (void *)dip);
15171991Sheppo 
15187899SJames.Marks@Sun.COM 		DR_DBG_KMEM("%s: free addr %p size %d\n",
15197899SJames.Marks@Sun.COM 		    __func__, (void *)path, MAXPATHLEN);
15201991Sheppo 		kmem_free(path, MAXPATHLEN);
15211991Sheppo 
15221991Sheppo 		return (-1);
15231991Sheppo 	}
15241991Sheppo 
15251991Sheppo 	DR_DBG_CPU("CPU %d deprobed\n", cpuid);
15261991Sheppo 
15271991Sheppo 	return (0);
15281991Sheppo }
15291991Sheppo 
15301991Sheppo typedef struct {
15311991Sheppo 	processorid_t	cpuid;
15321991Sheppo 	dev_info_t	*dip;
15331991Sheppo } dr_search_arg_t;
15341991Sheppo 
15351991Sheppo static int
dr_cpu_check_node(dev_info_t * dip,void * arg)15361991Sheppo dr_cpu_check_node(dev_info_t *dip, void *arg)
15371991Sheppo {
15381991Sheppo 	char 		*name;
15391991Sheppo 	processorid_t	cpuid;
15401991Sheppo 	dr_search_arg_t	*sarg = (dr_search_arg_t *)arg;
15411991Sheppo 
15421991Sheppo 	if (dip == ddi_root_node()) {
15431991Sheppo 		return (DDI_WALK_CONTINUE);
15441991Sheppo 	}
15451991Sheppo 
15461991Sheppo 	name = ddi_node_name(dip);
15471991Sheppo 
15481991Sheppo 	if (strcmp(name, "cpu") != 0) {
15491991Sheppo 		return (DDI_WALK_PRUNECHILD);
15501991Sheppo 	}
15511991Sheppo 
15521991Sheppo 	cpuid = ddi_prop_get_int(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS,
15531991Sheppo 	    "reg", -1);
15541991Sheppo 
15551991Sheppo 	cpuid = PROM_CFGHDL_TO_CPUID(cpuid);
15561991Sheppo 
15571991Sheppo 	DR_DBG_CPU("found cpuid=0x%x, looking for 0x%x\n", cpuid, sarg->cpuid);
15581991Sheppo 
15591991Sheppo 	if (cpuid == sarg->cpuid) {
15601991Sheppo 		DR_DBG_CPU("matching node\n");
15611991Sheppo 
15621991Sheppo 		/* matching node must be returned held */
15631991Sheppo 		if (!e_ddi_branch_held(dip))
15641991Sheppo 			e_ddi_branch_hold(dip);
15651991Sheppo 
15661991Sheppo 		sarg->dip = dip;
15671991Sheppo 		return (DDI_WALK_TERMINATE);
15681991Sheppo 	}
15691991Sheppo 
15701991Sheppo 	return (DDI_WALK_CONTINUE);
15711991Sheppo }
15721991Sheppo 
15731991Sheppo /*
15741991Sheppo  * Walk the device tree to find the dip corresponding to the cpuid
15751991Sheppo  * passed in. If present, the dip is returned held. The caller must
15761991Sheppo  * release the hold on the dip once it is no longer required. If no
15771991Sheppo  * matching node if found, NULL is returned.
15781991Sheppo  */
15791991Sheppo static dev_info_t *
dr_cpu_find_node(processorid_t cpuid)15801991Sheppo dr_cpu_find_node(processorid_t cpuid)
15811991Sheppo {
15821991Sheppo 	dr_search_arg_t	arg;
15831991Sheppo 
15841991Sheppo 	DR_DBG_CPU("dr_cpu_find_node...\n");
15851991Sheppo 
15861991Sheppo 	arg.cpuid = cpuid;
15871991Sheppo 	arg.dip = NULL;
15881991Sheppo 
15891991Sheppo 	ddi_walk_devs(ddi_root_node(), dr_cpu_check_node, &arg);
15901991Sheppo 
15911991Sheppo 	ASSERT((arg.dip == NULL) || (e_ddi_branch_held(arg.dip)));
15921991Sheppo 
15931991Sheppo 	return ((arg.dip) ? arg.dip : NULL);
15941991Sheppo }
15951991Sheppo 
15961991Sheppo /*
15971991Sheppo  * Look up a particular cpuid in the MD. Returns the mde_cookie_t
15981991Sheppo  * representing that CPU if present, and MDE_INVAL_ELEM_COOKIE
15991991Sheppo  * otherwise. It is assumed the scratch array has already been
16001991Sheppo  * allocated so that it can accommodate the worst case scenario,
16011991Sheppo  * every node in the MD.
16021991Sheppo  */
16031991Sheppo static mde_cookie_t
dr_cpu_find_node_md(processorid_t cpuid,md_t * mdp,mde_cookie_t * listp)16041991Sheppo dr_cpu_find_node_md(processorid_t cpuid, md_t *mdp, mde_cookie_t *listp)
16051991Sheppo {
16061991Sheppo 	int		idx;
16071991Sheppo 	int		nnodes;
16081991Sheppo 	mde_cookie_t	rootnode;
16091991Sheppo 	uint64_t	cpuid_prop;
16101991Sheppo 	mde_cookie_t	result = MDE_INVAL_ELEM_COOKIE;
16111991Sheppo 
16121991Sheppo 	rootnode = md_root_node(mdp);
16131991Sheppo 	ASSERT(rootnode != MDE_INVAL_ELEM_COOKIE);
16141991Sheppo 
16151991Sheppo 	/*
16161991Sheppo 	 * Scan the DAG for all the CPU nodes
16171991Sheppo 	 */
16181991Sheppo 	nnodes = md_scan_dag(mdp, rootnode, md_find_name(mdp, "cpu"),
16191991Sheppo 	    md_find_name(mdp, "fwd"), listp);
16201991Sheppo 
16211991Sheppo 	if (nnodes < 0) {
16221991Sheppo 		DR_DBG_CPU("Scan for CPUs failed\n");
16231991Sheppo 		return (result);
16241991Sheppo 	}
16251991Sheppo 
16261991Sheppo 	DR_DBG_CPU("dr_cpu_find_node_md: found %d CPUs in the MD\n", nnodes);
16271991Sheppo 
16281991Sheppo 	/*
16291991Sheppo 	 * Find the CPU of interest
16301991Sheppo 	 */
16311991Sheppo 	for (idx = 0; idx < nnodes; idx++) {
16321991Sheppo 
16331991Sheppo 		if (md_get_prop_val(mdp, listp[idx], "id", &cpuid_prop)) {
16341991Sheppo 			DR_DBG_CPU("Missing 'id' property for CPU node %d\n",
16351991Sheppo 			    idx);
16361991Sheppo 			break;
16371991Sheppo 		}
16381991Sheppo 
16391991Sheppo 		if (cpuid_prop == cpuid) {
16401991Sheppo 			/* found a match */
16411991Sheppo 			DR_DBG_CPU("dr_cpu_find_node_md: found CPU %d "
16421991Sheppo 			    "in MD\n", cpuid);
16431991Sheppo 			result = listp[idx];
16441991Sheppo 			break;
16451991Sheppo 		}
16461991Sheppo 	}
16471991Sheppo 
16481991Sheppo 	if (result == MDE_INVAL_ELEM_COOKIE) {
16491991Sheppo 		DR_DBG_CPU("CPU %d not in MD\n", cpuid);
16501991Sheppo 	}
16511991Sheppo 
16521991Sheppo 	return (result);
16531991Sheppo }
1654