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