xref: /titanic_51/usr/src/uts/sun4u/ngdr/io/dr_mem.c (revision 36e852a172cba914383d7341c988128b2c667fbd)
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  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
23  * Use is subject to license terms.
24  */
25 
26 /*
27  * DR memory support routines.
28  */
29 
30 #include <sys/note.h>
31 #include <sys/debug.h>
32 #include <sys/types.h>
33 #include <sys/errno.h>
34 #include <sys/param.h>
35 #include <sys/dditypes.h>
36 #include <sys/kmem.h>
37 #include <sys/conf.h>
38 #include <sys/ddi.h>
39 #include <sys/sunddi.h>
40 #include <sys/sunndi.h>
41 #include <sys/ddi_impldefs.h>
42 #include <sys/ndi_impldefs.h>
43 #include <sys/sysmacros.h>
44 #include <sys/machsystm.h>
45 #include <sys/spitregs.h>
46 #include <sys/cpuvar.h>
47 #include <sys/promif.h>
48 #include <vm/seg_kmem.h>
49 #include <sys/lgrp.h>
50 #include <sys/platform_module.h>
51 
52 #include <vm/page.h>
53 
54 #include <sys/dr.h>
55 #include <sys/dr_util.h>
56 
57 extern struct memlist	*phys_install;
58 
59 /* TODO: push this reference below drmach line */
60 extern int		kcage_on;
61 
62 /* for the DR*INTERNAL_ERROR macros.  see sys/dr.h. */
63 static char *dr_ie_fmt = "dr_mem.c %d";
64 
65 static int	dr_post_detach_mem_unit(dr_mem_unit_t *mp);
66 static int	dr_reserve_mem_spans(memhandle_t *mhp, struct memlist *mlist);
67 static int	dr_select_mem_target(dr_handle_t *hp, dr_mem_unit_t *mp,
68     struct memlist *ml);
69 static void	dr_init_mem_unit_data(dr_mem_unit_t *mp);
70 
71 static int 	memlist_canfit(struct memlist *s_mlist,
72     struct memlist *t_mlist);
73 
74 /*
75  * dr_mem_unit_t.sbm_flags
76  */
77 #define	DR_MFLAG_RESERVED	0x01	/* mem unit reserved for delete */
78 #define	DR_MFLAG_SOURCE		0x02	/* source brd of copy/rename op */
79 #define	DR_MFLAG_TARGET		0x04	/* target brd of copy/rename op */
80 #define	DR_MFLAG_MEMUPSIZE	0x08	/* move from big to small board */
81 #define	DR_MFLAG_MEMDOWNSIZE	0x10	/* move from small to big board */
82 #define	DR_MFLAG_MEMRESIZE	0x18	/* move to different size board */
83 #define	DR_MFLAG_RELOWNER	0x20	/* memory release (delete) owner */
84 #define	DR_MFLAG_RELDONE	0x40	/* memory release (delete) done */
85 
86 /* helper macros */
87 #define	_ptob64(p) ((uint64_t)(p) << PAGESHIFT)
88 #define	_b64top(b) ((pgcnt_t)((b) >> PAGESHIFT))
89 
90 static struct memlist *
91 dr_get_memlist(dr_mem_unit_t *mp)
92 {
93 	struct memlist	*mlist = NULL;
94 	sbd_error_t	*err;
95 	static fn_t	f = "dr_get_memlist";
96 
97 	PR_MEM("%s for %s...\n", f, mp->sbm_cm.sbdev_path);
98 
99 	/*
100 	 * Return cached memlist, if present.
101 	 * This memlist will be present following an
102 	 * unconfigure (a.k.a: detach) of this memunit.
103 	 * It should only be used in the case were a configure
104 	 * is bringing this memunit back in without going
105 	 * through the disconnect and connect states.
106 	 */
107 	if (mp->sbm_mlist) {
108 		PR_MEM("%s: found cached memlist\n", f);
109 
110 		mlist = memlist_dup(mp->sbm_mlist);
111 	} else {
112 		uint64_t basepa = _ptob64(mp->sbm_basepfn);
113 
114 		/* attempt to construct a memlist using phys_install */
115 
116 		/* round down to slice base address */
117 		basepa &= ~(mp->sbm_slice_size - 1);
118 
119 		/* get a copy of phys_install to edit */
120 		memlist_read_lock();
121 		mlist = memlist_dup(phys_install);
122 		memlist_read_unlock();
123 
124 		/* trim lower irrelevant span */
125 		if (mlist)
126 			mlist = memlist_del_span(mlist, 0ull, basepa);
127 
128 		/* trim upper irrelevant span */
129 		if (mlist) {
130 			uint64_t endpa;
131 
132 			basepa += mp->sbm_slice_size;
133 			endpa = _ptob64(physmax + 1);
134 			if (endpa > basepa)
135 				mlist = memlist_del_span(
136 				    mlist,
137 				    basepa,
138 				    endpa - basepa);
139 		}
140 
141 		if (mlist) {
142 			/* successfully built a memlist */
143 			PR_MEM("%s: derived memlist from phys_install\n", f);
144 		}
145 
146 		/* if no mlist yet, try platform layer */
147 		if (!mlist) {
148 			err = drmach_mem_get_memlist(
149 			    mp->sbm_cm.sbdev_id, &mlist);
150 			if (err) {
151 				DRERR_SET_C(&mp->sbm_cm.sbdev_error, &err);
152 				mlist = NULL; /* paranoia */
153 			}
154 		}
155 	}
156 
157 	PR_MEM("%s: memlist for %s\n", f, mp->sbm_cm.sbdev_path);
158 	PR_MEMLIST_DUMP(mlist);
159 
160 	return (mlist);
161 }
162 
163 typedef struct {
164 	kcondvar_t cond;
165 	kmutex_t lock;
166 	int error;
167 	int done;
168 } dr_release_mem_sync_t;
169 
170 /*
171  * Memory has been logically removed by the time this routine is called.
172  */
173 static void
174 dr_mem_del_done(void *arg, int error)
175 {
176 	dr_release_mem_sync_t *ds = arg;
177 
178 	mutex_enter(&ds->lock);
179 	ds->error = error;
180 	ds->done = 1;
181 	cv_signal(&ds->cond);
182 	mutex_exit(&ds->lock);
183 }
184 
185 /*
186  * When we reach here the memory being drained should have
187  * already been reserved in dr_pre_release_mem().
188  * Our only task here is to kick off the "drain" and wait
189  * for it to finish.
190  */
191 void
192 dr_release_mem(dr_common_unit_t *cp)
193 {
194 	dr_mem_unit_t	*mp = (dr_mem_unit_t *)cp;
195 	int		err;
196 	dr_release_mem_sync_t rms;
197 	static fn_t	f = "dr_release_mem";
198 
199 	/* check that this memory unit has been reserved */
200 	if (!(mp->sbm_flags & DR_MFLAG_RELOWNER)) {
201 		DR_DEV_INTERNAL_ERROR(&mp->sbm_cm);
202 		return;
203 	}
204 
205 	bzero((void *) &rms, sizeof (rms));
206 
207 	mutex_init(&rms.lock, NULL, MUTEX_DRIVER, NULL);
208 	cv_init(&rms.cond, NULL, CV_DRIVER, NULL);
209 
210 	mutex_enter(&rms.lock);
211 	err = kphysm_del_start(mp->sbm_memhandle, dr_mem_del_done,
212 	    (void *) &rms);
213 	if (err == KPHYSM_OK) {
214 		/* wait for completion or interrupt */
215 		while (!rms.done) {
216 			if (cv_wait_sig(&rms.cond, &rms.lock) == 0) {
217 				/* then there is a pending UNIX signal */
218 				(void) kphysm_del_cancel(mp->sbm_memhandle);
219 
220 				/* wait for completion */
221 				while (!rms.done)
222 					cv_wait(&rms.cond, &rms.lock);
223 			}
224 		}
225 		/* get the result of the memory delete operation */
226 		err = rms.error;
227 	}
228 	mutex_exit(&rms.lock);
229 
230 	cv_destroy(&rms.cond);
231 	mutex_destroy(&rms.lock);
232 
233 	if (err != KPHYSM_OK) {
234 		int e_code;
235 
236 		switch (err) {
237 			case KPHYSM_ENOWORK:
238 				e_code = ESBD_NOERROR;
239 				break;
240 
241 			case KPHYSM_EHANDLE:
242 			case KPHYSM_ESEQUENCE:
243 				e_code = ESBD_INTERNAL;
244 				break;
245 
246 			case KPHYSM_ENOTVIABLE:
247 				e_code = ESBD_MEM_NOTVIABLE;
248 				break;
249 
250 			case KPHYSM_EREFUSED:
251 				e_code = ESBD_MEM_REFUSED;
252 				break;
253 
254 			case KPHYSM_ENONRELOC:
255 				e_code = ESBD_MEM_NONRELOC;
256 				break;
257 
258 			case KPHYSM_ECANCELLED:
259 				e_code = ESBD_MEM_CANCELLED;
260 				break;
261 
262 			case KPHYSM_ERESOURCE:
263 				e_code = ESBD_MEMFAIL;
264 				break;
265 
266 			default:
267 				cmn_err(CE_WARN,
268 				    "%s: unexpected kphysm error code %d,"
269 				    " id 0x%p",
270 				    f, err, mp->sbm_cm.sbdev_id);
271 
272 				e_code = ESBD_IO;
273 				break;
274 		}
275 
276 		if (e_code != ESBD_NOERROR) {
277 			dr_dev_err(CE_IGNORE, &mp->sbm_cm, e_code);
278 		}
279 	}
280 }
281 
282 void
283 dr_attach_mem(dr_handle_t *hp, dr_common_unit_t *cp)
284 {
285 	_NOTE(ARGUNUSED(hp))
286 
287 	dr_mem_unit_t	*mp = (dr_mem_unit_t *)cp;
288 	struct memlist	*ml, *mc;
289 	sbd_error_t	*err;
290 	static fn_t	f = "dr_attach_mem";
291 
292 	PR_MEM("%s...\n", f);
293 
294 	dr_lock_status(hp->h_bd);
295 	err = drmach_configure(cp->sbdev_id, 0);
296 	dr_unlock_status(hp->h_bd);
297 	if (err) {
298 		DRERR_SET_C(&cp->sbdev_error, &err);
299 		return;
300 	}
301 
302 	ml = dr_get_memlist(mp);
303 	for (mc = ml; mc; mc = mc->next) {
304 		int		 rv;
305 		sbd_error_t	*err;
306 
307 		rv = kphysm_add_memory_dynamic(
308 		    (pfn_t)(mc->address >> PAGESHIFT),
309 		    (pgcnt_t)(mc->size >> PAGESHIFT));
310 		if (rv != KPHYSM_OK) {
311 			/*
312 			 * translate kphysm error and
313 			 * store in devlist error
314 			 */
315 			switch (rv) {
316 			case KPHYSM_ERESOURCE:
317 				rv = ESBD_NOMEM;
318 				break;
319 
320 			case KPHYSM_EFAULT:
321 				rv = ESBD_FAULT;
322 				break;
323 
324 			default:
325 				rv = ESBD_INTERNAL;
326 				break;
327 			}
328 
329 			if (rv == ESBD_INTERNAL) {
330 				DR_DEV_INTERNAL_ERROR(&mp->sbm_cm);
331 			} else
332 				dr_dev_err(CE_WARN, &mp->sbm_cm, rv);
333 			break;
334 		}
335 
336 		err = drmach_mem_add_span(
337 		    mp->sbm_cm.sbdev_id, mc->address, mc->size);
338 		if (err) {
339 			DRERR_SET_C(&mp->sbm_cm.sbdev_error, &err);
340 			break;
341 		}
342 	}
343 
344 	memlist_delete(ml);
345 
346 	/* back out if configure failed */
347 	if (mp->sbm_cm.sbdev_error != NULL) {
348 		dr_lock_status(hp->h_bd);
349 		err = drmach_unconfigure(cp->sbdev_id,
350 		    DEVI_BRANCH_DESTROY);
351 		if (err)
352 			sbd_err_clear(&err);
353 		dr_unlock_status(hp->h_bd);
354 	}
355 }
356 
357 #define	DR_SCRUB_VALUE	0x0d0e0a0d0b0e0e0fULL
358 
359 static void
360 dr_mem_ecache_scrub(dr_mem_unit_t *mp, struct memlist *mlist)
361 {
362 #ifdef DEBUG
363 	clock_t		stime = ddi_get_lbolt();
364 #endif /* DEBUG */
365 
366 	struct memlist	*ml;
367 	uint64_t	scrub_value = DR_SCRUB_VALUE;
368 	processorid_t	cpuid;
369 	static fn_t	f = "dr_mem_ecache_scrub";
370 
371 	cpuid = drmach_mem_cpu_affinity(mp->sbm_cm.sbdev_id);
372 	affinity_set(cpuid);
373 
374 	PR_MEM("%s: using proc %d, memlist...\n", f,
375 	    (cpuid == CPU_CURRENT) ? CPU->cpu_id : cpuid);
376 	PR_MEMLIST_DUMP(mlist);
377 
378 	for (ml = mlist; ml; ml = ml->next) {
379 		uint64_t	dst_pa;
380 		uint64_t	nbytes;
381 
382 		/* calculate the destination physical address */
383 		dst_pa = ml->address;
384 		if (ml->address & PAGEOFFSET)
385 			cmn_err(CE_WARN,
386 			    "%s: address (0x%lx) not on "
387 			    "page boundary", f, ml->address);
388 
389 		nbytes = ml->size;
390 		if (ml->size & PAGEOFFSET)
391 			cmn_err(CE_WARN,
392 			    "%s: size (0x%lx) not on "
393 			    "page boundary", f, ml->size);
394 
395 		/*LINTED*/
396 		while (nbytes > 0) {
397 			/* write 64 bits to dst_pa */
398 			stdphys(dst_pa, scrub_value);
399 
400 			/* increment/decrement by cacheline sizes */
401 			dst_pa += DRMACH_COHERENCY_UNIT;
402 			nbytes -= DRMACH_COHERENCY_UNIT;
403 		}
404 	}
405 
406 	/*
407 	 * flush this cpu's ecache and take care to ensure
408 	 * that all of it's bus transactions have retired.
409 	 */
410 	drmach_cpu_flush_ecache_sync();
411 
412 	affinity_clear();
413 
414 #ifdef DEBUG
415 	stime = ddi_get_lbolt() - stime;
416 	PR_MEM("%s: scrub ticks = %ld (%ld secs)\n", f, stime, stime / hz);
417 #endif /* DEBUG */
418 }
419 
420 static int
421 dr_move_memory(dr_handle_t *hp, dr_mem_unit_t *s_mp, dr_mem_unit_t *t_mp)
422 {
423 	time_t		 copytime;
424 	drmachid_t	 cr_id;
425 	dr_sr_handle_t	*srhp;
426 	struct memlist	*c_ml, *d_ml;
427 	sbd_error_t	*err;
428 	static fn_t	 f = "dr_move_memory";
429 
430 	PR_MEM("%s: (INLINE) moving memory from %s to %s\n",
431 	    f,
432 	    s_mp->sbm_cm.sbdev_path,
433 	    t_mp->sbm_cm.sbdev_path);
434 
435 	ASSERT(s_mp->sbm_flags & DR_MFLAG_SOURCE);
436 	ASSERT(s_mp->sbm_peer == t_mp);
437 	ASSERT(s_mp->sbm_mlist);
438 
439 	ASSERT(t_mp->sbm_flags & DR_MFLAG_TARGET);
440 	ASSERT(t_mp->sbm_peer == s_mp);
441 
442 	/*
443 	 * create a memlist of spans to copy by removing
444 	 * the spans that have been deleted, if any, from
445 	 * the full source board memlist.  s_mp->sbm_del_mlist
446 	 * will be NULL if there were no spans deleted from
447 	 * the source board.
448 	 */
449 	c_ml = memlist_dup(s_mp->sbm_mlist);
450 	d_ml = s_mp->sbm_del_mlist;
451 	while (d_ml != NULL) {
452 		c_ml = memlist_del_span(c_ml, d_ml->address, d_ml->size);
453 		d_ml = d_ml->next;
454 	}
455 
456 	affinity_set(drmach_mem_cpu_affinity(t_mp->sbm_cm.sbdev_id));
457 
458 	err = drmach_copy_rename_init(
459 	    t_mp->sbm_cm.sbdev_id, _ptob64(t_mp->sbm_slice_offset),
460 	    s_mp->sbm_cm.sbdev_id, c_ml, &cr_id);
461 	if (err) {
462 		DRERR_SET_C(&s_mp->sbm_cm.sbdev_error, &err);
463 		affinity_clear();
464 		return (-1);
465 	}
466 
467 	srhp = dr_get_sr_handle(hp);
468 	ASSERT(srhp);
469 
470 	copytime = ddi_get_lbolt();
471 
472 	/* Quiesce the OS.  */
473 	if (dr_suspend(srhp)) {
474 		cmn_err(CE_WARN, "%s: failed to quiesce OS"
475 		    " for copy-rename", f);
476 
477 		dr_release_sr_handle(srhp);
478 		err = drmach_copy_rename_fini(cr_id);
479 		if (err) {
480 			/*
481 			 * no error is expected since the program has
482 			 * not yet run.
483 			 */
484 
485 			/* catch this in debug kernels */
486 			ASSERT(0);
487 
488 			sbd_err_clear(&err);
489 		}
490 
491 		/* suspend error reached via hp */
492 		s_mp->sbm_cm.sbdev_error = hp->h_err;
493 		hp->h_err = NULL;
494 
495 		affinity_clear();
496 		return (-1);
497 	}
498 
499 	/*
500 	 * Rename memory for lgroup.
501 	 * Source and target board numbers are packaged in arg.
502 	 */
503 	{
504 		dr_board_t	*t_bp, *s_bp;
505 
506 		s_bp = s_mp->sbm_cm.sbdev_bp;
507 		t_bp = t_mp->sbm_cm.sbdev_bp;
508 
509 		lgrp_plat_config(LGRP_CONFIG_MEM_RENAME,
510 		    (uintptr_t)(s_bp->b_num | (t_bp->b_num << 16)));
511 	}
512 
513 	drmach_copy_rename(cr_id);
514 
515 	/* Resume the OS.  */
516 	dr_resume(srhp);
517 
518 	copytime = ddi_get_lbolt() - copytime;
519 
520 	dr_release_sr_handle(srhp);
521 	err = drmach_copy_rename_fini(cr_id);
522 	if (err)
523 		DRERR_SET_C(&s_mp->sbm_cm.sbdev_error, &err);
524 
525 	affinity_clear();
526 
527 	PR_MEM("%s: copy-rename elapsed time = %ld ticks (%ld secs)\n",
528 	    f, copytime, copytime / hz);
529 
530 	/* return -1 if dr_suspend or copy/rename recorded an error */
531 	return (err == NULL ? 0 : -1);
532 }
533 
534 /*
535  * If detaching node contains memory that is "non-permanent"
536  * then the memory adr's are simply cleared.  If the memory
537  * is non-relocatable, then do a copy-rename.
538  */
539 void
540 dr_detach_mem(dr_handle_t *hp, dr_common_unit_t *cp)
541 {
542 	int			rv = 0;
543 	dr_mem_unit_t		*s_mp = (dr_mem_unit_t *)cp;
544 	dr_mem_unit_t		*t_mp;
545 	dr_state_t		state;
546 	static fn_t		f = "dr_detach_mem";
547 
548 	PR_MEM("%s...\n", f);
549 
550 	/* lookup target mem unit and target board structure, if any */
551 	if (s_mp->sbm_flags & DR_MFLAG_SOURCE) {
552 		t_mp = s_mp->sbm_peer;
553 		ASSERT(t_mp != NULL);
554 		ASSERT(t_mp->sbm_peer == s_mp);
555 	} else {
556 		t_mp = NULL;
557 	}
558 
559 	/* verify mem unit's state is UNREFERENCED */
560 	state = s_mp->sbm_cm.sbdev_state;
561 	if (state != DR_STATE_UNREFERENCED) {
562 		dr_dev_err(CE_IGNORE, &s_mp->sbm_cm, ESBD_STATE);
563 		return;
564 	}
565 
566 	/* verify target mem unit's state is UNREFERENCED, if any */
567 	if (t_mp != NULL) {
568 		state = t_mp->sbm_cm.sbdev_state;
569 		if (state != DR_STATE_UNREFERENCED) {
570 			dr_dev_err(CE_IGNORE, &t_mp->sbm_cm, ESBD_STATE);
571 			return;
572 		}
573 	}
574 
575 	/*
576 	 * Scrub deleted memory.  This will cause all cachelines
577 	 * referencing the memory to only be in the local cpu's
578 	 * ecache.
579 	 */
580 	if (s_mp->sbm_flags & DR_MFLAG_RELDONE) {
581 		/* no del mlist for src<=dst mem size copy/rename */
582 		if (s_mp->sbm_del_mlist)
583 			dr_mem_ecache_scrub(s_mp, s_mp->sbm_del_mlist);
584 	}
585 	if (t_mp != NULL && (t_mp->sbm_flags & DR_MFLAG_RELDONE)) {
586 		ASSERT(t_mp->sbm_del_mlist);
587 		dr_mem_ecache_scrub(t_mp, t_mp->sbm_del_mlist);
588 	}
589 
590 	/*
591 	 * If there is no target board (no copy/rename was needed), then
592 	 * we're done!
593 	 */
594 	if (t_mp == NULL) {
595 		sbd_error_t *err;
596 		/*
597 		 * Reprogram interconnect hardware and disable
598 		 * memory controllers for memory node that's going away.
599 		 */
600 
601 		err = drmach_mem_disable(s_mp->sbm_cm.sbdev_id);
602 		if (err) {
603 			DRERR_SET_C(&s_mp->sbm_cm.sbdev_error, &err);
604 			rv = -1;
605 		}
606 	} else {
607 		rv = dr_move_memory(hp, s_mp, t_mp);
608 		PR_MEM("%s: %s memory COPY-RENAME (board %d -> %d)\n",
609 		    f,
610 		    rv ? "FAILED" : "COMPLETED",
611 		    s_mp->sbm_cm.sbdev_bp->b_num,
612 		    t_mp->sbm_cm.sbdev_bp->b_num);
613 
614 		if (rv != 0)
615 			(void) dr_cancel_mem(s_mp);
616 	}
617 
618 	if (rv == 0) {
619 		sbd_error_t *err;
620 
621 		dr_lock_status(hp->h_bd);
622 		err = drmach_unconfigure(s_mp->sbm_cm.sbdev_id,
623 		    DEVI_BRANCH_DESTROY);
624 		dr_unlock_status(hp->h_bd);
625 		if (err)
626 			sbd_err_clear(&err);
627 	}
628 }
629 
630 #ifndef _STARFIRE
631 /*
632  * XXX workaround for certain lab configurations (see also starcat drmach.c)
633  * Temporary code to get around observed incorrect results from
634  * kphysm_del_span_query when the queried span contains address spans
635  * not occupied by memory in between spans that do have memory.
636  * This routine acts as a wrapper to kphysm_del_span_query.  It builds
637  * a memlist from phys_install of spans that exist between base and
638  * base + npages, inclusively.  Kphysm_del_span_query is called for each
639  * node in the memlist with the results accumulated in *mp.
640  */
641 static int
642 dr_del_span_query(pfn_t base, pgcnt_t npages, memquery_t *mp)
643 {
644 	uint64_t	 pa = _ptob64(base);
645 	uint64_t	 sm = ~ (137438953472ull - 1);
646 	uint64_t	 sa = pa & sm;
647 	struct memlist	*mlist, *ml;
648 	int		 rv;
649 
650 	npages = npages; /* silence lint */
651 	memlist_read_lock();
652 	mlist = memlist_dup(phys_install);
653 	memlist_read_unlock();
654 
655 again:
656 	for (ml = mlist; ml; ml = ml->next) {
657 		if ((ml->address & sm) != sa) {
658 			mlist = memlist_del_span(mlist, ml->address, ml->size);
659 			goto again;
660 		}
661 	}
662 
663 	mp->phys_pages = 0;
664 	mp->managed = 0;
665 	mp->nonrelocatable = 0;
666 	mp->first_nonrelocatable = (pfn_t)-1;	/* XXX */
667 	mp->last_nonrelocatable = 0;
668 
669 	for (ml = mlist; ml; ml = ml->next) {
670 		memquery_t mq;
671 
672 		rv = kphysm_del_span_query(
673 		    _b64top(ml->address), _b64top(ml->size), &mq);
674 		if (rv)
675 			break;
676 
677 		mp->phys_pages += mq.phys_pages;
678 		mp->managed += mq.managed;
679 		mp->nonrelocatable += mq.nonrelocatable;
680 
681 		if (mq.nonrelocatable != 0) {
682 			if (mq.first_nonrelocatable < mp->first_nonrelocatable)
683 				mp->first_nonrelocatable =
684 				    mq.first_nonrelocatable;
685 			if (mq.last_nonrelocatable > mp->last_nonrelocatable)
686 				mp->last_nonrelocatable =
687 				    mq.last_nonrelocatable;
688 		}
689 	}
690 
691 	if (mp->nonrelocatable == 0)
692 		mp->first_nonrelocatable = 0;	/* XXX */
693 
694 	memlist_delete(mlist);
695 	return (rv);
696 }
697 
698 #define	kphysm_del_span_query dr_del_span_query
699 #endif /* _STARFIRE */
700 
701 /*
702  * NOTE: This routine is only partially smart about multiple
703  *	 mem-units.  Need to make mem-status structure smart
704  *	 about them also.
705  */
706 int
707 dr_mem_status(dr_handle_t *hp, dr_devset_t devset, sbd_dev_stat_t *dsp)
708 {
709 	int		m, mix;
710 	memdelstat_t	mdst;
711 	memquery_t	mq;
712 	dr_board_t	*bp;
713 	dr_mem_unit_t	*mp;
714 	sbd_mem_stat_t	*msp;
715 	static fn_t	f = "dr_mem_status";
716 
717 	bp = hp->h_bd;
718 	devset &= DR_DEVS_PRESENT(bp);
719 
720 	for (m = mix = 0; m < MAX_MEM_UNITS_PER_BOARD; m++) {
721 		int		rv;
722 		sbd_error_t	*err;
723 		drmach_status_t	 pstat;
724 		dr_mem_unit_t	*p_mp;
725 
726 		if (DEVSET_IN_SET(devset, SBD_COMP_MEM, m) == 0)
727 			continue;
728 
729 		mp = dr_get_mem_unit(bp, m);
730 
731 		if (mp->sbm_cm.sbdev_state == DR_STATE_EMPTY) {
732 			/* present, but not fully initialized */
733 			continue;
734 		}
735 
736 		if (mp->sbm_cm.sbdev_id == (drmachid_t)0)
737 			continue;
738 
739 		/* fetch platform status */
740 		err = drmach_status(mp->sbm_cm.sbdev_id, &pstat);
741 		if (err) {
742 			DRERR_SET_C(&mp->sbm_cm.sbdev_error, &err);
743 			continue;
744 		}
745 
746 		msp = &dsp->d_mem;
747 		bzero((caddr_t)msp, sizeof (*msp));
748 
749 		strncpy(msp->ms_cm.c_id.c_name, pstat.type,
750 		    sizeof (msp->ms_cm.c_id.c_name));
751 		msp->ms_cm.c_id.c_type = mp->sbm_cm.sbdev_type;
752 		msp->ms_cm.c_id.c_unit = SBD_NULL_UNIT;
753 		msp->ms_cm.c_cond = mp->sbm_cm.sbdev_cond;
754 		msp->ms_cm.c_busy = mp->sbm_cm.sbdev_busy | pstat.busy;
755 		msp->ms_cm.c_time = mp->sbm_cm.sbdev_time;
756 		msp->ms_cm.c_ostate = mp->sbm_cm.sbdev_ostate;
757 
758 		msp->ms_totpages = mp->sbm_npages;
759 		msp->ms_basepfn = mp->sbm_basepfn;
760 		msp->ms_pageslost = mp->sbm_pageslost;
761 		msp->ms_cage_enabled = kcage_on;
762 
763 		if (mp->sbm_flags & DR_MFLAG_RESERVED)
764 			p_mp = mp->sbm_peer;
765 		else
766 			p_mp = NULL;
767 
768 		if (p_mp == NULL) {
769 			msp->ms_peer_is_target = 0;
770 			msp->ms_peer_ap_id[0] = '\0';
771 		} else if (p_mp->sbm_flags & DR_MFLAG_RESERVED) {
772 			char *path = kmem_alloc(MAXPATHLEN, KM_SLEEP);
773 			char *minor;
774 
775 			/*
776 			 * b_dip doesn't have to be held for ddi_pathname()
777 			 * because the board struct (dr_board_t) will be
778 			 * destroyed before b_dip detaches.
779 			 */
780 			(void) ddi_pathname(bp->b_dip, path);
781 			minor = strchr(p_mp->sbm_cm.sbdev_path, ':');
782 
783 			snprintf(msp->ms_peer_ap_id,
784 			    sizeof (msp->ms_peer_ap_id), "%s%s",
785 			    path, (minor == NULL) ? "" : minor);
786 
787 			kmem_free(path, MAXPATHLEN);
788 
789 			if (p_mp->sbm_flags & DR_MFLAG_TARGET)
790 				msp->ms_peer_is_target = 1;
791 		}
792 
793 		if (mp->sbm_flags & DR_MFLAG_RELOWNER)
794 			rv = kphysm_del_status(mp->sbm_memhandle, &mdst);
795 		else
796 			rv = KPHYSM_EHANDLE;	/* force 'if' to fail */
797 
798 		if (rv == KPHYSM_OK) {
799 			/*
800 			 * Any pages above managed is "free",
801 			 * i.e. it's collected.
802 			 */
803 			msp->ms_detpages += (uint_t)(mdst.collected +
804 			    mdst.phys_pages - mdst.managed);
805 		} else {
806 			/*
807 			 * If we're UNREFERENCED or UNCONFIGURED,
808 			 * then the number of detached pages is
809 			 * however many pages are on the board.
810 			 * I.e. detached = not in use by OS.
811 			 */
812 			switch (msp->ms_cm.c_ostate) {
813 			/*
814 			 * changed to use cfgadm states
815 			 *
816 			 * was:
817 			 *	case DR_STATE_UNREFERENCED:
818 			 *	case DR_STATE_UNCONFIGURED:
819 			 */
820 			case SBD_STAT_UNCONFIGURED:
821 				msp->ms_detpages = msp->ms_totpages;
822 				break;
823 
824 			default:
825 				break;
826 			}
827 		}
828 
829 		/*
830 		 * kphysm_del_span_query can report non-reloc pages = total
831 		 * pages for memory that is not yet configured
832 		 */
833 		if (mp->sbm_cm.sbdev_state != DR_STATE_UNCONFIGURED) {
834 
835 			rv = kphysm_del_span_query(mp->sbm_basepfn,
836 			    mp->sbm_npages, &mq);
837 
838 			if (rv == KPHYSM_OK) {
839 				msp->ms_managed_pages = mq.managed;
840 				msp->ms_noreloc_pages = mq.nonrelocatable;
841 				msp->ms_noreloc_first =
842 				    mq.first_nonrelocatable;
843 				msp->ms_noreloc_last =
844 				    mq.last_nonrelocatable;
845 				msp->ms_cm.c_sflags = 0;
846 				if (mq.nonrelocatable) {
847 					SBD_SET_SUSPEND(SBD_CMD_UNCONFIGURE,
848 					    msp->ms_cm.c_sflags);
849 				}
850 			} else {
851 				PR_MEM("%s: kphysm_del_span_query() = %d\n",
852 				    f, rv);
853 			}
854 		}
855 
856 		/*
857 		 * Check source unit state during copy-rename
858 		 */
859 		if ((mp->sbm_flags & DR_MFLAG_SOURCE) &&
860 		    (mp->sbm_cm.sbdev_state == DR_STATE_UNREFERENCED ||
861 		    mp->sbm_cm.sbdev_state == DR_STATE_RELEASE))
862 			msp->ms_cm.c_ostate = SBD_STAT_CONFIGURED;
863 
864 		mix++;
865 		dsp++;
866 	}
867 
868 	return (mix);
869 }
870 
871 int
872 dr_pre_attach_mem(dr_handle_t *hp, dr_common_unit_t **devlist, int devnum)
873 {
874 	_NOTE(ARGUNUSED(hp))
875 
876 	int		err_flag = 0;
877 	int		d;
878 	sbd_error_t	*err;
879 	static fn_t	f = "dr_pre_attach_mem";
880 
881 	PR_MEM("%s...\n", f);
882 
883 	for (d = 0; d < devnum; d++) {
884 		dr_mem_unit_t	*mp = (dr_mem_unit_t *)devlist[d];
885 		dr_state_t	state;
886 
887 		cmn_err(CE_CONT, "OS configure %s", mp->sbm_cm.sbdev_path);
888 
889 		state = mp->sbm_cm.sbdev_state;
890 		switch (state) {
891 		case DR_STATE_UNCONFIGURED:
892 			PR_MEM("%s: recovering from UNCONFIG for %s\n",
893 			    f,
894 			    mp->sbm_cm.sbdev_path);
895 
896 			/* use memlist cached by dr_post_detach_mem_unit */
897 			ASSERT(mp->sbm_mlist != NULL);
898 			PR_MEM("%s: re-configuring cached memlist for %s:\n",
899 			    f, mp->sbm_cm.sbdev_path);
900 			PR_MEMLIST_DUMP(mp->sbm_mlist);
901 
902 			/* kphysm del handle should be have been freed */
903 			ASSERT((mp->sbm_flags & DR_MFLAG_RELOWNER) == 0);
904 
905 			/*FALLTHROUGH*/
906 
907 		case DR_STATE_CONNECTED:
908 			PR_MEM("%s: reprogramming mem hardware on %s\n",
909 			    f, mp->sbm_cm.sbdev_bp->b_path);
910 
911 			PR_MEM("%s: enabling %s\n",
912 			    f, mp->sbm_cm.sbdev_path);
913 
914 			err = drmach_mem_enable(mp->sbm_cm.sbdev_id);
915 			if (err) {
916 				DRERR_SET_C(&mp->sbm_cm.sbdev_error, &err);
917 				err_flag = 1;
918 			}
919 			break;
920 
921 		default:
922 			dr_dev_err(CE_WARN, &mp->sbm_cm, ESBD_STATE);
923 			err_flag = 1;
924 			break;
925 		}
926 
927 		/* exit for loop if error encountered */
928 		if (err_flag)
929 			break;
930 	}
931 
932 	return (err_flag ? -1 : 0);
933 }
934 
935 int
936 dr_post_attach_mem(dr_handle_t *hp, dr_common_unit_t **devlist, int devnum)
937 {
938 	_NOTE(ARGUNUSED(hp))
939 
940 	int		d;
941 	static fn_t	f = "dr_post_attach_mem";
942 
943 	PR_MEM("%s...\n", f);
944 
945 	for (d = 0; d < devnum; d++) {
946 		dr_mem_unit_t	*mp = (dr_mem_unit_t *)devlist[d];
947 		struct memlist	*mlist, *ml;
948 
949 		mlist = dr_get_memlist(mp);
950 		if (mlist == NULL) {
951 			dr_dev_err(CE_WARN, &mp->sbm_cm, ESBD_MEMFAIL);
952 			continue;
953 		}
954 
955 		/*
956 		 * Verify the memory really did successfully attach
957 		 * by checking for its existence in phys_install.
958 		 */
959 		memlist_read_lock();
960 		if (memlist_intersect(phys_install, mlist) == 0) {
961 			memlist_read_unlock();
962 
963 			DR_DEV_INTERNAL_ERROR(&mp->sbm_cm);
964 
965 			PR_MEM("%s: %s memlist not in phys_install",
966 			    f, mp->sbm_cm.sbdev_path);
967 
968 			memlist_delete(mlist);
969 			continue;
970 		}
971 		memlist_read_unlock();
972 
973 		for (ml = mlist; ml != NULL; ml = ml->next) {
974 			sbd_error_t *err;
975 
976 			err = drmach_mem_add_span(
977 			    mp->sbm_cm.sbdev_id,
978 			    ml->address,
979 			    ml->size);
980 			if (err)
981 				DRERR_SET_C(&mp->sbm_cm.sbdev_error, &err);
982 		}
983 
984 		memlist_delete(mlist);
985 
986 		/*
987 		 * Destroy cached memlist, if any.
988 		 * There will be a cached memlist in sbm_mlist if
989 		 * this board is being configured directly after
990 		 * an unconfigure.
991 		 * To support this transition, dr_post_detach_mem
992 		 * left a copy of the last known memlist in sbm_mlist.
993 		 * This memlist could differ from any derived from
994 		 * hardware if while this memunit was last configured
995 		 * the system detected and deleted bad pages from
996 		 * phys_install.  The location of those bad pages
997 		 * will be reflected in the cached memlist.
998 		 */
999 		if (mp->sbm_mlist) {
1000 			memlist_delete(mp->sbm_mlist);
1001 			mp->sbm_mlist = NULL;
1002 		}
1003 
1004 /*
1005  * TODO: why is this call to dr_init_mem_unit_data here?
1006  * this has been done at discovery or connect time, so this is
1007  * probably redundant and unnecessary.
1008  */
1009 		dr_init_mem_unit_data(mp);
1010 	}
1011 
1012 	return (0);
1013 }
1014 
1015 int
1016 dr_pre_detach_mem(dr_handle_t *hp, dr_common_unit_t **devlist, int devnum)
1017 {
1018 	_NOTE(ARGUNUSED(hp))
1019 
1020 	int d;
1021 
1022 	for (d = 0; d < devnum; d++) {
1023 		dr_mem_unit_t *mp = (dr_mem_unit_t *)devlist[d];
1024 
1025 		cmn_err(CE_CONT, "OS unconfigure %s", mp->sbm_cm.sbdev_path);
1026 	}
1027 
1028 	return (0);
1029 }
1030 
1031 
1032 int
1033 dr_post_detach_mem(dr_handle_t *hp, dr_common_unit_t **devlist, int devnum)
1034 {
1035 	_NOTE(ARGUNUSED(hp))
1036 
1037 	int		d, rv;
1038 	static fn_t	f = "dr_post_detach_mem";
1039 
1040 	PR_MEM("%s...\n", f);
1041 
1042 	rv = 0;
1043 	for (d = 0; d < devnum; d++) {
1044 		dr_mem_unit_t	*mp = (dr_mem_unit_t *)devlist[d];
1045 
1046 		ASSERT(mp->sbm_cm.sbdev_bp == hp->h_bd);
1047 
1048 		if (dr_post_detach_mem_unit(mp))
1049 			rv = -1;
1050 	}
1051 
1052 	return (rv);
1053 }
1054 
1055 static void
1056 dr_add_memory_spans(dr_mem_unit_t *mp, struct memlist *ml)
1057 {
1058 	static fn_t	f = "dr_add_memory_spans";
1059 
1060 	PR_MEM("%s...", f);
1061 	PR_MEMLIST_DUMP(ml);
1062 
1063 #ifdef DEBUG
1064 	memlist_read_lock();
1065 	if (memlist_intersect(phys_install, ml)) {
1066 		PR_MEM("%s:WARNING: memlist intersects with phys_install\n", f);
1067 	}
1068 	memlist_read_unlock();
1069 #endif
1070 
1071 	for (; ml; ml = ml->next) {
1072 		pfn_t		 base;
1073 		pgcnt_t		 npgs;
1074 		int		 rv;
1075 		sbd_error_t	*err;
1076 
1077 		base = _b64top(ml->address);
1078 		npgs = _b64top(ml->size);
1079 
1080 		rv = kphysm_add_memory_dynamic(base, npgs);
1081 
1082 		err = drmach_mem_add_span(
1083 		    mp->sbm_cm.sbdev_id,
1084 		    ml->address,
1085 		    ml->size);
1086 
1087 		if (err)
1088 			DRERR_SET_C(&mp->sbm_cm.sbdev_error, &err);
1089 
1090 		if (rv != KPHYSM_OK) {
1091 			cmn_err(CE_WARN, "%s:"
1092 			    " unexpected kphysm_add_memory_dynamic"
1093 			    " return value %d;"
1094 			    " basepfn=0x%lx, npages=%ld\n",
1095 			    f, rv, base, npgs);
1096 
1097 			continue;
1098 		}
1099 	}
1100 }
1101 
1102 static int
1103 dr_post_detach_mem_unit(dr_mem_unit_t *s_mp)
1104 {
1105 	uint64_t	sz = s_mp->sbm_slice_size;
1106 	uint64_t	sm = sz - 1;
1107 	/* old and new below refer to PAs before and after copy-rename */
1108 	uint64_t	s_old_basepa, s_new_basepa;
1109 	uint64_t	t_old_basepa, t_new_basepa;
1110 	uint64_t	t_new_smallsize = 0;
1111 	dr_mem_unit_t	*t_mp, *x_mp;
1112 	struct memlist	*ml;
1113 	int		rv;
1114 	sbd_error_t	*err;
1115 	static fn_t	f = "dr_post_detach_mem_unit";
1116 
1117 	PR_MEM("%s...\n", f);
1118 
1119 	/* s_mp->sbm_del_mlist could be NULL, meaning no deleted spans */
1120 	PR_MEM("%s: %s: deleted memlist (EMPTY maybe okay):\n",
1121 	    f, s_mp->sbm_cm.sbdev_path);
1122 	PR_MEMLIST_DUMP(s_mp->sbm_del_mlist);
1123 
1124 	/* sanity check */
1125 	ASSERT(s_mp->sbm_del_mlist == NULL ||
1126 	    (s_mp->sbm_flags & DR_MFLAG_RELDONE) != 0);
1127 
1128 	if (s_mp->sbm_flags & DR_MFLAG_SOURCE) {
1129 		t_mp = s_mp->sbm_peer;
1130 		ASSERT(t_mp != NULL);
1131 		ASSERT(t_mp->sbm_flags & DR_MFLAG_TARGET);
1132 		ASSERT(t_mp->sbm_peer == s_mp);
1133 
1134 		ASSERT(t_mp->sbm_flags & DR_MFLAG_RELDONE);
1135 		ASSERT(t_mp->sbm_del_mlist);
1136 
1137 		PR_MEM("%s: target %s: deleted memlist:\n",
1138 		    f, t_mp->sbm_cm.sbdev_path);
1139 		PR_MEMLIST_DUMP(t_mp->sbm_del_mlist);
1140 	} else {
1141 		/* this is no target unit */
1142 		t_mp = NULL;
1143 	}
1144 
1145 	/*
1146 	 * Verify the memory really did successfully detach
1147 	 * by checking for its non-existence in phys_install.
1148 	 */
1149 	rv = 0;
1150 	memlist_read_lock();
1151 	if (s_mp->sbm_flags & DR_MFLAG_RELDONE) {
1152 		x_mp = s_mp;
1153 		rv = memlist_intersect(phys_install, x_mp->sbm_del_mlist);
1154 	}
1155 	if (rv == 0 && t_mp && (t_mp->sbm_flags & DR_MFLAG_RELDONE)) {
1156 		x_mp = t_mp;
1157 		rv = memlist_intersect(phys_install, x_mp->sbm_del_mlist);
1158 	}
1159 	memlist_read_unlock();
1160 
1161 	if (rv) {
1162 		/* error: memlist still in phys_install */
1163 		DR_DEV_INTERNAL_ERROR(&x_mp->sbm_cm);
1164 	}
1165 
1166 	/*
1167 	 * clean mem unit state and bail out if an error has been recorded.
1168 	 */
1169 	rv = 0;
1170 	if (s_mp->sbm_cm.sbdev_error) {
1171 		PR_MEM("%s: %s flags=%x", f,
1172 		    s_mp->sbm_cm.sbdev_path, s_mp->sbm_flags);
1173 		DR_DEV_CLR_UNREFERENCED(&s_mp->sbm_cm);
1174 		DR_DEV_CLR_RELEASED(&s_mp->sbm_cm);
1175 		dr_device_transition(&s_mp->sbm_cm, DR_STATE_CONFIGURED);
1176 		rv = -1;
1177 	}
1178 	if (t_mp != NULL && t_mp->sbm_cm.sbdev_error != NULL) {
1179 		PR_MEM("%s: %s flags=%x", f,
1180 		    s_mp->sbm_cm.sbdev_path, s_mp->sbm_flags);
1181 		DR_DEV_CLR_UNREFERENCED(&t_mp->sbm_cm);
1182 		DR_DEV_CLR_RELEASED(&t_mp->sbm_cm);
1183 		dr_device_transition(&t_mp->sbm_cm, DR_STATE_CONFIGURED);
1184 		rv = -1;
1185 	}
1186 	if (rv)
1187 		goto cleanup;
1188 
1189 	s_old_basepa = _ptob64(s_mp->sbm_basepfn);
1190 	err = drmach_mem_get_base_physaddr(s_mp->sbm_cm.sbdev_id,
1191 	    &s_new_basepa);
1192 	ASSERT(err == NULL);
1193 
1194 	PR_MEM("%s:s_old_basepa: 0x%lx\n", f, s_old_basepa);
1195 	PR_MEM("%s:s_new_basepa: 0x%lx\n", f, s_new_basepa);
1196 
1197 	if (t_mp != NULL) {
1198 		struct memlist *s_copy_mlist;
1199 
1200 		t_old_basepa	= _ptob64(t_mp->sbm_basepfn);
1201 		err = drmach_mem_get_base_physaddr(t_mp->sbm_cm.sbdev_id,
1202 		    &t_new_basepa);
1203 		ASSERT(err == NULL);
1204 
1205 		PR_MEM("%s:t_old_basepa: 0x%lx\n", f, t_old_basepa);
1206 		PR_MEM("%s:t_new_basepa: 0x%lx\n", f, t_new_basepa);
1207 
1208 		/*
1209 		 * Construct copy list with original source addresses.
1210 		 * Used to add back excess target mem.
1211 		 */
1212 		s_copy_mlist = memlist_dup(s_mp->sbm_mlist);
1213 		for (ml = s_mp->sbm_del_mlist; ml; ml = ml->next) {
1214 			s_copy_mlist = memlist_del_span(s_copy_mlist,
1215 			    ml->address, ml->size);
1216 		}
1217 
1218 		PR_MEM("%s: source copy list:\n:", f);
1219 		PR_MEMLIST_DUMP(s_copy_mlist);
1220 
1221 		/*
1222 		 * We had to swap mem-units, so update
1223 		 * memlists accordingly with new base
1224 		 * addresses.
1225 		 */
1226 		for (ml = t_mp->sbm_mlist; ml; ml = ml->next) {
1227 			ml->address -= t_old_basepa;
1228 			ml->address += t_new_basepa;
1229 		}
1230 
1231 		/*
1232 		 * There is no need to explicitly rename the target delete
1233 		 * memlist, because sbm_del_mlist and sbm_mlist always
1234 		 * point to the same memlist for a copy/rename operation.
1235 		 */
1236 		ASSERT(t_mp->sbm_del_mlist == t_mp->sbm_mlist);
1237 
1238 		PR_MEM("%s: renamed target memlist and delete memlist:\n", f);
1239 		PR_MEMLIST_DUMP(t_mp->sbm_mlist);
1240 
1241 		for (ml = s_mp->sbm_mlist; ml; ml = ml->next) {
1242 			ml->address -= s_old_basepa;
1243 			ml->address += s_new_basepa;
1244 		}
1245 
1246 		PR_MEM("%s: renamed source memlist:\n", f);
1247 		PR_MEMLIST_DUMP(s_mp->sbm_mlist);
1248 
1249 		/*
1250 		 * Keep track of dynamically added segments
1251 		 * since they cannot be split if we need to delete
1252 		 * excess source memory later for this board.
1253 		 */
1254 		if (t_mp->sbm_dyn_segs)
1255 			memlist_delete(t_mp->sbm_dyn_segs);
1256 		t_mp->sbm_dyn_segs = s_mp->sbm_dyn_segs;
1257 		s_mp->sbm_dyn_segs = NULL;
1258 
1259 		/*
1260 		 * If the target memory range with the new target base PA
1261 		 * extends beyond the usable slice, prevent any "target excess"
1262 		 * from being added back after this copy/rename and
1263 		 * calculate the new smaller size of the target board
1264 		 * to be set as part of target cleanup. The base + npages
1265 		 * must only include the range of memory up to the end of
1266 		 * this slice. This will only be used after a category 4
1267 		 * large-to-small target type copy/rename - see comments
1268 		 * in dr_select_mem_target.
1269 		 */
1270 		if (((t_new_basepa & sm) + _ptob64(t_mp->sbm_npages)) > sz) {
1271 			t_new_smallsize = sz - (t_new_basepa & sm);
1272 		}
1273 
1274 		if (s_mp->sbm_flags & DR_MFLAG_MEMRESIZE &&
1275 		    t_new_smallsize == 0) {
1276 			struct memlist	*t_excess_mlist;
1277 
1278 			/*
1279 			 * Add back excess target memory.
1280 			 * Subtract out the portion of the target memory
1281 			 * node that was taken over by the source memory
1282 			 * node.
1283 			 */
1284 			t_excess_mlist = memlist_dup(t_mp->sbm_mlist);
1285 			for (ml = s_copy_mlist; ml; ml = ml->next) {
1286 				t_excess_mlist =
1287 				    memlist_del_span(t_excess_mlist,
1288 				    ml->address, ml->size);
1289 			}
1290 
1291 			/*
1292 			 * Update dynamically added segs
1293 			 */
1294 			for (ml = s_mp->sbm_del_mlist; ml; ml = ml->next) {
1295 				t_mp->sbm_dyn_segs =
1296 				    memlist_del_span(t_mp->sbm_dyn_segs,
1297 				    ml->address, ml->size);
1298 			}
1299 			for (ml = t_excess_mlist; ml; ml = ml->next) {
1300 				t_mp->sbm_dyn_segs =
1301 				    memlist_cat_span(t_mp->sbm_dyn_segs,
1302 				    ml->address, ml->size);
1303 			}
1304 			PR_MEM("%s: %s: updated dynamic seg list:\n",
1305 			    f, t_mp->sbm_cm.sbdev_path);
1306 			PR_MEMLIST_DUMP(t_mp->sbm_dyn_segs);
1307 
1308 			PR_MEM("%s: adding back remaining portion"
1309 			    " of %s, memlist:\n",
1310 			    f, t_mp->sbm_cm.sbdev_path);
1311 			PR_MEMLIST_DUMP(t_excess_mlist);
1312 
1313 			dr_add_memory_spans(s_mp, t_excess_mlist);
1314 			memlist_delete(t_excess_mlist);
1315 		}
1316 		memlist_delete(s_copy_mlist);
1317 
1318 #ifdef DEBUG
1319 		/*
1320 		 * Renaming s_mp->sbm_del_mlist is not necessary.  This
1321 		 * list is not used beyond this point, and in fact, is
1322 		 * disposed of at the end of this function.
1323 		 */
1324 		for (ml = s_mp->sbm_del_mlist; ml; ml = ml->next) {
1325 			ml->address -= s_old_basepa;
1326 			ml->address += s_new_basepa;
1327 		}
1328 
1329 		PR_MEM("%s: renamed source delete memlist", f);
1330 		PR_MEMLIST_DUMP(s_mp->sbm_del_mlist);
1331 #endif
1332 
1333 	}
1334 
1335 	if (t_mp != NULL) {
1336 		/* delete target's entire address space */
1337 		err = drmach_mem_del_span(t_mp->sbm_cm.sbdev_id,
1338 		    t_old_basepa & ~ sm, sz);
1339 		if (err)
1340 			DRERR_SET_C(&t_mp->sbm_cm.sbdev_error, &err);
1341 		ASSERT(err == NULL);
1342 
1343 		/*
1344 		 * After the copy/rename, the original address space
1345 		 * for the source board (which is now located on the
1346 		 * target board) may now have some excess to be deleted.
1347 		 * The amount is calculated by masking the slice
1348 		 * info and keeping the slice offset from t_new_basepa.
1349 		 */
1350 		err = drmach_mem_del_span(s_mp->sbm_cm.sbdev_id,
1351 		    s_old_basepa & ~ sm, t_new_basepa & sm);
1352 		if (err)
1353 			DRERR_SET_C(&s_mp->sbm_cm.sbdev_error, &err);
1354 		ASSERT(err == NULL);
1355 
1356 	} else {
1357 		/* delete board's entire address space */
1358 		err = drmach_mem_del_span(s_mp->sbm_cm.sbdev_id,
1359 		    s_old_basepa & ~ sm, sz);
1360 		if (err)
1361 			DRERR_SET_C(&s_mp->sbm_cm.sbdev_error, &err);
1362 		ASSERT(err == NULL);
1363 	}
1364 
1365 cleanup:
1366 	/* clean up target mem unit */
1367 	if (t_mp != NULL) {
1368 		memlist_delete(t_mp->sbm_del_mlist);
1369 		/* no need to delete sbm_mlist, it shares sbm_del_mlist */
1370 
1371 		t_mp->sbm_del_mlist = NULL;
1372 		t_mp->sbm_mlist = NULL;
1373 		t_mp->sbm_peer = NULL;
1374 		t_mp->sbm_flags = 0;
1375 		t_mp->sbm_cm.sbdev_busy = 0;
1376 		dr_init_mem_unit_data(t_mp);
1377 
1378 		/* reduce target size if new PAs go past end of usable slice */
1379 		if (t_new_smallsize > 0) {
1380 			t_mp->sbm_npages = _b64top(t_new_smallsize);
1381 			PR_MEM("%s: target new size 0x%lx bytes\n",
1382 			    f, t_new_smallsize);
1383 		}
1384 	}
1385 	if (t_mp != NULL && t_mp->sbm_cm.sbdev_error == NULL) {
1386 		/*
1387 		 * now that copy/rename has completed, undo this
1388 		 * work that was done in dr_release_mem_done.
1389 		 */
1390 		DR_DEV_CLR_UNREFERENCED(&t_mp->sbm_cm);
1391 		DR_DEV_CLR_RELEASED(&t_mp->sbm_cm);
1392 		dr_device_transition(&t_mp->sbm_cm, DR_STATE_CONFIGURED);
1393 	}
1394 
1395 	/*
1396 	 * clean up (source) board's mem unit structure.
1397 	 * NOTE: sbm_mlist is retained if no error has been record (in other
1398 	 * words, when s_mp->sbm_cm.sbdev_error is NULL). This memlist is
1399 	 * referred to elsewhere as the cached memlist.  The cached memlist
1400 	 * is used to re-attach (configure back in) this memunit from the
1401 	 * unconfigured state.  The memlist is retained because it may
1402 	 * represent bad pages that were detected while the memory was
1403 	 * configured into the OS.  The OS deletes bad pages from phys_install.
1404 	 * Those deletes, if any, will be represented in the cached mlist.
1405 	 */
1406 	if (s_mp->sbm_del_mlist && s_mp->sbm_del_mlist != s_mp->sbm_mlist)
1407 		memlist_delete(s_mp->sbm_del_mlist);
1408 
1409 	if (s_mp->sbm_cm.sbdev_error && s_mp->sbm_mlist) {
1410 		memlist_delete(s_mp->sbm_mlist);
1411 		s_mp->sbm_mlist = NULL;
1412 	}
1413 
1414 	if (s_mp->sbm_dyn_segs != NULL && s_mp->sbm_cm.sbdev_error == 0) {
1415 		memlist_delete(s_mp->sbm_dyn_segs);
1416 		s_mp->sbm_dyn_segs = NULL;
1417 	}
1418 
1419 	s_mp->sbm_del_mlist = NULL;
1420 	s_mp->sbm_peer = NULL;
1421 	s_mp->sbm_flags = 0;
1422 	s_mp->sbm_cm.sbdev_busy = 0;
1423 	dr_init_mem_unit_data(s_mp);
1424 
1425 	PR_MEM("%s: cached memlist for %s:", f, s_mp->sbm_cm.sbdev_path);
1426 	PR_MEMLIST_DUMP(s_mp->sbm_mlist);
1427 
1428 	return (0);
1429 }
1430 
1431 /*
1432  * Successful return from this function will have the memory
1433  * handle in bp->b_dev[..mem-unit...].sbm_memhandle allocated
1434  * and waiting.  This routine's job is to select the memory that
1435  * actually has to be released (detached) which may not necessarily
1436  * be the same memory node that came in in devlist[],
1437  * i.e. a copy-rename is needed.
1438  */
1439 int
1440 dr_pre_release_mem(dr_handle_t *hp, dr_common_unit_t **devlist, int devnum)
1441 {
1442 	int		d;
1443 	int		err_flag = 0;
1444 	static fn_t	f = "dr_pre_release_mem";
1445 
1446 	PR_MEM("%s...\n", f);
1447 
1448 	for (d = 0; d < devnum; d++) {
1449 		dr_mem_unit_t	*mp = (dr_mem_unit_t *)devlist[d];
1450 		int		rv;
1451 		memquery_t	mq;
1452 		struct memlist	*ml;
1453 
1454 		if (mp->sbm_cm.sbdev_error) {
1455 			err_flag = 1;
1456 			continue;
1457 		} else if (!kcage_on) {
1458 			dr_dev_err(CE_WARN, &mp->sbm_cm, ESBD_KCAGE_OFF);
1459 			err_flag = 1;
1460 			continue;
1461 		}
1462 
1463 		if (mp->sbm_flags & DR_MFLAG_RESERVED) {
1464 			/*
1465 			 * Board is currently involved in a delete
1466 			 * memory operation. Can't detach this guy until
1467 			 * that operation completes.
1468 			 */
1469 			dr_dev_err(CE_WARN, &mp->sbm_cm, ESBD_INVAL);
1470 			err_flag = 1;
1471 			break;
1472 		}
1473 
1474 		/*
1475 		 * Check whether the detaching memory requires a
1476 		 * copy-rename.
1477 		 */
1478 		ASSERT(mp->sbm_npages != 0);
1479 		rv = kphysm_del_span_query(mp->sbm_basepfn, mp->sbm_npages,
1480 		    &mq);
1481 		if (rv != KPHYSM_OK) {
1482 			DR_DEV_INTERNAL_ERROR(&mp->sbm_cm);
1483 			err_flag = 1;
1484 			break;
1485 		}
1486 
1487 		if (mq.nonrelocatable != 0) {
1488 			if (!(dr_cmd_flags(hp) &
1489 			    (SBD_FLAG_FORCE | SBD_FLAG_QUIESCE_OKAY))) {
1490 				/* caller wasn't prompted for a suspend */
1491 				dr_dev_err(CE_WARN, &mp->sbm_cm,
1492 				    ESBD_QUIESCE_REQD);
1493 				err_flag = 1;
1494 				break;
1495 			}
1496 		}
1497 
1498 		/* flags should be clean at this time */
1499 		ASSERT(mp->sbm_flags == 0);
1500 
1501 		ASSERT(mp->sbm_mlist == NULL);		/* should be null */
1502 		ASSERT(mp->sbm_del_mlist == NULL);	/* should be null */
1503 		if (mp->sbm_mlist != NULL) {
1504 			memlist_delete(mp->sbm_mlist);
1505 			mp->sbm_mlist = NULL;
1506 		}
1507 
1508 		ml = dr_get_memlist(mp);
1509 		if (ml == NULL) {
1510 			err_flag = 1;
1511 			PR_MEM("%s: no memlist found for %s\n",
1512 			    f, mp->sbm_cm.sbdev_path);
1513 			continue;
1514 		}
1515 
1516 		/* allocate a kphysm handle */
1517 		rv = kphysm_del_gethandle(&mp->sbm_memhandle);
1518 		if (rv != KPHYSM_OK) {
1519 			memlist_delete(ml);
1520 
1521 			DR_DEV_INTERNAL_ERROR(&mp->sbm_cm);
1522 			err_flag = 1;
1523 			break;
1524 		}
1525 		mp->sbm_flags |= DR_MFLAG_RELOWNER;
1526 
1527 		if ((mq.nonrelocatable != 0) ||
1528 		    dr_reserve_mem_spans(&mp->sbm_memhandle, ml)) {
1529 			/*
1530 			 * Either the detaching memory node contains
1531 			 * non-reloc memory or we failed to reserve the
1532 			 * detaching memory node (which did _not_ have
1533 			 * any non-reloc memory, i.e. some non-reloc mem
1534 			 * got onboard).
1535 			 */
1536 
1537 			if (dr_select_mem_target(hp, mp, ml)) {
1538 				int rv;
1539 
1540 				/*
1541 				 * We had no luck locating a target
1542 				 * memory node to be the recipient of
1543 				 * the non-reloc memory on the node
1544 				 * we're trying to detach.
1545 				 * Clean up be disposing the mem handle
1546 				 * and the mem list.
1547 				 */
1548 				rv = kphysm_del_release(mp->sbm_memhandle);
1549 				if (rv != KPHYSM_OK) {
1550 					/*
1551 					 * can do nothing but complain
1552 					 * and hope helpful for debug
1553 					 */
1554 					cmn_err(CE_WARN, "%s: unexpected"
1555 					    " kphysm_del_release return"
1556 					    " value %d",
1557 					    f, rv);
1558 				}
1559 				mp->sbm_flags &= ~DR_MFLAG_RELOWNER;
1560 
1561 				memlist_delete(ml);
1562 
1563 				/* make sure sbm_flags is clean */
1564 				ASSERT(mp->sbm_flags == 0);
1565 
1566 				dr_dev_err(CE_WARN, &mp->sbm_cm,
1567 				    ESBD_NO_TARGET);
1568 
1569 				err_flag = 1;
1570 				break;
1571 			}
1572 
1573 			/*
1574 			 * ml is not memlist_delete'd here because
1575 			 * it has been assigned to mp->sbm_mlist
1576 			 * by dr_select_mem_target.
1577 			 */
1578 		} else {
1579 			/* no target needed to detach this board */
1580 			mp->sbm_flags |= DR_MFLAG_RESERVED;
1581 			mp->sbm_peer = NULL;
1582 			mp->sbm_del_mlist = ml;
1583 			mp->sbm_mlist = ml;
1584 			mp->sbm_cm.sbdev_busy = 1;
1585 		}
1586 #ifdef DEBUG
1587 		ASSERT(mp->sbm_mlist != NULL);
1588 
1589 		if (mp->sbm_flags & DR_MFLAG_SOURCE) {
1590 			PR_MEM("%s: release of %s requires copy/rename;"
1591 			    " selected target board %s\n",
1592 			    f,
1593 			    mp->sbm_cm.sbdev_path,
1594 			    mp->sbm_peer->sbm_cm.sbdev_path);
1595 		} else {
1596 			PR_MEM("%s: copy/rename not required to release %s\n",
1597 			    f, mp->sbm_cm.sbdev_path);
1598 		}
1599 
1600 		ASSERT(mp->sbm_flags & DR_MFLAG_RELOWNER);
1601 		ASSERT(mp->sbm_flags & DR_MFLAG_RESERVED);
1602 #endif
1603 	}
1604 
1605 	return (err_flag ? -1 : 0);
1606 }
1607 
1608 void
1609 dr_release_mem_done(dr_common_unit_t *cp)
1610 {
1611 	dr_mem_unit_t	*s_mp = (dr_mem_unit_t *)cp;
1612 	dr_mem_unit_t *t_mp, *mp;
1613 	int		rv;
1614 	static fn_t	f = "dr_release_mem_done";
1615 
1616 	/*
1617 	 * This unit will be flagged with DR_MFLAG_SOURCE, if it
1618 	 * has a target unit.
1619 	 */
1620 	if (s_mp->sbm_flags & DR_MFLAG_SOURCE) {
1621 		t_mp = s_mp->sbm_peer;
1622 		ASSERT(t_mp != NULL);
1623 		ASSERT(t_mp->sbm_peer == s_mp);
1624 		ASSERT(t_mp->sbm_flags & DR_MFLAG_TARGET);
1625 		ASSERT(t_mp->sbm_flags & DR_MFLAG_RESERVED);
1626 	} else {
1627 		/* this is no target unit */
1628 		t_mp = NULL;
1629 	}
1630 
1631 	/* free delete handle */
1632 	ASSERT(s_mp->sbm_flags & DR_MFLAG_RELOWNER);
1633 	ASSERT(s_mp->sbm_flags & DR_MFLAG_RESERVED);
1634 	rv = kphysm_del_release(s_mp->sbm_memhandle);
1635 	if (rv != KPHYSM_OK) {
1636 		/*
1637 		 * can do nothing but complain
1638 		 * and hope helpful for debug
1639 		 */
1640 		cmn_err(CE_WARN, "%s: unexpected kphysm_del_release"
1641 		    " return value %d", f, rv);
1642 	}
1643 	s_mp->sbm_flags &= ~DR_MFLAG_RELOWNER;
1644 
1645 	/*
1646 	 * If an error was encountered during release, clean up
1647 	 * the source (and target, if present) unit data.
1648 	 */
1649 /* XXX Can we know that sbdev_error was encountered during release? */
1650 	if (s_mp->sbm_cm.sbdev_error != NULL) {
1651 		PR_MEM("%s: %s: error %d noted\n",
1652 		    f,
1653 		    s_mp->sbm_cm.sbdev_path,
1654 		    s_mp->sbm_cm.sbdev_error->e_code);
1655 
1656 		if (t_mp != NULL) {
1657 			ASSERT(t_mp->sbm_del_mlist == t_mp->sbm_mlist);
1658 			t_mp->sbm_del_mlist = NULL;
1659 
1660 			if (t_mp->sbm_mlist != NULL) {
1661 				memlist_delete(t_mp->sbm_mlist);
1662 				t_mp->sbm_mlist = NULL;
1663 			}
1664 
1665 			t_mp->sbm_peer = NULL;
1666 			t_mp->sbm_flags = 0;
1667 			t_mp->sbm_cm.sbdev_busy = 0;
1668 		}
1669 
1670 		if (s_mp->sbm_del_mlist != s_mp->sbm_mlist)
1671 			memlist_delete(s_mp->sbm_del_mlist);
1672 		s_mp->sbm_del_mlist = NULL;
1673 
1674 		if (s_mp->sbm_mlist != NULL) {
1675 			memlist_delete(s_mp->sbm_mlist);
1676 			s_mp->sbm_mlist = NULL;
1677 		}
1678 
1679 		s_mp->sbm_peer = NULL;
1680 		s_mp->sbm_flags = 0;
1681 		s_mp->sbm_cm.sbdev_busy = 0;
1682 
1683 		/* bail out */
1684 		return;
1685 	}
1686 
1687 	DR_DEV_SET_RELEASED(&s_mp->sbm_cm);
1688 	dr_device_transition(&s_mp->sbm_cm, DR_STATE_RELEASE);
1689 
1690 	if (t_mp != NULL) {
1691 		/*
1692 		 * the kphysm delete operation that drained the source
1693 		 * board also drained this target board.  Since the source
1694 		 * board drain is now known to have succeeded, we know this
1695 		 * target board is drained too.
1696 		 *
1697 		 * because DR_DEV_SET_RELEASED and dr_device_transition
1698 		 * is done here, the dr_release_dev_done should not
1699 		 * fail.
1700 		 */
1701 		DR_DEV_SET_RELEASED(&t_mp->sbm_cm);
1702 		dr_device_transition(&t_mp->sbm_cm, DR_STATE_RELEASE);
1703 
1704 		/*
1705 		 * NOTE: do not transition target's board state,
1706 		 * even if the mem-unit was the last configure
1707 		 * unit of the board.  When copy/rename completes
1708 		 * this mem-unit will transitioned back to
1709 		 * the configured state.  In the meantime, the
1710 		 * board's must remain as is.
1711 		 */
1712 	}
1713 
1714 	/* if board(s) had deleted memory, verify it is gone */
1715 	rv = 0;
1716 	memlist_read_lock();
1717 	if (s_mp->sbm_del_mlist != NULL) {
1718 		mp = s_mp;
1719 		rv = memlist_intersect(phys_install, mp->sbm_del_mlist);
1720 	}
1721 	if (rv == 0 && t_mp && t_mp->sbm_del_mlist != NULL) {
1722 		mp = t_mp;
1723 		rv = memlist_intersect(phys_install, mp->sbm_del_mlist);
1724 	}
1725 	memlist_read_unlock();
1726 	if (rv) {
1727 		cmn_err(CE_WARN, "%s: %smem-unit (%d.%d): "
1728 		    "deleted memory still found in phys_install",
1729 		    f,
1730 		    (mp == t_mp ? "target " : ""),
1731 		    mp->sbm_cm.sbdev_bp->b_num,
1732 		    mp->sbm_cm.sbdev_unum);
1733 
1734 		DR_DEV_INTERNAL_ERROR(&s_mp->sbm_cm);
1735 		return;
1736 	}
1737 
1738 	s_mp->sbm_flags |= DR_MFLAG_RELDONE;
1739 	if (t_mp != NULL)
1740 		t_mp->sbm_flags |= DR_MFLAG_RELDONE;
1741 
1742 	/* this should not fail */
1743 	if (dr_release_dev_done(&s_mp->sbm_cm) != 0) {
1744 		/* catch this in debug kernels */
1745 		ASSERT(0);
1746 		return;
1747 	}
1748 
1749 	PR_MEM("%s: marking %s release DONE\n",
1750 	    f, s_mp->sbm_cm.sbdev_path);
1751 
1752 	s_mp->sbm_cm.sbdev_ostate = SBD_STAT_UNCONFIGURED;
1753 
1754 	if (t_mp != NULL) {
1755 		/* should not fail */
1756 		rv = dr_release_dev_done(&t_mp->sbm_cm);
1757 		if (rv != 0) {
1758 			/* catch this in debug kernels */
1759 			ASSERT(0);
1760 			return;
1761 		}
1762 
1763 		PR_MEM("%s: marking %s release DONE\n",
1764 		    f, t_mp->sbm_cm.sbdev_path);
1765 
1766 		t_mp->sbm_cm.sbdev_ostate = SBD_STAT_UNCONFIGURED;
1767 	}
1768 }
1769 
1770 /*ARGSUSED*/
1771 int
1772 dr_disconnect_mem(dr_mem_unit_t *mp)
1773 {
1774 	static fn_t	f = "dr_disconnect_mem";
1775 	update_membounds_t umb;
1776 
1777 #ifdef DEBUG
1778 	int state = mp->sbm_cm.sbdev_state;
1779 	ASSERT(state == DR_STATE_CONNECTED || state == DR_STATE_UNCONFIGURED);
1780 #endif
1781 
1782 	PR_MEM("%s...\n", f);
1783 
1784 	if (mp->sbm_del_mlist && mp->sbm_del_mlist != mp->sbm_mlist)
1785 		memlist_delete(mp->sbm_del_mlist);
1786 	mp->sbm_del_mlist = NULL;
1787 
1788 	if (mp->sbm_mlist) {
1789 		memlist_delete(mp->sbm_mlist);
1790 		mp->sbm_mlist = NULL;
1791 	}
1792 
1793 	/*
1794 	 * Remove memory from lgroup
1795 	 * For now, only board info is required.
1796 	 */
1797 	umb.u_board = mp->sbm_cm.sbdev_bp->b_num;
1798 	umb.u_base = (uint64_t)-1;
1799 	umb.u_len = (uint64_t)-1;
1800 
1801 	lgrp_plat_config(LGRP_CONFIG_MEM_DEL, (uintptr_t)&umb);
1802 
1803 	return (0);
1804 }
1805 
1806 int
1807 dr_cancel_mem(dr_mem_unit_t *s_mp)
1808 {
1809 	dr_mem_unit_t	*t_mp;
1810 	dr_state_t	state;
1811 	static fn_t	f = "dr_cancel_mem";
1812 
1813 	state = s_mp->sbm_cm.sbdev_state;
1814 
1815 	if (s_mp->sbm_flags & DR_MFLAG_TARGET) {
1816 		/* must cancel source board, not target board */
1817 		/* TODO: set error */
1818 		return (-1);
1819 	} else if (s_mp->sbm_flags & DR_MFLAG_SOURCE) {
1820 		t_mp = s_mp->sbm_peer;
1821 		ASSERT(t_mp != NULL);
1822 		ASSERT(t_mp->sbm_peer == s_mp);
1823 
1824 		/* must always match the source board's state */
1825 /* TODO: is this assertion correct? */
1826 		ASSERT(t_mp->sbm_cm.sbdev_state == state);
1827 	} else {
1828 		/* this is no target unit */
1829 		t_mp = NULL;
1830 	}
1831 
1832 	switch (state) {
1833 	case DR_STATE_UNREFERENCED:	/* state set by dr_release_dev_done */
1834 		ASSERT((s_mp->sbm_flags & DR_MFLAG_RELOWNER) == 0);
1835 
1836 		if (t_mp != NULL && t_mp->sbm_del_mlist != NULL) {
1837 			PR_MEM("%s: undoing target %s memory delete\n",
1838 			    f, t_mp->sbm_cm.sbdev_path);
1839 			dr_add_memory_spans(t_mp, t_mp->sbm_del_mlist);
1840 
1841 			DR_DEV_CLR_UNREFERENCED(&t_mp->sbm_cm);
1842 		}
1843 
1844 		if (s_mp->sbm_del_mlist != NULL) {
1845 			PR_MEM("%s: undoing %s memory delete\n",
1846 			    f, s_mp->sbm_cm.sbdev_path);
1847 
1848 			dr_add_memory_spans(s_mp, s_mp->sbm_del_mlist);
1849 		}
1850 
1851 		/*FALLTHROUGH*/
1852 
1853 /* TODO: should no longer be possible to see the release state here */
1854 	case DR_STATE_RELEASE:	/* state set by dr_release_mem_done */
1855 
1856 		ASSERT((s_mp->sbm_flags & DR_MFLAG_RELOWNER) == 0);
1857 
1858 		if (t_mp != NULL) {
1859 			ASSERT(t_mp->sbm_del_mlist == t_mp->sbm_mlist);
1860 			t_mp->sbm_del_mlist = NULL;
1861 
1862 			if (t_mp->sbm_mlist != NULL) {
1863 				memlist_delete(t_mp->sbm_mlist);
1864 				t_mp->sbm_mlist = NULL;
1865 			}
1866 
1867 			t_mp->sbm_peer = NULL;
1868 			t_mp->sbm_flags = 0;
1869 			t_mp->sbm_cm.sbdev_busy = 0;
1870 			dr_init_mem_unit_data(t_mp);
1871 
1872 			DR_DEV_CLR_RELEASED(&t_mp->sbm_cm);
1873 
1874 			dr_device_transition(&t_mp->sbm_cm,
1875 			    DR_STATE_CONFIGURED);
1876 		}
1877 
1878 		if (s_mp->sbm_del_mlist != s_mp->sbm_mlist)
1879 			memlist_delete(s_mp->sbm_del_mlist);
1880 		s_mp->sbm_del_mlist = NULL;
1881 
1882 		if (s_mp->sbm_mlist != NULL) {
1883 			memlist_delete(s_mp->sbm_mlist);
1884 			s_mp->sbm_mlist = NULL;
1885 		}
1886 
1887 		s_mp->sbm_peer = NULL;
1888 		s_mp->sbm_flags = 0;
1889 		s_mp->sbm_cm.sbdev_busy = 0;
1890 		dr_init_mem_unit_data(s_mp);
1891 
1892 		return (0);
1893 
1894 	default:
1895 		PR_MEM("%s: WARNING unexpected state (%d) for %s\n",
1896 		    f, (int)state, s_mp->sbm_cm.sbdev_path);
1897 
1898 		return (-1);
1899 	}
1900 	/*NOTREACHED*/
1901 }
1902 
1903 void
1904 dr_init_mem_unit(dr_mem_unit_t *mp)
1905 {
1906 	dr_state_t	new_state;
1907 
1908 
1909 	if (DR_DEV_IS_ATTACHED(&mp->sbm_cm)) {
1910 		new_state = DR_STATE_CONFIGURED;
1911 		mp->sbm_cm.sbdev_cond = SBD_COND_OK;
1912 	} else if (DR_DEV_IS_PRESENT(&mp->sbm_cm)) {
1913 		new_state = DR_STATE_CONNECTED;
1914 		mp->sbm_cm.sbdev_cond = SBD_COND_OK;
1915 	} else if (mp->sbm_cm.sbdev_id != (drmachid_t)0) {
1916 		new_state = DR_STATE_OCCUPIED;
1917 	} else {
1918 		new_state = DR_STATE_EMPTY;
1919 	}
1920 
1921 	if (DR_DEV_IS_PRESENT(&mp->sbm_cm))
1922 		dr_init_mem_unit_data(mp);
1923 
1924 	/* delay transition until fully initialized */
1925 	dr_device_transition(&mp->sbm_cm, new_state);
1926 }
1927 
1928 static void
1929 dr_init_mem_unit_data(dr_mem_unit_t *mp)
1930 {
1931 	drmachid_t	id = mp->sbm_cm.sbdev_id;
1932 	uint64_t	bytes;
1933 	sbd_error_t	*err;
1934 	static fn_t	f = "dr_init_mem_unit_data";
1935 	update_membounds_t umb;
1936 
1937 	PR_MEM("%s...\n", f);
1938 
1939 	/* a little sanity checking */
1940 	ASSERT(mp->sbm_peer == NULL);
1941 	ASSERT(mp->sbm_flags == 0);
1942 
1943 	/* get basepfn of mem unit */
1944 	err = drmach_mem_get_base_physaddr(id, &bytes);
1945 	if (err) {
1946 		DRERR_SET_C(&mp->sbm_cm.sbdev_error, &err);
1947 		mp->sbm_basepfn = (pfn_t)-1;
1948 	} else
1949 		mp->sbm_basepfn = _b64top(bytes);
1950 
1951 	/* attempt to get number of pages from PDA */
1952 	err = drmach_mem_get_size(id, &bytes);
1953 	if (err) {
1954 		DRERR_SET_C(&mp->sbm_cm.sbdev_error, &err);
1955 		mp->sbm_npages = 0;
1956 	} else
1957 		mp->sbm_npages = _b64top(bytes);
1958 
1959 	/* if didn't work, calculate using memlist */
1960 	if (mp->sbm_npages == 0) {
1961 		struct memlist	*ml, *mlist;
1962 		/*
1963 		 * Either we couldn't open the PDA or our
1964 		 * PDA has garbage in it.  We must have the
1965 		 * page count consistent and whatever the
1966 		 * OS states has precedence over the PDA
1967 		 * so let's check the kernel.
1968 		 */
1969 /* TODO: curious comment. it suggests pda query should happen if this fails */
1970 		PR_MEM("%s: PDA query failed for npages."
1971 		    " Checking memlist for %s\n",
1972 		    f, mp->sbm_cm.sbdev_path);
1973 
1974 		mlist = dr_get_memlist(mp);
1975 		for (ml = mlist; ml; ml = ml->next)
1976 			mp->sbm_npages += btop(ml->size);
1977 		memlist_delete(mlist);
1978 	}
1979 
1980 	err = drmach_mem_get_alignment(id, &bytes);
1981 	if (err) {
1982 		DRERR_SET_C(&mp->sbm_cm.sbdev_error, &err);
1983 		mp->sbm_alignment_mask = 0;
1984 	} else
1985 		mp->sbm_alignment_mask = _b64top(bytes);
1986 
1987 	err = drmach_mem_get_slice_size(id, &bytes);
1988 	if (err) {
1989 		DRERR_SET_C(&mp->sbm_cm.sbdev_error, &err);
1990 		mp->sbm_slice_size = 0; /* paranoia */
1991 	} else
1992 		mp->sbm_slice_size = bytes;
1993 
1994 	/*
1995 	 * Add memory to lgroup
1996 	 */
1997 	umb.u_board = mp->sbm_cm.sbdev_bp->b_num;
1998 	umb.u_base = (uint64_t)mp->sbm_basepfn << MMU_PAGESHIFT;
1999 	umb.u_len = (uint64_t)mp->sbm_npages << MMU_PAGESHIFT;
2000 
2001 	lgrp_plat_config(LGRP_CONFIG_MEM_ADD, (uintptr_t)&umb);
2002 
2003 	PR_MEM("%s: %s (basepfn = 0x%lx, npgs = %ld)\n",
2004 	    f, mp->sbm_cm.sbdev_path, mp->sbm_basepfn, mp->sbm_npages);
2005 }
2006 
2007 static int
2008 dr_reserve_mem_spans(memhandle_t *mhp, struct memlist *ml)
2009 {
2010 	int		err;
2011 	pfn_t		base;
2012 	pgcnt_t		npgs;
2013 	struct memlist	*mc;
2014 	static fn_t	f = "dr_reserve_mem_spans";
2015 
2016 	PR_MEM("%s...\n", f);
2017 
2018 	/*
2019 	 * Walk the supplied memlist scheduling each span for removal
2020 	 * with kphysm_del_span.  It is possible that a span may intersect
2021 	 * an area occupied by the cage.
2022 	 */
2023 	for (mc = ml; mc != NULL; mc = mc->next) {
2024 		base = _b64top(mc->address);
2025 		npgs = _b64top(mc->size);
2026 
2027 		err = kphysm_del_span(*mhp, base, npgs);
2028 		if (err != KPHYSM_OK) {
2029 			cmn_err(CE_WARN, "%s memory reserve failed."
2030 			    " unexpected kphysm_del_span return value %d;"
2031 			    " basepfn=0x%lx npages=%ld",
2032 			    f, err, base, npgs);
2033 
2034 			return (-1);
2035 		}
2036 	}
2037 
2038 	return (0);
2039 }
2040 
2041 /* debug counters */
2042 int dr_smt_realigned;
2043 int dr_smt_preference[4];
2044 
2045 #ifdef DEBUG
2046 uint_t dr_ignore_board; /* if bit[bnum-1] set, board won't be candidate */
2047 #endif
2048 
2049 /*
2050  * Find and reserve a copy/rename target board suitable for the
2051  * given source board.
2052  * All boards in the system are examined and categorized in relation to
2053  * their memory size versus the source board's memory size.  Order of
2054  * preference is:
2055  *	1st: board has same memory size
2056  * 	2nd: board has larger memory size
2057  *	3rd: board has smaller memory size
2058  *	4th: board has smaller memory size, available memory will be reduced.
2059  * Boards in category 3 and 4 will have their MC's reprogrammed to locate the
2060  * span to which the MC responds to address span that appropriately covers
2061  * the nonrelocatable span of the source board.
2062  */
2063 static int
2064 dr_select_mem_target(dr_handle_t *hp,
2065 	dr_mem_unit_t *s_mp, struct memlist *s_ml)
2066 {
2067 	pgcnt_t		sz = _b64top(s_mp->sbm_slice_size);
2068 	pgcnt_t		sm = sz - 1; /* mem_slice_mask */
2069 	pfn_t		s_phi, t_phi;
2070 
2071 	int		n_sets = 4; /* same, larger, smaller, clipped */
2072 	int		preference; /* lower value is higher preference */
2073 	int		n_units_per_set;
2074 	int		idx;
2075 	dr_mem_unit_t	**sets;
2076 
2077 	int		t_bd;
2078 	int		t_unit;
2079 	int		rv;
2080 	int		allow_src_memrange_modify;
2081 	int		allow_targ_memrange_modify;
2082 	drmachid_t	t_id;
2083 	dr_board_t	*s_bp, *t_bp;
2084 	dr_mem_unit_t	*t_mp, *c_mp;
2085 	struct memlist	*d_ml, *t_ml, *x_ml;
2086 	memquery_t	s_mq = {0};
2087 	static fn_t	f = "dr_select_mem_target";
2088 
2089 	PR_MEM("%s...\n", f);
2090 
2091 	ASSERT(s_ml != NULL);
2092 
2093 	n_units_per_set = MAX_BOARDS * MAX_MEM_UNITS_PER_BOARD;
2094 	sets = GETSTRUCT(dr_mem_unit_t *, n_units_per_set * n_sets);
2095 
2096 	s_bp = hp->h_bd;
2097 	/* calculate the offset into the slice of the last source board pfn */
2098 	ASSERT(s_mp->sbm_npages != 0);
2099 	s_phi = (s_mp->sbm_basepfn + s_mp->sbm_npages - 1) & sm;
2100 
2101 	allow_src_memrange_modify = drmach_allow_memrange_modify(s_bp->b_id);
2102 
2103 	/*
2104 	 * Make one pass through all memory units on all boards
2105 	 * and categorize them with respect to the source board.
2106 	 */
2107 	for (t_bd = 0; t_bd < MAX_BOARDS; t_bd++) {
2108 		/*
2109 		 * The board structs are a contiguous array
2110 		 * so we take advantage of that to find the
2111 		 * correct board struct pointer for a given
2112 		 * board number.
2113 		 */
2114 		t_bp = dr_lookup_board(t_bd);
2115 
2116 		/* source board can not be its own target */
2117 		if (s_bp->b_num == t_bp->b_num)
2118 			continue;
2119 
2120 		for (t_unit = 0; t_unit < MAX_MEM_UNITS_PER_BOARD; t_unit++) {
2121 
2122 			t_mp = dr_get_mem_unit(t_bp, t_unit);
2123 
2124 			/* this memory node must be attached */
2125 			if (!DR_DEV_IS_ATTACHED(&t_mp->sbm_cm))
2126 				continue;
2127 
2128 			/* source unit can not be its own target */
2129 			if (s_mp == t_mp) {
2130 				/* catch this is debug kernels */
2131 				ASSERT(0);
2132 				continue;
2133 			}
2134 
2135 			/*
2136 			 * this memory node must not already be reserved
2137 			 * by some other memory delete operation.
2138 			 */
2139 			if (t_mp->sbm_flags & DR_MFLAG_RESERVED)
2140 				continue;
2141 
2142 			/*
2143 			 * categorize the memory node
2144 			 * If this is a smaller memory node, create a
2145 			 * temporary, edited copy of the source board's
2146 			 * memlist containing only the span of the non-
2147 			 * relocatable pages.
2148 			 */
2149 			t_phi = (t_mp->sbm_basepfn + t_mp->sbm_npages - 1) & sm;
2150 			t_id = t_mp->sbm_cm.sbdev_bp->b_id;
2151 			allow_targ_memrange_modify =
2152 			    drmach_allow_memrange_modify(t_id);
2153 			if (t_mp->sbm_npages == s_mp->sbm_npages &&
2154 			    t_phi == s_phi) {
2155 				preference = 0;
2156 				t_mp->sbm_slice_offset = 0;
2157 			} else if (t_mp->sbm_npages > s_mp->sbm_npages &&
2158 			    t_phi > s_phi) {
2159 				/*
2160 				 * Selecting this target will require modifying
2161 				 * the source and/or target physical address
2162 				 * ranges.  Skip if not supported by platform.
2163 				 */
2164 				if (!allow_src_memrange_modify ||
2165 				    !allow_targ_memrange_modify) {
2166 					PR_MEM("%s: skip target %s, memory "
2167 					    "range relocation not supported "
2168 					    "by platform\n", f,
2169 					    t_mp->sbm_cm.sbdev_path);
2170 					continue;
2171 				}
2172 				preference = 1;
2173 				t_mp->sbm_slice_offset = 0;
2174 			} else {
2175 				pfn_t		pfn = 0;
2176 
2177 				/*
2178 				 * Selecting this target will require modifying
2179 				 * the source and/or target physical address
2180 				 * ranges.  Skip if not supported by platform.
2181 				 */
2182 				if (!allow_src_memrange_modify ||
2183 				    !allow_targ_memrange_modify) {
2184 					PR_MEM("%s: skip target %s, memory "
2185 					    "range relocation not supported "
2186 					    "by platform\n", f,
2187 					    t_mp->sbm_cm.sbdev_path);
2188 					continue;
2189 				}
2190 
2191 				/*
2192 				 * Check if its mc can be programmed to relocate
2193 				 * the active address range to match the
2194 				 * nonrelocatable span of the source board.
2195 				 */
2196 				preference = 2;
2197 
2198 				if (s_mq.phys_pages == 0) {
2199 					/*
2200 					 * find non-relocatable span on
2201 					 * source board.
2202 					 */
2203 					rv = kphysm_del_span_query(
2204 					    s_mp->sbm_basepfn,
2205 					    s_mp->sbm_npages, &s_mq);
2206 					if (rv != KPHYSM_OK) {
2207 						PR_MEM("%s: %s: unexpected"
2208 						    " kphysm_del_span_query"
2209 						    " return value %d;"
2210 						    " basepfn 0x%lx,"
2211 						    " npages %ld\n",
2212 						    f,
2213 						    s_mp->sbm_cm.sbdev_path,
2214 						    rv,
2215 						    s_mp->sbm_basepfn,
2216 						    s_mp->sbm_npages);
2217 
2218 						/* paranoia */
2219 						s_mq.phys_pages = 0;
2220 
2221 						continue;
2222 					}
2223 
2224 					/* more paranoia */
2225 					ASSERT(s_mq.phys_pages != 0);
2226 					ASSERT(s_mq.nonrelocatable != 0);
2227 
2228 					/*
2229 					 * this should not happen
2230 					 * if it does, it simply means that
2231 					 * we can not proceed with qualifying
2232 					 * this target candidate.
2233 					 */
2234 					if (s_mq.nonrelocatable == 0)
2235 						continue;
2236 
2237 					PR_MEM("%s: %s: nonrelocatable"
2238 					    " span (0x%lx..0x%lx)\n",
2239 					    f,
2240 					    s_mp->sbm_cm.sbdev_path,
2241 					    s_mq.first_nonrelocatable,
2242 					    s_mq.last_nonrelocatable);
2243 				}
2244 
2245 				/*
2246 				 * Round down the starting pfn of the
2247 				 * nonrelocatable span on the source board
2248 				 * to nearest programmable boundary possible
2249 				 * with this target candidate.
2250 				 */
2251 				pfn = s_mq.first_nonrelocatable &
2252 				    ~t_mp->sbm_alignment_mask;
2253 
2254 				/* skip candidate if memory is too small */
2255 				if (pfn + t_mp->sbm_npages <
2256 				    s_mq.last_nonrelocatable)
2257 					continue;
2258 
2259 				/*
2260 				 * reprogramming an mc to relocate its
2261 				 * active address range means the beginning
2262 				 * address to which the DIMMS respond will
2263 				 * be somewhere above the slice boundary
2264 				 * address.  The larger the size of memory
2265 				 * on this unit, the more likely part of it
2266 				 * will exist beyond the end of the slice.
2267 				 * The portion of the memory that does is
2268 				 * unavailable to the system until the mc
2269 				 * reprogrammed to a more favorable base
2270 				 * address.
2271 				 * An attempt is made to avoid the loss by
2272 				 * recalculating the mc base address relative
2273 				 * to the end of the slice.  This may produce
2274 				 * a more favorable result.  If not, we lower
2275 				 * the board's preference rating so that it
2276 				 * is one the last candidate boards to be
2277 				 * considered.
2278 				 */
2279 				if ((pfn + t_mp->sbm_npages) & ~sm) {
2280 					pfn_t p;
2281 
2282 					ASSERT(sz >= t_mp->sbm_npages);
2283 
2284 					/*
2285 					 * calculate an alternative starting
2286 					 * address relative to the end of the
2287 					 * slice's address space.
2288 					 */
2289 					p = pfn & ~sm;
2290 					p = p + (sz - t_mp->sbm_npages);
2291 					p = p & ~t_mp->sbm_alignment_mask;
2292 
2293 					if ((p > s_mq.first_nonrelocatable) ||
2294 					    (p + t_mp->sbm_npages <
2295 					    s_mq.last_nonrelocatable)) {
2296 
2297 						/*
2298 						 * alternative starting addr
2299 						 * won't work. Lower preference
2300 						 * rating of this board, since
2301 						 * some number of pages will
2302 						 * unavailable for use.
2303 						 */
2304 						preference = 3;
2305 					} else {
2306 						dr_smt_realigned++;
2307 						pfn = p;
2308 					}
2309 				}
2310 
2311 				/*
2312 				 * translate calculated pfn to an offset
2313 				 * relative to the slice boundary.  If the
2314 				 * candidate board is selected, this offset
2315 				 * will be used to calculate the values
2316 				 * programmed into the mc.
2317 				 */
2318 				t_mp->sbm_slice_offset = pfn & sm;
2319 				PR_MEM("%s: %s:"
2320 				    "  proposed mc offset 0x%lx\n",
2321 				    f,
2322 				    t_mp->sbm_cm.sbdev_path,
2323 				    t_mp->sbm_slice_offset);
2324 			}
2325 
2326 			dr_smt_preference[preference]++;
2327 
2328 			/* calculate index to start of preference set */
2329 			idx  = n_units_per_set * preference;
2330 			/* calculate offset to respective element */
2331 			idx += t_bd * MAX_MEM_UNITS_PER_BOARD + t_unit;
2332 
2333 			ASSERT(idx < n_units_per_set * n_sets);
2334 			sets[idx] = t_mp;
2335 		}
2336 	}
2337 
2338 	/*
2339 	 * NOTE: this would be a good place to sort each candidate
2340 	 * set in to some desired order, e.g. memory size in ascending
2341 	 * order.  Without an additional sorting step here, the order
2342 	 * within a set is ascending board number order.
2343 	 */
2344 
2345 	c_mp = NULL;
2346 	x_ml = NULL;
2347 	t_ml = NULL;
2348 	for (idx = 0; idx < n_units_per_set * n_sets; idx++) {
2349 		memquery_t mq;
2350 
2351 		/* cleanup t_ml after previous pass */
2352 		if (t_ml != NULL) {
2353 			memlist_delete(t_ml);
2354 			t_ml = NULL;
2355 		}
2356 
2357 		/* get candidate target board mem unit */
2358 		t_mp = sets[idx];
2359 		if (t_mp == NULL)
2360 			continue;
2361 
2362 		/* get target board memlist */
2363 		t_ml = dr_get_memlist(t_mp);
2364 		if (t_ml == NULL) {
2365 			cmn_err(CE_WARN, "%s: no memlist for"
2366 			    " mem-unit %d, board %d",
2367 			    f,
2368 			    t_mp->sbm_cm.sbdev_bp->b_num,
2369 			    t_mp->sbm_cm.sbdev_unum);
2370 
2371 			continue;
2372 		}
2373 
2374 		/* get appropriate source board memlist */
2375 		t_phi = (t_mp->sbm_basepfn + t_mp->sbm_npages - 1) & sm;
2376 		if (t_mp->sbm_npages < s_mp->sbm_npages || t_phi < s_phi) {
2377 			spgcnt_t excess;
2378 
2379 			/*
2380 			 * make a copy of the source board memlist
2381 			 * then edit it to remove the spans that
2382 			 * are outside the calculated span of
2383 			 * [pfn..s_mq.last_nonrelocatable].
2384 			 */
2385 			if (x_ml != NULL)
2386 				memlist_delete(x_ml);
2387 
2388 			x_ml = memlist_dup(s_ml);
2389 			if (x_ml == NULL) {
2390 				PR_MEM("%s: memlist_dup failed\n", f);
2391 				/* TODO: should abort */
2392 				continue;
2393 			}
2394 
2395 			/* trim off lower portion */
2396 			excess = t_mp->sbm_slice_offset -
2397 			    (s_mp->sbm_basepfn & sm);
2398 
2399 			if (excess > 0) {
2400 				x_ml = memlist_del_span(
2401 				    x_ml,
2402 				    _ptob64(s_mp->sbm_basepfn),
2403 				    _ptob64(excess));
2404 			}
2405 			ASSERT(x_ml);
2406 
2407 			/*
2408 			 * Since this candidate target board is smaller
2409 			 * than the source board, s_mq must have been
2410 			 * initialized in previous loop while processing
2411 			 * this or some other candidate board.
2412 			 * FIXME: this is weak.
2413 			 */
2414 			ASSERT(s_mq.phys_pages != 0);
2415 
2416 			/* trim off upper portion */
2417 			excess = (s_mp->sbm_basepfn + s_mp->sbm_npages)
2418 			    - (s_mq.last_nonrelocatable + 1);
2419 			if (excess > 0) {
2420 				pfn_t p;
2421 
2422 				p  = s_mq.last_nonrelocatable + 1;
2423 				x_ml = memlist_del_span(
2424 				    x_ml,
2425 				    _ptob64(p),
2426 				    _ptob64(excess));
2427 			}
2428 
2429 			PR_MEM("%s: %s: edited source memlist:\n",
2430 			    f, s_mp->sbm_cm.sbdev_path);
2431 			PR_MEMLIST_DUMP(x_ml);
2432 
2433 #ifdef DEBUG
2434 			/* sanity check memlist */
2435 			d_ml = x_ml;
2436 			while (d_ml->next != NULL)
2437 				d_ml = d_ml->next;
2438 
2439 			ASSERT(d_ml->address + d_ml->size ==
2440 			    _ptob64(s_mq.last_nonrelocatable + 1));
2441 #endif
2442 
2443 			/*
2444 			 * x_ml now describes only the portion of the
2445 			 * source board that will be moved during the
2446 			 * copy/rename operation.
2447 			 */
2448 			d_ml = x_ml;
2449 		} else {
2450 			/* use original memlist; all spans will be moved */
2451 			d_ml = s_ml;
2452 		}
2453 
2454 		/* verify target can support source memory spans. */
2455 		if (memlist_canfit(d_ml, t_ml) == 0) {
2456 			PR_MEM("%s: source memlist won't"
2457 			    " fit in target memlist\n", f);
2458 			PR_MEM("%s: source memlist:\n", f);
2459 			PR_MEMLIST_DUMP(d_ml);
2460 			PR_MEM("%s: target memlist:\n", f);
2461 			PR_MEMLIST_DUMP(t_ml);
2462 
2463 			continue;
2464 		}
2465 
2466 		/* NOTE: the value of d_ml is not used beyond this point */
2467 
2468 		PR_MEM("%s: checking for no-reloc in %s, "
2469 		    " basepfn=0x%lx, npages=%ld\n",
2470 		    f,
2471 		    t_mp->sbm_cm.sbdev_path,
2472 		    t_mp->sbm_basepfn,
2473 		    t_mp->sbm_npages);
2474 
2475 		rv = kphysm_del_span_query(
2476 		    t_mp->sbm_basepfn, t_mp->sbm_npages, &mq);
2477 		if (rv != KPHYSM_OK) {
2478 			PR_MEM("%s: kphysm_del_span_query:"
2479 			    " unexpected return value %d\n", f, rv);
2480 
2481 			continue;
2482 		}
2483 
2484 		if (mq.nonrelocatable != 0) {
2485 			PR_MEM("%s: candidate %s has"
2486 			    " nonrelocatable span [0x%lx..0x%lx]\n",
2487 			    f,
2488 			    t_mp->sbm_cm.sbdev_path,
2489 			    mq.first_nonrelocatable,
2490 			    mq.last_nonrelocatable);
2491 
2492 			continue;
2493 		}
2494 
2495 #ifdef DEBUG
2496 		/*
2497 		 * This is a debug tool for excluding certain boards
2498 		 * from being selected as a target board candidate.
2499 		 * dr_ignore_board is only tested by this driver.
2500 		 * It must be set with adb, obp, /etc/system or your
2501 		 * favorite debugger.
2502 		 */
2503 		if (dr_ignore_board &
2504 		    (1 << (t_mp->sbm_cm.sbdev_bp->b_num - 1))) {
2505 			PR_MEM("%s: dr_ignore_board flag set,"
2506 			    " ignoring %s as candidate\n",
2507 			    f, t_mp->sbm_cm.sbdev_path);
2508 			continue;
2509 		}
2510 #endif
2511 
2512 		/*
2513 		 * Reserve excess source board memory, if any.
2514 		 *
2515 		 * When the number of pages on the candidate target
2516 		 * board is less than the number of pages on the source,
2517 		 * then some spans (clearly) of the source board's address
2518 		 * space will not be covered by physical memory after the
2519 		 * copy/rename completes.  The following code block
2520 		 * schedules those spans to be deleted.
2521 		 */
2522 		if (t_mp->sbm_npages < s_mp->sbm_npages || t_phi < s_phi) {
2523 			pfn_t pfn;
2524 			uint64_t s_del_pa;
2525 			struct memlist *ml;
2526 
2527 			d_ml = memlist_dup(s_ml);
2528 			if (d_ml == NULL) {
2529 				PR_MEM("%s: cant dup src brd memlist\n", f);
2530 				/* TODO: should abort */
2531 				continue;
2532 			}
2533 
2534 			/* calculate base pfn relative to target board */
2535 			pfn  = s_mp->sbm_basepfn & ~sm;
2536 			pfn += t_mp->sbm_slice_offset;
2537 
2538 			/*
2539 			 * cannot split dynamically added segment
2540 			 */
2541 			s_del_pa = _ptob64(pfn + t_mp->sbm_npages);
2542 			PR_MEM("%s: proposed src delete pa=0x%lx\n", f,
2543 			    s_del_pa);
2544 			PR_MEM("%s: checking for split of dyn seg list:\n", f);
2545 			PR_MEMLIST_DUMP(s_mp->sbm_dyn_segs);
2546 			for (ml = s_mp->sbm_dyn_segs; ml; ml = ml->next) {
2547 				if (s_del_pa > ml->address &&
2548 				    s_del_pa < ml->address + ml->size) {
2549 					s_del_pa = ml->address;
2550 					break;
2551 				}
2552 			}
2553 
2554 			/* remove span that will reside on candidate board */
2555 			d_ml = memlist_del_span(d_ml, _ptob64(pfn),
2556 			    s_del_pa - _ptob64(pfn));
2557 
2558 			PR_MEM("%s: %s: reserving src brd memlist:\n",
2559 			    f, s_mp->sbm_cm.sbdev_path);
2560 			PR_MEMLIST_DUMP(d_ml);
2561 
2562 			/* reserve excess spans */
2563 			if (dr_reserve_mem_spans(&s_mp->sbm_memhandle, d_ml)
2564 			    != 0) {
2565 
2566 				/* likely more non-reloc pages appeared */
2567 				/* TODO: restart from top? */
2568 				continue;
2569 			}
2570 		} else {
2571 			/* no excess source board memory */
2572 			d_ml = NULL;
2573 		}
2574 
2575 		s_mp->sbm_flags |= DR_MFLAG_RESERVED;
2576 
2577 		/*
2578 		 * reserve all memory on target board.
2579 		 * NOTE: source board's memhandle is used.
2580 		 *
2581 		 * If this succeeds (eq 0), then target selection is
2582 		 * complete and all unwanted memory spans, both source and
2583 		 * target, have been reserved.  Loop is terminated.
2584 		 */
2585 		if (dr_reserve_mem_spans(&s_mp->sbm_memhandle, t_ml) == 0) {
2586 			PR_MEM("%s: %s: target board memory reserved\n",
2587 			    f, t_mp->sbm_cm.sbdev_path);
2588 
2589 			/* a candidate target board is now reserved */
2590 			t_mp->sbm_flags |= DR_MFLAG_RESERVED;
2591 			c_mp = t_mp;
2592 
2593 			/* *** EXITING LOOP *** */
2594 			break;
2595 		}
2596 
2597 		/* did not successfully reserve the target board. */
2598 		PR_MEM("%s: could not reserve target %s\n",
2599 		    f, t_mp->sbm_cm.sbdev_path);
2600 
2601 		/*
2602 		 * NOTE: an undo of the dr_reserve_mem_span work
2603 		 * will happen automatically when the memhandle
2604 		 * (s_mp->sbm_memhandle) is kphysm_del_release'd.
2605 		 */
2606 
2607 		s_mp->sbm_flags &= ~DR_MFLAG_RESERVED;
2608 	}
2609 
2610 	/* clean up after memlist editing logic */
2611 	if (x_ml != NULL)
2612 		memlist_delete(x_ml);
2613 
2614 	FREESTRUCT(sets, dr_mem_unit_t *, n_units_per_set * n_sets);
2615 
2616 	/*
2617 	 * c_mp will be NULL when the entire sets[] array
2618 	 * has been searched without reserving a target board.
2619 	 */
2620 	if (c_mp == NULL) {
2621 		PR_MEM("%s: %s: target selection failed.\n",
2622 		    f, s_mp->sbm_cm.sbdev_path);
2623 
2624 		if (t_ml != NULL)
2625 			memlist_delete(t_ml);
2626 
2627 		return (-1);
2628 	}
2629 
2630 	PR_MEM("%s: found target %s for source %s\n",
2631 	    f,
2632 	    c_mp->sbm_cm.sbdev_path,
2633 	    s_mp->sbm_cm.sbdev_path);
2634 
2635 	s_mp->sbm_peer = c_mp;
2636 	s_mp->sbm_flags |= DR_MFLAG_SOURCE;
2637 	s_mp->sbm_del_mlist = d_ml;	/* spans to be deleted, if any */
2638 	s_mp->sbm_mlist = s_ml;
2639 	s_mp->sbm_cm.sbdev_busy = 1;
2640 
2641 	c_mp->sbm_peer = s_mp;
2642 	c_mp->sbm_flags |= DR_MFLAG_TARGET;
2643 	c_mp->sbm_del_mlist = t_ml;	/* spans to be deleted */
2644 	c_mp->sbm_mlist = t_ml;
2645 	c_mp->sbm_cm.sbdev_busy = 1;
2646 
2647 	s_mp->sbm_flags &= ~DR_MFLAG_MEMRESIZE;
2648 	if (c_mp->sbm_npages > s_mp->sbm_npages) {
2649 		s_mp->sbm_flags |= DR_MFLAG_MEMUPSIZE;
2650 		PR_MEM("%s: upsize detected (source=%ld < target=%ld)\n",
2651 		    f, s_mp->sbm_npages, c_mp->sbm_npages);
2652 	} else if (c_mp->sbm_npages < s_mp->sbm_npages) {
2653 		s_mp->sbm_flags |= DR_MFLAG_MEMDOWNSIZE;
2654 		PR_MEM("%s: downsize detected (source=%ld > target=%ld)\n",
2655 		    f, s_mp->sbm_npages, c_mp->sbm_npages);
2656 	}
2657 
2658 	return (0);
2659 }
2660 
2661 /*
2662  * Memlist support.
2663  */
2664 
2665 /*
2666  * Determine whether the source memlist (s_mlist) will
2667  * fit into the target memlist (t_mlist) in terms of
2668  * size and holes (i.e. based on same relative base address).
2669  */
2670 static int
2671 memlist_canfit(struct memlist *s_mlist, struct memlist *t_mlist)
2672 {
2673 	int		rv = 0;
2674 	uint64_t	s_basepa, t_basepa;
2675 	struct memlist	*s_ml, *t_ml;
2676 
2677 	if ((s_mlist == NULL) || (t_mlist == NULL))
2678 		return (0);
2679 
2680 	/*
2681 	 * Base both memlists on common base address (0).
2682 	 */
2683 	s_basepa = s_mlist->address;
2684 	t_basepa = t_mlist->address;
2685 
2686 	for (s_ml = s_mlist; s_ml; s_ml = s_ml->next)
2687 		s_ml->address -= s_basepa;
2688 
2689 	for (t_ml = t_mlist; t_ml; t_ml = t_ml->next)
2690 		t_ml->address -= t_basepa;
2691 
2692 	s_ml = s_mlist;
2693 	for (t_ml = t_mlist; t_ml && s_ml; t_ml = t_ml->next) {
2694 		uint64_t	s_start, s_end;
2695 		uint64_t	t_start, t_end;
2696 
2697 		t_start = t_ml->address;
2698 		t_end = t_start + t_ml->size;
2699 
2700 		for (; s_ml; s_ml = s_ml->next) {
2701 			s_start = s_ml->address;
2702 			s_end = s_start + s_ml->size;
2703 
2704 			if ((s_start < t_start) || (s_end > t_end))
2705 				break;
2706 		}
2707 	}
2708 	/*
2709 	 * If we ran out of source memlist chunks that mean
2710 	 * we found a home for all of them.
2711 	 */
2712 	if (s_ml == NULL)
2713 		rv = 1;
2714 
2715 	/*
2716 	 * Need to add base addresses back since memlists
2717 	 * are probably in use by caller.
2718 	 */
2719 	for (s_ml = s_mlist; s_ml; s_ml = s_ml->next)
2720 		s_ml->address += s_basepa;
2721 
2722 	for (t_ml = t_mlist; t_ml; t_ml = t_ml->next)
2723 		t_ml->address += t_basepa;
2724 
2725 	return (rv);
2726 }
2727