xref: /illumos-gate/usr/src/uts/sun4v/io/dr_cpu.c (revision 9acbbeaf2a1ffe5c14b244867d427714fab43c5c)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 
22 /*
23  * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 #pragma ident	"%Z%%M%	%I%	%E% SMI"
28 
29 /*
30  * sun4v CPU DR Module
31  */
32 
33 #include <sys/modctl.h>
34 #include <sys/processor.h>
35 #include <sys/cpuvar.h>
36 #include <sys/sunddi.h>
37 #include <sys/sunndi.h>
38 #include <sys/note.h>
39 #include <sys/sysevent/dr.h>
40 #include <sys/hypervisor_api.h>
41 #include <sys/mach_descrip.h>
42 #include <sys/mdesc.h>
43 #include <sys/ds.h>
44 #include <sys/drctl.h>
45 #include <sys/dr_util.h>
46 #include <sys/dr_cpu.h>
47 #include <sys/promif.h>
48 #include <sys/machsystm.h>
49 
50 
51 static struct modlmisc modlmisc = {
52 	&mod_miscops,
53 	"sun4v CPU DR %I%"
54 };
55 
56 static struct modlinkage modlinkage = {
57 	MODREV_1,
58 	(void *)&modlmisc,
59 	NULL
60 };
61 
62 typedef int (*fn_t)(processorid_t, int *, boolean_t);
63 
64 /*
65  * Global DS Handle
66  */
67 static ds_svc_hdl_t ds_handle;
68 
69 /*
70  * Supported DS Capability Versions
71  */
72 static ds_ver_t		dr_cpu_vers[] = { { 1, 0 } };
73 #define	DR_CPU_NVERS	(sizeof (dr_cpu_vers) / sizeof (dr_cpu_vers[0]))
74 
75 /*
76  * DS Capability Description
77  */
78 static ds_capability_t dr_cpu_cap = {
79 	DR_CPU_DS_ID,		/* svc_id */
80 	dr_cpu_vers,		/* vers */
81 	DR_CPU_NVERS		/* nvers */
82 };
83 
84 /*
85  * DS Callbacks
86  */
87 static void dr_cpu_reg_handler(ds_cb_arg_t, ds_ver_t *, ds_svc_hdl_t);
88 static void dr_cpu_unreg_handler(ds_cb_arg_t arg);
89 static void dr_cpu_data_handler(ds_cb_arg_t arg, void *buf, size_t buflen);
90 
91 /*
92  * DS Client Ops Vector
93  */
94 static ds_clnt_ops_t dr_cpu_ops = {
95 	dr_cpu_reg_handler,	/* ds_reg_cb */
96 	dr_cpu_unreg_handler,	/* ds_unreg_cb */
97 	dr_cpu_data_handler,	/* ds_data_cb */
98 	NULL			/* cb_arg */
99 };
100 
101 /*
102  * Internal Functions
103  */
104 static int dr_cpu_init(void);
105 static int dr_cpu_fini(void);
106 
107 static int dr_cpu_list_wrk(dr_cpu_hdr_t *, dr_cpu_hdr_t **, int *, fn_t);
108 static int dr_cpu_list_status(dr_cpu_hdr_t *, dr_cpu_hdr_t **, int *);
109 
110 static int dr_cpu_unconfigure(processorid_t, int *status, boolean_t force);
111 static int dr_cpu_configure(processorid_t, int *status, boolean_t force);
112 static int dr_cpu_status(processorid_t, int *status);
113 
114 static int dr_cpu_probe(processorid_t newcpuid);
115 static int dr_cpu_deprobe(processorid_t cpuid);
116 
117 static dev_info_t *dr_cpu_find_node(processorid_t cpuid);
118 static mde_cookie_t dr_cpu_find_node_md(processorid_t, md_t *, mde_cookie_t *);
119 static void dr_cpu_check_cpus(uint32_t *cpuids, int ncpus, dr_cpu_stat_t *stat);
120 
121 
122 int
123 _init(void)
124 {
125 	int	status;
126 
127 	/* check that CPU DR is enabled */
128 	if (dr_is_disabled(DR_TYPE_CPU)) {
129 		cmn_err(CE_CONT, "!CPU DR is disabled\n");
130 		return (-1);
131 	}
132 
133 	if ((status = dr_cpu_init()) != 0) {
134 		cmn_err(CE_NOTE, "CPU DR initialization failed");
135 		return (status);
136 	}
137 
138 	if ((status = mod_install(&modlinkage)) != 0) {
139 		(void) dr_cpu_fini();
140 	}
141 
142 	return (status);
143 }
144 
145 int
146 _info(struct modinfo *modinfop)
147 {
148 	return (mod_info(&modlinkage, modinfop));
149 }
150 
151 int dr_cpu_allow_unload;
152 
153 int
154 _fini(void)
155 {
156 	int	status;
157 
158 	if (dr_cpu_allow_unload == 0)
159 		return (EBUSY);
160 
161 	if ((status = mod_remove(&modlinkage)) == 0) {
162 		(void) dr_cpu_fini();
163 	}
164 
165 	return (status);
166 }
167 
168 static int
169 dr_cpu_init(void)
170 {
171 	int	rv;
172 
173 	if ((rv = ds_cap_init(&dr_cpu_cap, &dr_cpu_ops)) != 0) {
174 		cmn_err(CE_NOTE, "ds_cap_init failed: %d", rv);
175 		return (-1);
176 	}
177 
178 	return (0);
179 }
180 
181 static int
182 dr_cpu_fini(void)
183 {
184 	int	rv;
185 
186 	if ((rv = ds_cap_fini(&dr_cpu_cap)) != 0) {
187 		cmn_err(CE_NOTE, "ds_cap_fini failed: %d", rv);
188 		return (-1);
189 	}
190 
191 	return (0);
192 }
193 
194 static void
195 dr_cpu_reg_handler(ds_cb_arg_t arg, ds_ver_t *ver, ds_svc_hdl_t hdl)
196 {
197 	DR_DBG_CPU("reg_handler: arg=0x%p, ver=%d.%d, hdl=0x%lx\n", arg,
198 	    ver->major, ver->minor, hdl);
199 
200 	ds_handle = hdl;
201 }
202 
203 static void
204 dr_cpu_unreg_handler(ds_cb_arg_t arg)
205 {
206 	DR_DBG_CPU("unreg_handler: arg=0x%p\n", arg);
207 
208 	ds_handle = DS_INVALID_HDL;
209 }
210 
211 static void
212 dr_cpu_data_handler(ds_cb_arg_t arg, void *buf, size_t buflen)
213 {
214 	_NOTE(ARGUNUSED(arg))
215 
216 	dr_cpu_hdr_t	*req = buf;
217 	dr_cpu_hdr_t	err_resp;
218 	dr_cpu_hdr_t	*resp = &err_resp;
219 	int		resp_len = 0;
220 	int		rv;
221 
222 	/*
223 	 * Sanity check the message
224 	 */
225 	if (buflen < sizeof (dr_cpu_hdr_t)) {
226 		DR_DBG_CPU("incoming message short: expected at least %ld "
227 		    "bytes, received %ld\n", sizeof (dr_cpu_hdr_t), buflen);
228 		goto done;
229 	}
230 
231 	if (req == NULL) {
232 		DR_DBG_CPU("empty message: expected at least %ld bytes\n",
233 		    sizeof (dr_cpu_hdr_t));
234 		goto done;
235 	}
236 
237 	DR_DBG_CPU("incoming request:\n");
238 	DR_DBG_DUMP_MSG(buf, buflen);
239 
240 	if (req->num_records > NCPU) {
241 		DR_DBG_CPU("CPU list too long: %d when %d is the maximum\n",
242 		    req->num_records, NCPU);
243 		goto done;
244 	}
245 
246 	if (req->num_records == 0) {
247 		DR_DBG_CPU("No CPU specified for operation\n");
248 		goto done;
249 	}
250 
251 	/*
252 	 * Process the command
253 	 */
254 	switch (req->msg_type) {
255 	case DR_CPU_CONFIGURE:
256 		rv = dr_cpu_list_wrk(req, &resp, &resp_len, dr_cpu_configure);
257 		if (rv != 0)
258 			DR_DBG_CPU("dr_cpu_list_configure failed (%d)\n", rv);
259 		break;
260 
261 	case DR_CPU_UNCONFIGURE:
262 	case DR_CPU_FORCE_UNCONFIG:
263 		rv = dr_cpu_list_wrk(req, &resp, &resp_len, dr_cpu_unconfigure);
264 		if (rv != 0)
265 			DR_DBG_CPU("dr_cpu_list_unconfigure failed (%d)\n", rv);
266 		break;
267 
268 	case DR_CPU_STATUS:
269 		if ((rv = dr_cpu_list_status(req, &resp, &resp_len)) != 0)
270 			DR_DBG_CPU("dr_cpu_list_status failed (%d)\n", rv);
271 		break;
272 
273 	default:
274 		cmn_err(CE_NOTE, "unsupported DR operation (%d)",
275 		    req->msg_type);
276 		break;
277 	}
278 
279 done:
280 	/* check if an error occurred */
281 	if (resp == &err_resp) {
282 		resp->req_num = (req) ? req->req_num : 0;
283 		resp->msg_type = DR_CPU_ERROR;
284 		resp->num_records = 0;
285 		resp_len = sizeof (dr_cpu_hdr_t);
286 	}
287 
288 	/* send back the response */
289 	if (ds_cap_send(ds_handle, resp, resp_len) != 0) {
290 		DR_DBG_CPU("ds_send failed\n");
291 	}
292 
293 	/* free any allocated memory */
294 	if (resp != &err_resp) {
295 		kmem_free(resp, resp_len);
296 	}
297 }
298 
299 /*
300  * Common routine to config or unconfig multiple cpus.  The unconfig
301  * case checks with the OS to see if the removal of cpus will be
302  * permitted, but can be overridden by the "force" version of the
303  * command.  Otherwise, the logic for both cases is identical.
304  *
305  * Note: Do not modify result buffer or length on error.
306  */
307 static int
308 dr_cpu_list_wrk(dr_cpu_hdr_t *rq, dr_cpu_hdr_t **resp, int *resp_len, fn_t f)
309 {
310 	/* related to request message (based on cpu_hdr_t *rq function arg) */
311 
312 	uint32_t	*rq_cpus;	/* address of cpuid array in request */
313 
314 	/* the response message to our caller (passed back via **resp) */
315 
316 	dr_cpu_hdr_t	*rs;		/* address of allocated response msg */
317 	size_t		rs_len;		/* length of response msg */
318 	dr_cpu_stat_t	*rs_stat;	/* addr. of status array in response */
319 	caddr_t		rs_str;		/* addr. of string area in response */
320 	size_t		rs_stat_len;	/* length of status area in response */
321 	size_t		rs_str_len;	/* length of string area in response */
322 
323 	/* the request message sent to drctl_config_[init|fini] */
324 
325 	drctl_rsrc_t	*dr_rq;		/* addr. of allocated msg for drctl */
326 	size_t		dr_rq_len;	/* length of same */
327 
328 	/* the response message received from drctl_config_init */
329 
330 	drctl_rsrc_t	*dr_rs;		/* &response from drctl_config_init */
331 	size_t		dr_rs_len = 0;	/* length of response from same */
332 	caddr_t		dr_rs_str;	/* &(string area) in same */
333 	drctl_cookie_t	dr_rs_ck;	/* the cookie from same */
334 
335 	/* common temp variables */
336 
337 	int		idx;
338 	int		cmd;
339 	int		result;
340 	int		status;
341 	int		count;
342 	int		rv;
343 	int		flags;
344 	int		force;
345 	int		fail_status;
346 	static const char me[] = "dr_cpu_list_wrk";
347 
348 
349 	ASSERT(rq != NULL && rq->num_records != 0);
350 
351 	count = rq->num_records;
352 	flags = 0;
353 	force = B_FALSE;
354 
355 	switch (rq->msg_type) {
356 	case DR_CPU_CONFIGURE:
357 		cmd = DRCTL_CPU_CONFIG_REQUEST;
358 		fail_status = DR_CPU_STAT_UNCONFIGURED;
359 		break;
360 	case DR_CPU_FORCE_UNCONFIG:
361 		flags = DRCTL_FLAG_FORCE;
362 		force = B_TRUE;
363 		_NOTE(FALLTHROUGH)
364 	case DR_CPU_UNCONFIGURE:
365 		cmd = DRCTL_CPU_UNCONFIG_REQUEST;
366 		fail_status = DR_CPU_STAT_CONFIGURED;
367 		break;
368 	default:
369 		/* Programming error if we reach this. */
370 		ASSERT(0);
371 		cmn_err(CE_NOTE, "%s: bad msg_type %d\n", me, rq->msg_type);
372 		return (-1);
373 	}
374 
375 	/* the incoming array of cpuids to configure */
376 	rq_cpus = (uint32_t *)((caddr_t)rq + sizeof (dr_cpu_hdr_t));
377 
378 	/* allocate drctl request msg based on incoming resource count */
379 	dr_rq_len = sizeof (drctl_rsrc_t) * count;
380 	dr_rq = kmem_zalloc(dr_rq_len, KM_SLEEP);
381 
382 	/* copy the cpuids for the drctl call from the incoming request msg */
383 	for (idx = 0; idx < count; idx++)
384 		dr_rq[idx].res_cpu_id = rq_cpus[idx];
385 
386 	rv = drctl_config_init(cmd,
387 	    flags, dr_rq, count, &dr_rs, &dr_rs_len, &dr_rs_ck);
388 
389 	if (rv != 0) {
390 		cmn_err(CE_CONT,
391 		    "?%s: drctl_config_init returned: %d\n", me, rv);
392 		kmem_free(dr_rq, dr_rq_len);
393 		return (-1);
394 	}
395 
396 	ASSERT(dr_rs != NULL && dr_rs_len != 0);
397 
398 	/*
399 	 * Allocate a response buffer for our caller.  It consists of
400 	 * the header plus the (per resource) status array and a string
401 	 * area the size of which is equal to the size of the string
402 	 * area in the drctl_config_init response.  The latter is
403 	 * simply the size difference between the config_init request
404 	 * and config_init response messages (and may be zero).
405 	 */
406 	rs_stat_len =  count * sizeof (dr_cpu_stat_t);
407 	rs_str_len = dr_rs_len - dr_rq_len;
408 	rs_len = sizeof (dr_cpu_hdr_t) + rs_stat_len + rs_str_len;
409 	rs = kmem_zalloc(rs_len, KM_SLEEP);
410 
411 	/* fill in the known data */
412 	rs->req_num = rq->req_num;
413 	rs->msg_type = DR_CPU_OK;
414 	rs->num_records = count;
415 
416 	/* stat array for the response */
417 	rs_stat = (dr_cpu_stat_t *)((caddr_t)rs + sizeof (dr_cpu_hdr_t));
418 
419 	if (rq->msg_type == DR_CPU_FORCE_UNCONFIG)
420 		dr_cpu_check_cpus(rq_cpus, count, rs_stat);
421 
422 	/* [un]configure each of the CPUs */
423 	for (idx = 0; idx < count; idx++) {
424 
425 		if (dr_rs[idx].status != DRCTL_STATUS_ALLOW ||
426 		    rs_stat[idx].result == DR_CPU_RES_BLOCKED) {
427 			result = DR_CPU_RES_FAILURE;
428 			status = fail_status;
429 		} else {
430 			result = (*f)(rq_cpus[idx], &status, force);
431 		}
432 
433 		/* save off results of the configure */
434 		rs_stat[idx].cpuid = rq_cpus[idx];
435 		rs_stat[idx].result = result;
436 		rs_stat[idx].status = status;
437 
438 		/*
439 		 * Convert any string offset from being relative to
440 		 * the start of the drctl response to being relative
441 		 * to the start of the response sent to our caller.
442 		 */
443 		if (dr_rs[idx].offset != 0)
444 			rs_stat[idx].string_off = (uint32_t)dr_rs[idx].offset -
445 			    dr_rq_len + (rs_len - rs_str_len);
446 
447 		/* save result for _fini() reusing _init msg memory */
448 		dr_rq[idx].status = (status == fail_status) ?
449 		    DRCTL_STATUS_CONFIG_FAILURE : DRCTL_STATUS_CONFIG_SUCCESS;
450 		DR_DBG_CPU("%s: cpuid %d status %d result %d off %d",
451 		    me, rq_cpus[idx], dr_rq[idx].status,
452 		    result, rs_stat[idx].string_off);
453 	}
454 
455 	/* copy the strings (if any) from drctl resp. into resp. for caller */
456 	dr_rs_str = (caddr_t)dr_rs + dr_rq_len;
457 	rs_str = (caddr_t)rs + rs_len - rs_str_len;
458 	bcopy(dr_rs_str, rs_str, rs_str_len);
459 
460 	rv = drctl_config_fini(&dr_rs_ck, dr_rq, count);
461 	if (rv != 0)
462 		cmn_err(CE_CONT,
463 		    "?%s: drctl_config_fini returned: %d\n", me, rv);
464 
465 	kmem_free(dr_rs, dr_rs_len);
466 
467 	kmem_free(dr_rq, dr_rq_len);
468 
469 	*resp = rs;
470 	*resp_len = rs_len;
471 
472 	dr_generate_event(DR_TYPE_CPU, SE_HINT_INSERT);
473 
474 	return (0);
475 }
476 
477 static void
478 dr_cpu_check_cpus(uint32_t *cpuids, int ncpus, dr_cpu_stat_t *stat)
479 {
480 	int		idx;
481 	kthread_t	*tp;
482 	proc_t		*pp;
483 
484 	DR_DBG_CPU("dr_cpu_check_cpus...\n");
485 
486 	mutex_enter(&cpu_lock);
487 
488 	/* process each cpu that is part of the request */
489 	for (idx = 0; idx < ncpus; idx++) {
490 
491 		if (cpu_get(cpuids[idx]) == NULL)
492 			continue;
493 
494 		mutex_enter(&pidlock);
495 
496 		/*
497 		 * Walk the active processes, checking if each
498 		 * thread belonging to the process is bound.
499 		 */
500 		for (pp = practive; pp != NULL; pp = pp->p_next) {
501 			mutex_enter(&pp->p_lock);
502 			tp = pp->p_tlist;
503 
504 			if (tp == NULL || (pp->p_flag & SSYS)) {
505 				mutex_exit(&pp->p_lock);
506 				continue;
507 			}
508 
509 			do {
510 				if (tp->t_bind_cpu != cpuids[idx])
511 					continue;
512 
513 				DR_DBG_CPU("thread(s) bound to cpu %d\n",
514 				    cpuids[idx]);
515 
516 				stat[idx].cpuid = cpuids[idx];
517 				stat[idx].result = DR_CPU_RES_BLOCKED;
518 				stat[idx].status = DR_CPU_STAT_CONFIGURED;
519 				break;
520 
521 			} while ((tp = tp->t_forw) != pp->p_tlist);
522 			mutex_exit(&pp->p_lock);
523 		}
524 
525 		mutex_exit(&pidlock);
526 	}
527 
528 	mutex_exit(&cpu_lock);
529 }
530 
531 
532 /*
533  * Do not modify result buffer or length on error.
534  */
535 static int
536 dr_cpu_list_status(dr_cpu_hdr_t *req, dr_cpu_hdr_t **resp, int *resp_len)
537 {
538 	int		idx;
539 	int		result;
540 	int		status;
541 	int		rlen;
542 	uint32_t	*cpuids;
543 	dr_cpu_hdr_t	*rp;
544 	dr_cpu_stat_t	*stat;
545 	md_t		*mdp = NULL;
546 	int		num_nodes;
547 	int		listsz;
548 	mde_cookie_t	*listp = NULL;
549 	mde_cookie_t	cpunode;
550 	boolean_t	walk_md = B_FALSE;
551 
552 	/* the incoming array of cpuids to configure */
553 	cpuids = (uint32_t *)((caddr_t)req + sizeof (dr_cpu_hdr_t));
554 
555 	/* allocate a response message */
556 	rlen = sizeof (dr_cpu_hdr_t);
557 	rlen += req->num_records * sizeof (dr_cpu_stat_t);
558 	rp = kmem_zalloc(rlen, KM_SLEEP);
559 
560 	/* fill in the known data */
561 	rp->req_num = req->req_num;
562 	rp->msg_type = DR_CPU_STATUS;
563 	rp->num_records = req->num_records;
564 
565 	/* stat array for the response */
566 	stat = (dr_cpu_stat_t *)((caddr_t)rp + sizeof (dr_cpu_hdr_t));
567 
568 	/* get the status for each of the CPUs */
569 	for (idx = 0; idx < req->num_records; idx++) {
570 
571 		result = dr_cpu_status(cpuids[idx], &status);
572 
573 		if (result == DR_CPU_RES_FAILURE)
574 			walk_md = B_TRUE;
575 
576 		/* save off results of the status */
577 		stat[idx].cpuid = cpuids[idx];
578 		stat[idx].result = result;
579 		stat[idx].status = status;
580 	}
581 
582 	if (walk_md == B_FALSE)
583 		goto done;
584 
585 	/*
586 	 * At least one of the cpus did not have a CPU
587 	 * structure. So, consult the MD to determine if
588 	 * they are present.
589 	 */
590 
591 	if ((mdp = md_get_handle()) == NULL) {
592 		DR_DBG_CPU("unable to initialize MD\n");
593 		goto done;
594 	}
595 
596 	num_nodes = md_node_count(mdp);
597 	ASSERT(num_nodes > 0);
598 
599 	listsz = num_nodes * sizeof (mde_cookie_t);
600 	listp = kmem_zalloc(listsz, KM_SLEEP);
601 
602 	for (idx = 0; idx < req->num_records; idx++) {
603 
604 		if (stat[idx].result != DR_CPU_RES_FAILURE)
605 			continue;
606 
607 		/* check the MD for the current cpuid */
608 		cpunode = dr_cpu_find_node_md(stat[idx].cpuid, mdp, listp);
609 
610 		stat[idx].result = DR_CPU_RES_OK;
611 
612 		if (cpunode == MDE_INVAL_ELEM_COOKIE) {
613 			stat[idx].status = DR_CPU_STAT_NOT_PRESENT;
614 		} else {
615 			stat[idx].status = DR_CPU_STAT_UNCONFIGURED;
616 		}
617 	}
618 
619 	kmem_free(listp, listsz);
620 
621 	(void) md_fini_handle(mdp);
622 
623 done:
624 	*resp = rp;
625 	*resp_len = rlen;
626 
627 	return (0);
628 }
629 
630 
631 static int
632 dr_cpu_configure(processorid_t cpuid, int *status, boolean_t force)
633 {
634 	 _NOTE(ARGUNUSED(force))
635 	struct cpu	*cp;
636 	int		rv = 0;
637 
638 	DR_DBG_CPU("dr_cpu_configure...\n");
639 
640 	/*
641 	 * Build device tree node for the CPU
642 	 */
643 	if ((rv = dr_cpu_probe(cpuid)) != 0) {
644 		DR_DBG_CPU("failed to probe CPU %d (%d)\n", cpuid, rv);
645 		if (rv == EINVAL) {
646 			*status = DR_CPU_STAT_NOT_PRESENT;
647 			return (DR_CPU_RES_NOT_IN_MD);
648 		}
649 		*status = DR_CPU_STAT_UNCONFIGURED;
650 		return (DR_CPU_RES_FAILURE);
651 	}
652 
653 	mutex_enter(&cpu_lock);
654 
655 	/*
656 	 * Configure the CPU
657 	 */
658 	if ((cp = cpu_get(cpuid)) == NULL) {
659 
660 		if ((rv = cpu_configure(cpuid)) != 0) {
661 			DR_DBG_CPU("failed to configure CPU %d (%d)\n",
662 			    cpuid, rv);
663 			rv = DR_CPU_RES_FAILURE;
664 			*status = DR_CPU_STAT_UNCONFIGURED;
665 			goto done;
666 		}
667 
668 		DR_DBG_CPU("CPU %d configured\n", cpuid);
669 
670 		/* CPU struct should exist now */
671 		cp = cpu_get(cpuid);
672 	}
673 
674 	ASSERT(cp);
675 
676 	/*
677 	 * Power on the CPU. In sun4v, this brings the stopped
678 	 * CPU into the guest from the Hypervisor.
679 	 */
680 	if (cpu_is_poweredoff(cp)) {
681 
682 		if ((rv = cpu_poweron(cp)) != 0) {
683 			DR_DBG_CPU("failed to power on CPU %d (%d)\n",
684 			    cpuid, rv);
685 			rv = DR_CPU_RES_FAILURE;
686 			*status = DR_CPU_STAT_UNCONFIGURED;
687 			goto done;
688 		}
689 
690 		DR_DBG_CPU("CPU %d powered on\n", cpuid);
691 	}
692 
693 	/*
694 	 * Online the CPU
695 	 */
696 	if (cpu_is_offline(cp)) {
697 
698 		if ((rv = cpu_online(cp)) != 0) {
699 			DR_DBG_CPU("failed to online CPU %d (%d)\n",
700 			    cpuid, rv);
701 			rv = DR_CPU_RES_FAILURE;
702 			/* offline is still configured */
703 			*status = DR_CPU_STAT_CONFIGURED;
704 			goto done;
705 		}
706 
707 		DR_DBG_CPU("CPU %d online\n", cpuid);
708 	}
709 
710 	rv = DR_CPU_RES_OK;
711 	*status = DR_CPU_STAT_CONFIGURED;
712 
713 done:
714 	mutex_exit(&cpu_lock);
715 
716 	return (rv);
717 }
718 
719 static int
720 dr_cpu_unconfigure(processorid_t cpuid, int *status, boolean_t force)
721 {
722 	struct cpu	*cp;
723 	int		rv = 0;
724 	int		cpu_flags;
725 
726 	DR_DBG_CPU("dr_cpu_unconfigure%s...\n", (force) ? " (force)" : "");
727 
728 	mutex_enter(&cpu_lock);
729 
730 	cp = cpu_get(cpuid);
731 
732 	if (cp == NULL) {
733 
734 		/*
735 		 * The OS CPU structures are already torn down,
736 		 * Attempt to deprobe the CPU to make sure the
737 		 * device tree is up to date.
738 		 */
739 		if (dr_cpu_deprobe(cpuid) != 0) {
740 			DR_DBG_CPU("failed to deprobe CPU %d\n", cpuid);
741 			rv = DR_CPU_RES_FAILURE;
742 			*status = DR_CPU_STAT_UNCONFIGURED;
743 			goto done;
744 		}
745 
746 		goto done;
747 	}
748 
749 	ASSERT(cp->cpu_id == cpuid);
750 
751 	/*
752 	 * Offline the CPU
753 	 */
754 	if (cpu_is_active(cp)) {
755 
756 		/* set the force flag correctly */
757 		cpu_flags = (force) ? CPU_FORCED : 0;
758 
759 		if ((rv = cpu_offline(cp, cpu_flags)) != 0) {
760 			DR_DBG_CPU("failed to offline CPU %d (%d)\n",
761 			    cpuid, rv);
762 
763 			rv = DR_CPU_RES_FAILURE;
764 			*status = DR_CPU_STAT_CONFIGURED;
765 			goto done;
766 		}
767 
768 		DR_DBG_CPU("CPU %d offline\n", cpuid);
769 	}
770 
771 	/*
772 	 * Power off the CPU. In sun4v, this puts the running
773 	 * CPU into the stopped state in the Hypervisor.
774 	 */
775 	if (!cpu_is_poweredoff(cp)) {
776 
777 		if ((rv = cpu_poweroff(cp)) != 0) {
778 			DR_DBG_CPU("failed to power off CPU %d (%d)\n",
779 			    cpuid, rv);
780 			rv = DR_CPU_RES_FAILURE;
781 			*status = DR_CPU_STAT_CONFIGURED;
782 			goto done;
783 		}
784 
785 		DR_DBG_CPU("CPU %d powered off\n", cpuid);
786 	}
787 
788 	/*
789 	 * Unconfigure the CPU
790 	 */
791 	if ((rv = cpu_unconfigure(cpuid)) != 0) {
792 		DR_DBG_CPU("failed to unconfigure CPU %d (%d)\n", cpuid, rv);
793 		rv = DR_CPU_RES_FAILURE;
794 		*status = DR_CPU_STAT_UNCONFIGURED;
795 		goto done;
796 	}
797 
798 	DR_DBG_CPU("CPU %d unconfigured\n", cpuid);
799 
800 	/*
801 	 * Tear down device tree.
802 	 */
803 	if ((rv = dr_cpu_deprobe(cpuid)) != 0) {
804 		DR_DBG_CPU("failed to deprobe CPU %d (%d)\n", cpuid, rv);
805 		rv = DR_CPU_RES_FAILURE;
806 		*status = DR_CPU_STAT_UNCONFIGURED;
807 		goto done;
808 	}
809 
810 	rv = DR_CPU_RES_OK;
811 	*status = DR_CPU_STAT_UNCONFIGURED;
812 
813 done:
814 	mutex_exit(&cpu_lock);
815 
816 	return (rv);
817 }
818 
819 /*
820  * Determine the state of a CPU. If the CPU structure is not present,
821  * it does not attempt to determine whether or not the CPU is in the
822  * MD. It is more efficient to do this at the higher level for all
823  * CPUs since it may not even be necessary to search the MD if all
824  * the CPUs are accounted for. Returns DR_CPU_RES_OK if the CPU
825  * structure is present, and DR_CPU_RES_FAILURE otherwise as a signal
826  * that an MD walk is necessary.
827  */
828 static int
829 dr_cpu_status(processorid_t cpuid, int *status)
830 {
831 	int		rv;
832 	struct cpu	*cp;
833 
834 	DR_DBG_CPU("dr_cpu_status...\n");
835 
836 	mutex_enter(&cpu_lock);
837 
838 	if ((cp = cpu_get(cpuid)) == NULL) {
839 		/* need to check if cpu is in the MD */
840 		rv = DR_CPU_RES_FAILURE;
841 		goto done;
842 	}
843 
844 	if (cpu_is_poweredoff(cp)) {
845 		/*
846 		 * The CPU is powered off, so it is considered
847 		 * unconfigured from the service entity point of
848 		 * view. The CPU is not available to the system
849 		 * and intervention by the service entity would
850 		 * be required to change that.
851 		 */
852 		*status = DR_CPU_STAT_UNCONFIGURED;
853 	} else {
854 		/*
855 		 * The CPU is powered on, so it is considered
856 		 * configured from the service entity point of
857 		 * view. It is available for use by the system
858 		 * and service entities are not concerned about
859 		 * the operational status (offline, online, etc.)
860 		 * of the CPU in terms of DR.
861 		 */
862 		*status = DR_CPU_STAT_CONFIGURED;
863 	}
864 
865 	rv = DR_CPU_RES_OK;
866 
867 done:
868 	mutex_exit(&cpu_lock);
869 
870 	return (rv);
871 }
872 
873 typedef struct {
874 	md_t		*mdp;
875 	mde_cookie_t	cpunode;
876 	dev_info_t	*dip;
877 } cb_arg_t;
878 
879 #define	STR_ARR_LEN	5
880 
881 static int
882 new_cpu_node(dev_info_t *new_node, void *arg, uint_t flags)
883 {
884 	_NOTE(ARGUNUSED(flags))
885 
886 	char		*compat;
887 	uint64_t	freq;
888 	uint64_t	cpuid = 0;
889 	int		regbuf[4];
890 	int		len = 0;
891 	cb_arg_t	*cba;
892 	char		*str_arr[STR_ARR_LEN];
893 	char		*curr;
894 	int		idx = 0;
895 
896 	DR_DBG_CPU("new_cpu_node...\n");
897 
898 	cba = (cb_arg_t *)arg;
899 
900 	/*
901 	 * Add 'name' property
902 	 */
903 	if (ndi_prop_update_string(DDI_DEV_T_NONE, new_node,
904 	    "name", "cpu") != DDI_SUCCESS) {
905 		DR_DBG_CPU("new_cpu_node: failed to create 'name' property\n");
906 		return (DDI_WALK_ERROR);
907 	}
908 
909 	/*
910 	 * Add 'compatible' property
911 	 */
912 	if (md_get_prop_data(cba->mdp, cba->cpunode, "compatible",
913 	    (uint8_t **)(&compat), &len)) {
914 		DR_DBG_CPU("new_cpu_node: failed to read 'compatible' property "
915 		    "from MD\n");
916 		return (DDI_WALK_ERROR);
917 	}
918 
919 	DR_DBG_CPU("'compatible' len is %d\n", len);
920 
921 	/* parse the MD string array */
922 	curr = compat;
923 	while (curr < (compat + len)) {
924 
925 		DR_DBG_CPU("adding '%s' to 'compatible' property\n", curr);
926 
927 		str_arr[idx++] = curr;
928 		curr += strlen(curr) + 1;
929 
930 		if (idx == STR_ARR_LEN) {
931 			DR_DBG_CPU("exceeded str_arr len (%d)\n", STR_ARR_LEN);
932 			break;
933 		}
934 	}
935 
936 	if (ndi_prop_update_string_array(DDI_DEV_T_NONE, new_node,
937 	    "compatible", str_arr, idx) != DDI_SUCCESS) {
938 		DR_DBG_CPU("new_cpu_node: failed to create 'compatible' "
939 		    "property\n");
940 		return (DDI_WALK_ERROR);
941 	}
942 
943 	/*
944 	 * Add 'device_type' property
945 	 */
946 	if (ndi_prop_update_string(DDI_DEV_T_NONE, new_node,
947 	    "device_type", "cpu") != DDI_SUCCESS) {
948 		DR_DBG_CPU("new_cpu_node: failed to create 'device_type' "
949 		    "property\n");
950 		return (DDI_WALK_ERROR);
951 	}
952 
953 	/*
954 	 * Add 'clock-frequency' property
955 	 */
956 	if (md_get_prop_val(cba->mdp, cba->cpunode, "clock-frequency", &freq)) {
957 		DR_DBG_CPU("new_cpu_node: failed to read 'clock-frequency' "
958 		    "property from MD\n");
959 		return (DDI_WALK_ERROR);
960 	}
961 
962 	if (ndi_prop_update_int(DDI_DEV_T_NONE, new_node,
963 	    "clock-frequency", freq) != DDI_SUCCESS) {
964 		DR_DBG_CPU("new_cpu_node: failed to create 'clock-frequency' "
965 		    "property\n");
966 		return (DDI_WALK_ERROR);
967 	}
968 
969 	/*
970 	 * Add 'reg' (cpuid) property
971 	 */
972 	if (md_get_prop_val(cba->mdp, cba->cpunode, "id", &cpuid)) {
973 		DR_DBG_CPU("new_cpu_node: failed to read 'id' property "
974 		    "from MD\n");
975 		return (DDI_WALK_ERROR);
976 	}
977 
978 	DR_DBG_CPU("new cpuid=0x%lx\n", cpuid);
979 
980 	bzero(regbuf, 4 * sizeof (int));
981 	regbuf[0] = 0xc0000000 | cpuid;
982 
983 	if (ndi_prop_update_int_array(DDI_DEV_T_NONE, new_node,
984 	    "reg", regbuf, 4) != DDI_SUCCESS) {
985 		DR_DBG_CPU("new_cpu_node: failed to create 'reg' property\n");
986 		return (DDI_WALK_ERROR);
987 	}
988 
989 	cba->dip = new_node;
990 
991 	return (DDI_WALK_TERMINATE);
992 }
993 
994 static int
995 dr_cpu_probe(processorid_t cpuid)
996 {
997 	dev_info_t	*pdip;
998 	dev_info_t	*dip;
999 	devi_branch_t	br;
1000 	md_t		*mdp = NULL;
1001 	int		num_nodes;
1002 	int		rv = 0;
1003 	int		listsz;
1004 	mde_cookie_t	*listp = NULL;
1005 	cb_arg_t	cba;
1006 	mde_cookie_t	cpunode;
1007 
1008 	if ((dip = dr_cpu_find_node(cpuid)) != NULL) {
1009 		/* nothing to do */
1010 		e_ddi_branch_rele(dip);
1011 		return (0);
1012 	}
1013 
1014 	if ((mdp = md_get_handle()) == NULL) {
1015 		DR_DBG_CPU("unable to initialize machine description\n");
1016 		return (-1);
1017 	}
1018 
1019 	num_nodes = md_node_count(mdp);
1020 	ASSERT(num_nodes > 0);
1021 
1022 	listsz = num_nodes * sizeof (mde_cookie_t);
1023 	listp = kmem_zalloc(listsz, KM_SLEEP);
1024 
1025 	cpunode = dr_cpu_find_node_md(cpuid, mdp, listp);
1026 
1027 	if (cpunode == MDE_INVAL_ELEM_COOKIE) {
1028 		rv = EINVAL;
1029 		goto done;
1030 	}
1031 
1032 	/* pass in MD cookie for CPU */
1033 	cba.mdp = mdp;
1034 	cba.cpunode = cpunode;
1035 
1036 	br.arg = (void *)&cba;
1037 	br.type = DEVI_BRANCH_SID;
1038 	br.create.sid_branch_create = new_cpu_node;
1039 	br.devi_branch_callback = NULL;
1040 	pdip = ddi_root_node();
1041 
1042 	if ((rv = e_ddi_branch_create(pdip, &br, NULL, 0))) {
1043 		DR_DBG_CPU("e_ddi_branch_create failed: %d\n", rv);
1044 		rv = -1;
1045 		goto done;
1046 	}
1047 
1048 	DR_DBG_CPU("CPU %d probed\n", cpuid);
1049 
1050 	rv = 0;
1051 
1052 done:
1053 	if (listp)
1054 		kmem_free(listp, listsz);
1055 
1056 	if (mdp)
1057 		(void) md_fini_handle(mdp);
1058 
1059 	return (rv);
1060 }
1061 
1062 static int
1063 dr_cpu_deprobe(processorid_t cpuid)
1064 {
1065 	dev_info_t	*fdip = NULL;
1066 	dev_info_t	*dip;
1067 
1068 	if ((dip = dr_cpu_find_node(cpuid)) == NULL) {
1069 		DR_DBG_CPU("cpuid %d already deprobed\n", cpuid);
1070 		return (0);
1071 	}
1072 
1073 	ASSERT(e_ddi_branch_held(dip));
1074 
1075 	if (e_ddi_branch_destroy(dip, &fdip, 0)) {
1076 		char *path = kmem_alloc(MAXPATHLEN, KM_SLEEP);
1077 
1078 		/*
1079 		 * If non-NULL, fdip is held and must be released.
1080 		 */
1081 		if (fdip != NULL) {
1082 			(void) ddi_pathname(fdip, path);
1083 			ddi_release_devi(fdip);
1084 		} else {
1085 			(void) ddi_pathname(dip, path);
1086 		}
1087 		cmn_err(CE_NOTE, "node removal failed: %s (%p)",
1088 		    path, (fdip) ? (void *)fdip : (void *)dip);
1089 
1090 		kmem_free(path, MAXPATHLEN);
1091 
1092 		return (-1);
1093 	}
1094 
1095 	DR_DBG_CPU("CPU %d deprobed\n", cpuid);
1096 
1097 	return (0);
1098 }
1099 
1100 typedef struct {
1101 	processorid_t	cpuid;
1102 	dev_info_t	*dip;
1103 } dr_search_arg_t;
1104 
1105 static int
1106 dr_cpu_check_node(dev_info_t *dip, void *arg)
1107 {
1108 	char 		*name;
1109 	processorid_t	cpuid;
1110 	dr_search_arg_t	*sarg = (dr_search_arg_t *)arg;
1111 
1112 	if (dip == ddi_root_node()) {
1113 		return (DDI_WALK_CONTINUE);
1114 	}
1115 
1116 	name = ddi_node_name(dip);
1117 
1118 	if (strcmp(name, "cpu") != 0) {
1119 		return (DDI_WALK_PRUNECHILD);
1120 	}
1121 
1122 	cpuid = ddi_prop_get_int(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS,
1123 	    "reg", -1);
1124 
1125 	cpuid = PROM_CFGHDL_TO_CPUID(cpuid);
1126 
1127 	DR_DBG_CPU("found cpuid=0x%x, looking for 0x%x\n", cpuid, sarg->cpuid);
1128 
1129 	if (cpuid == sarg->cpuid) {
1130 		DR_DBG_CPU("matching node\n");
1131 
1132 		/* matching node must be returned held */
1133 		if (!e_ddi_branch_held(dip))
1134 			e_ddi_branch_hold(dip);
1135 
1136 		sarg->dip = dip;
1137 		return (DDI_WALK_TERMINATE);
1138 	}
1139 
1140 	return (DDI_WALK_CONTINUE);
1141 }
1142 
1143 /*
1144  * Walk the device tree to find the dip corresponding to the cpuid
1145  * passed in. If present, the dip is returned held. The caller must
1146  * release the hold on the dip once it is no longer required. If no
1147  * matching node if found, NULL is returned.
1148  */
1149 static dev_info_t *
1150 dr_cpu_find_node(processorid_t cpuid)
1151 {
1152 	dr_search_arg_t	arg;
1153 
1154 	DR_DBG_CPU("dr_cpu_find_node...\n");
1155 
1156 	arg.cpuid = cpuid;
1157 	arg.dip = NULL;
1158 
1159 	ddi_walk_devs(ddi_root_node(), dr_cpu_check_node, &arg);
1160 
1161 	ASSERT((arg.dip == NULL) || (e_ddi_branch_held(arg.dip)));
1162 
1163 	return ((arg.dip) ? arg.dip : NULL);
1164 }
1165 
1166 /*
1167  * Look up a particular cpuid in the MD. Returns the mde_cookie_t
1168  * representing that CPU if present, and MDE_INVAL_ELEM_COOKIE
1169  * otherwise. It is assumed the scratch array has already been
1170  * allocated so that it can accommodate the worst case scenario,
1171  * every node in the MD.
1172  */
1173 static mde_cookie_t
1174 dr_cpu_find_node_md(processorid_t cpuid, md_t *mdp, mde_cookie_t *listp)
1175 {
1176 	int		idx;
1177 	int		nnodes;
1178 	mde_cookie_t	rootnode;
1179 	uint64_t	cpuid_prop;
1180 	mde_cookie_t	result = MDE_INVAL_ELEM_COOKIE;
1181 
1182 	rootnode = md_root_node(mdp);
1183 	ASSERT(rootnode != MDE_INVAL_ELEM_COOKIE);
1184 
1185 	/*
1186 	 * Scan the DAG for all the CPU nodes
1187 	 */
1188 	nnodes = md_scan_dag(mdp, rootnode, md_find_name(mdp, "cpu"),
1189 	    md_find_name(mdp, "fwd"), listp);
1190 
1191 	if (nnodes < 0) {
1192 		DR_DBG_CPU("Scan for CPUs failed\n");
1193 		return (result);
1194 	}
1195 
1196 	DR_DBG_CPU("dr_cpu_find_node_md: found %d CPUs in the MD\n", nnodes);
1197 
1198 	/*
1199 	 * Find the CPU of interest
1200 	 */
1201 	for (idx = 0; idx < nnodes; idx++) {
1202 
1203 		if (md_get_prop_val(mdp, listp[idx], "id", &cpuid_prop)) {
1204 			DR_DBG_CPU("Missing 'id' property for CPU node %d\n",
1205 			    idx);
1206 			break;
1207 		}
1208 
1209 		if (cpuid_prop == cpuid) {
1210 			/* found a match */
1211 			DR_DBG_CPU("dr_cpu_find_node_md: found CPU %d "
1212 			    "in MD\n", cpuid);
1213 			result = listp[idx];
1214 			break;
1215 		}
1216 	}
1217 
1218 	if (result == MDE_INVAL_ELEM_COOKIE) {
1219 		DR_DBG_CPU("CPU %d not in MD\n", cpuid);
1220 	}
1221 
1222 	return (result);
1223 }
1224