xref: /onnv-gate/usr/src/uts/sun4u/serengeti/io/sbdp_mem.c (revision 11474:857f9db4ef05)
11708Sstevel /*
21708Sstevel  * CDDL HEADER START
31708Sstevel  *
41708Sstevel  * The contents of this file are subject to the terms of the
51708Sstevel  * Common Development and Distribution License (the "License").
61708Sstevel  * You may not use this file except in compliance with the License.
71708Sstevel  *
81708Sstevel  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
91708Sstevel  * or http://www.opensolaris.org/os/licensing.
101708Sstevel  * See the License for the specific language governing permissions
111708Sstevel  * and limitations under the License.
121708Sstevel  *
131708Sstevel  * When distributing Covered Code, include this CDDL HEADER in each
141708Sstevel  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
151708Sstevel  * If applicable, add the following below this CDDL HEADER, with the
161708Sstevel  * fields enclosed by brackets "[]" replaced with your own identifying
171708Sstevel  * information: Portions Copyright [yyyy] [name of copyright owner]
181708Sstevel  *
191708Sstevel  * CDDL HEADER END
201708Sstevel  */
211708Sstevel 
221708Sstevel /*
23*11474SJonathan.Adams@Sun.COM  * Copyright 2010 Sun Microsystems, Inc.  All rights reserved.
241708Sstevel  * Use is subject to license terms.
251708Sstevel  */
261708Sstevel 
271708Sstevel /*
281708Sstevel  * memory management for serengeti dr memory
291708Sstevel  */
301708Sstevel 
311708Sstevel #include <sys/obpdefs.h>
321708Sstevel #include <sys/types.h>
331708Sstevel #include <sys/conf.h>
341708Sstevel #include <sys/ddi.h>
351708Sstevel #include <sys/cpuvar.h>
361708Sstevel #include <sys/memlist_impl.h>
371708Sstevel #include <sys/machsystm.h>
381708Sstevel #include <sys/promif.h>
391708Sstevel #include <sys/mem_cage.h>
401708Sstevel #include <sys/kmem.h>
411708Sstevel #include <sys/note.h>
421708Sstevel #include <sys/lgrp.h>
431708Sstevel 
441708Sstevel #include <sys/sbd_ioctl.h>
451708Sstevel #include <sys/sbd.h>
461708Sstevel #include <sys/sbdp_priv.h>
471708Sstevel #include <sys/sbdp_mem.h>
481708Sstevel #include <sys/sun4asi.h>
491708Sstevel #include <sys/cheetahregs.h>
501708Sstevel #include <sys/cpu_module.h>
511708Sstevel #include <sys/esunddi.h>
521708Sstevel 
531708Sstevel #include <vm/page.h>
541708Sstevel 
551708Sstevel static int	sbdp_get_meminfo(pnode_t, int, uint64_t *, uint64_t *);
561708Sstevel int		mc_read_regs(pnode_t, mc_regs_t *);
571708Sstevel uint64_t	mc_get_addr(pnode_t, int, uint_t *);
581708Sstevel static pnode_t	mc_get_sibling_cpu(pnode_t nodeid);
591708Sstevel static int	mc_get_sibling_cpu_impl(pnode_t nodeid);
601708Sstevel static sbd_cond_t mc_check_sibling_cpu(pnode_t nodeid);
611708Sstevel static void	_sbdp_copy_rename_end(void);
621708Sstevel static int	sbdp_copy_rename__relocatable(sbdp_cr_handle_t *,
631708Sstevel 			struct memlist *, sbdp_rename_script_t *);
641708Sstevel static int	sbdp_prep_rename_script(sbdp_cr_handle_t *);
651708Sstevel static int	sbdp_get_lowest_addr_in_node(pnode_t, uint64_t *);
661708Sstevel 
671708Sstevel extern void bcopy32_il(uint64_t, uint64_t);
681708Sstevel extern void flush_ecache_il(uint64_t physaddr, size_t size, size_t linesize);
691708Sstevel extern uint64_t lddphys_il(uint64_t physaddr);
701708Sstevel extern uint64_t ldxasi_il(uint64_t physaddr, uint_t asi);
711708Sstevel extern void sbdp_exec_script_il(sbdp_rename_script_t *rsp);
721708Sstevel void sbdp_fill_bank_info(uint64_t, sbdp_bank_t **);
731708Sstevel int sbdp_add_nodes_banks(pnode_t node, sbdp_bank_t **banks);
741708Sstevel void sbdp_add_bank_to_seg(sbdp_bank_t *);
751708Sstevel void sbdp_remove_bank_from_seg(sbdp_bank_t *);
761708Sstevel uint64_t sbdp_determine_slice(sbdp_handle_t *);
771708Sstevel sbdp_seg_t *sbdp_get_seg(uint64_t);
781708Sstevel #ifdef DEBUG
791708Sstevel void sbdp_print_seg(sbdp_seg_t *);
801708Sstevel #endif
811708Sstevel 
821708Sstevel /*
831708Sstevel  * Head to the system segments link list
841708Sstevel  */
851708Sstevel sbdp_seg_t *sys_seg = NULL;
861708Sstevel 
871708Sstevel uint64_t
sbdp_determine_slice(sbdp_handle_t * hp)881708Sstevel sbdp_determine_slice(sbdp_handle_t *hp)
891708Sstevel {
901708Sstevel 	int size;
911708Sstevel 
921708Sstevel 	size = sbdp_get_mem_size(hp);
931708Sstevel 
941708Sstevel 	if (size <= SG_SLICE_16G_SIZE) {
951708Sstevel 		return (SG_SLICE_16G_SIZE);
961708Sstevel 	} else if (size <= SG_SLICE_32G_SIZE) {
971708Sstevel 		return (SG_SLICE_32G_SIZE);
981708Sstevel 	} else {
991708Sstevel 		return (SG_SLICE_64G_SIZE);
1001708Sstevel 	}
1011708Sstevel }
1021708Sstevel 
1031708Sstevel /* ARGSUSED */
1041708Sstevel int
sbdp_get_mem_alignment(sbdp_handle_t * hp,dev_info_t * dip,uint64_t * align)1051708Sstevel sbdp_get_mem_alignment(sbdp_handle_t *hp, dev_info_t *dip, uint64_t *align)
1061708Sstevel {
1071708Sstevel 	*align = sbdp_determine_slice(hp);
1081708Sstevel 	return (0);
1091708Sstevel }
1101708Sstevel 
1111708Sstevel 
1121708Sstevel void
sbdp_memlist_dump(struct memlist * mlist)1131708Sstevel sbdp_memlist_dump(struct memlist *mlist)
1141708Sstevel {
1151708Sstevel 	register struct memlist *ml;
1161708Sstevel 
1171708Sstevel 	if (mlist == NULL) {
1181708Sstevel 		SBDP_DBG_MEM("memlist> EMPTY\n");
1191708Sstevel 	} else {
120*11474SJonathan.Adams@Sun.COM 		for (ml = mlist; ml; ml = ml->ml_next)
1211708Sstevel 			SBDP_DBG_MEM("memlist>  0x%" PRIx64", 0x%" PRIx64"\n",
122*11474SJonathan.Adams@Sun.COM 			    ml->ml_address, ml->ml_size);
1231708Sstevel 	}
1241708Sstevel }
1251708Sstevel 
1261708Sstevel struct mem_arg {
1271708Sstevel 	int	board;
1281708Sstevel 	int	ndips;
1291708Sstevel 	dev_info_t **list;
1301708Sstevel };
1311708Sstevel 
1321708Sstevel /*
1331708Sstevel  * Returns mem dip held
1341708Sstevel  */
1351708Sstevel static int
sbdp_get_mem_dip(pnode_t node,void * arg,uint_t flags)1361708Sstevel sbdp_get_mem_dip(pnode_t node, void *arg, uint_t flags)
1371708Sstevel {
1381708Sstevel 	_NOTE(ARGUNUSED(flags))
1391708Sstevel 
1401708Sstevel 	dev_info_t *dip;
1411708Sstevel 	pnode_t nodeid;
1421708Sstevel 	mem_op_t mem = {0};
1431708Sstevel 	struct mem_arg *ap = arg;
1441708Sstevel 
1451708Sstevel 	if (node == OBP_BADNODE || node == OBP_NONODE)
1461708Sstevel 		return (DDI_FAILURE);
1471708Sstevel 
1481708Sstevel 	mem.nodes = &nodeid;
1491708Sstevel 	mem.board = ap->board;
1501708Sstevel 	mem.nmem = 0;
1511708Sstevel 
1521708Sstevel 	(void) sbdp_is_mem(node, &mem);
1531708Sstevel 
1541708Sstevel 	ASSERT(mem.nmem == 0 || mem.nmem == 1);
1551708Sstevel 
1561708Sstevel 	if (mem.nmem == 0 || nodeid != node)
1571708Sstevel 		return (DDI_FAILURE);
1581708Sstevel 
1591708Sstevel 	dip = e_ddi_nodeid_to_dip(nodeid);
1601708Sstevel 	if (dip) {
1611708Sstevel 		ASSERT(ap->ndips < SBDP_MAX_MEM_NODES_PER_BOARD);
1621708Sstevel 		ap->list[ap->ndips++] = dip;
1631708Sstevel 	}
1641708Sstevel 	return (DDI_SUCCESS);
1651708Sstevel }
1661708Sstevel 
1671708Sstevel struct memlist *
sbdp_get_memlist(sbdp_handle_t * hp,dev_info_t * dip)1681708Sstevel sbdp_get_memlist(sbdp_handle_t *hp, dev_info_t *dip)
1691708Sstevel {
1701708Sstevel 	_NOTE(ARGUNUSED(dip))
1711708Sstevel 
1721708Sstevel 	int i, j, skip = 0;
1731708Sstevel 	dev_info_t	*list[SBDP_MAX_MEM_NODES_PER_BOARD];
1741708Sstevel 	struct mem_arg	arg = {0};
1751708Sstevel 	uint64_t	base_pa, size;
1761708Sstevel 	struct memlist	*mlist = NULL;
1771708Sstevel 
1781708Sstevel 	list[0] = NULL;
1791708Sstevel 	arg.board = hp->h_board;
1801708Sstevel 	arg.list = list;
1811708Sstevel 
1821708Sstevel 	sbdp_walk_prom_tree(prom_rootnode(), sbdp_get_mem_dip, &arg);
1831708Sstevel 
1841708Sstevel 	for (i = 0; i < arg.ndips; i++) {
1851708Sstevel 		if (list[i] == NULL)
1861708Sstevel 			continue;
1871708Sstevel 
1881708Sstevel 		size = 0;
1891708Sstevel 		for (j = 0; j < SBDP_MAX_MCS_PER_NODE; j++) {
1901708Sstevel 			if (sbdp_get_meminfo(ddi_get_nodeid(list[i]), j,
1911708Sstevel 			    &size, &base_pa)) {
1921708Sstevel 				skip++;
1931708Sstevel 				continue;
1941708Sstevel 			}
1951708Sstevel 			if (size == -1 || size == 0)
1961708Sstevel 				continue;
1971708Sstevel 
1981708Sstevel 			(void) memlist_add_span(base_pa, size, &mlist);
1991708Sstevel 		}
2001708Sstevel 
2011708Sstevel 		/*
2021708Sstevel 		 * Release hold acquired in sbdp_get_mem_dip()
2031708Sstevel 		 */
2041708Sstevel 		ddi_release_devi(list[i]);
2051708Sstevel 	}
2061708Sstevel 
2071708Sstevel 	/*
2081708Sstevel 	 * XXX - The following two lines are from existing code.
2091708Sstevel 	 * However, this appears to be incorrect - this check should be
2101708Sstevel 	 * made for each dip in list i.e within the for(i) loop.
2111708Sstevel 	 */
2121708Sstevel 	if (skip == SBDP_MAX_MCS_PER_NODE)
2131708Sstevel 		sbdp_set_err(hp->h_err, ESGT_INTERNAL, NULL);
2141708Sstevel 
2151708Sstevel 	SBDP_DBG_MEM("memlist for board %d\n", hp->h_board);
2161708Sstevel 	sbdp_memlist_dump(mlist);
2171708Sstevel 	return (mlist);
2181708Sstevel }
2191708Sstevel 
2201708Sstevel struct memlist *
sbdp_memlist_dup(struct memlist * mlist)2211708Sstevel sbdp_memlist_dup(struct memlist *mlist)
2221708Sstevel {
2231708Sstevel 	struct memlist *hl, *prev;
2241708Sstevel 
2251708Sstevel 	if (mlist == NULL)
2261708Sstevel 		return (NULL);
2271708Sstevel 
2281708Sstevel 	prev = NULL;
2291708Sstevel 	hl = NULL;
230*11474SJonathan.Adams@Sun.COM 	for (; mlist; mlist = mlist->ml_next) {
2311708Sstevel 		struct memlist *mp;
2321708Sstevel 
2331708Sstevel 		mp = memlist_get_one();
2341708Sstevel 		if (mp == NULL) {
2351708Sstevel 			if (hl != NULL)
2361708Sstevel 				memlist_free_list(hl);
2371708Sstevel 			hl = NULL;
2381708Sstevel 			break;
2391708Sstevel 		}
240*11474SJonathan.Adams@Sun.COM 		mp->ml_address = mlist->ml_address;
241*11474SJonathan.Adams@Sun.COM 		mp->ml_size = mlist->ml_size;
242*11474SJonathan.Adams@Sun.COM 		mp->ml_next = NULL;
243*11474SJonathan.Adams@Sun.COM 		mp->ml_prev = prev;
2441708Sstevel 
2451708Sstevel 		if (prev == NULL)
2461708Sstevel 			hl = mp;
2471708Sstevel 		else
248*11474SJonathan.Adams@Sun.COM 			prev->ml_next = mp;
2491708Sstevel 		prev = mp;
2501708Sstevel 	}
2511708Sstevel 
2521708Sstevel 	return (hl);
2531708Sstevel }
2541708Sstevel 
2551708Sstevel int
sbdp_del_memlist(sbdp_handle_t * hp,struct memlist * mlist)2561708Sstevel sbdp_del_memlist(sbdp_handle_t *hp, struct memlist *mlist)
2571708Sstevel {
2581708Sstevel 	_NOTE(ARGUNUSED(hp))
2591708Sstevel 
2601708Sstevel 	memlist_free_list(mlist);
2611708Sstevel 
2621708Sstevel 	return (0);
2631708Sstevel }
2641708Sstevel 
2651708Sstevel /*ARGSUSED*/
2661708Sstevel static void
sbdp_flush_ecache(uint64_t a,uint64_t b)2671708Sstevel sbdp_flush_ecache(uint64_t a, uint64_t b)
2681708Sstevel {
2691708Sstevel 	cpu_flush_ecache();
2701708Sstevel }
2711708Sstevel 
2721708Sstevel typedef enum {
2731708Sstevel 	SBDP_CR_OK,
2741708Sstevel 	SBDP_CR_MC_IDLE_ERR
2751708Sstevel } sbdp_cr_err_t;
2761708Sstevel 
2771708Sstevel int
sbdp_move_memory(sbdp_handle_t * hp,int t_bd)2781708Sstevel sbdp_move_memory(sbdp_handle_t *hp, int t_bd)
2791708Sstevel {
2801708Sstevel 	sbdp_bd_t	*s_bdp, *t_bdp;
2811708Sstevel 	int		err = 0;
2821708Sstevel 	caddr_t		mempage;
2831708Sstevel 	ulong_t		data_area, index_area;
2841708Sstevel 	ulong_t		e_area, e_page;
2851708Sstevel 	int		availlen, indexlen, funclen, scriptlen;
2861708Sstevel 	int		*indexp;
2871708Sstevel 	time_t		copytime;
2881708Sstevel 	int		(*funcp)();
2891708Sstevel 	size_t		size;
2901708Sstevel 	struct memlist	*mlist;
2911708Sstevel 	sbdp_sr_handle_t	*srhp;
2921708Sstevel 	sbdp_rename_script_t	*rsp;
2931708Sstevel 	sbdp_rename_script_t	*rsbuffer;
2941708Sstevel 	sbdp_cr_handle_t	*cph;
2951708Sstevel 	int		linesize;
2961708Sstevel 	uint64_t	neer;
2971708Sstevel 	sbdp_cr_err_t	cr_err;
2981708Sstevel 
2991708Sstevel 	cph =  kmem_zalloc(sizeof (sbdp_cr_handle_t), KM_SLEEP);
3001708Sstevel 
3011708Sstevel 	SBDP_DBG_MEM("moving memory from memory board %d to board %d\n",
3021708Sstevel 	    hp->h_board, t_bd);
3031708Sstevel 
3041708Sstevel 	s_bdp = sbdp_get_bd_info(hp->h_wnode, hp->h_board);
3051708Sstevel 	t_bdp = sbdp_get_bd_info(hp->h_wnode, t_bd);
3061708Sstevel 
3071708Sstevel 	if ((s_bdp == NULL) || (t_bdp == NULL)) {
3081708Sstevel 		sbdp_set_err(hp->h_err, ESGT_INTERNAL, NULL);
3091708Sstevel 		return (-1);
3101708Sstevel 	}
3111708Sstevel 
3121708Sstevel 	funclen = (int)((ulong_t)_sbdp_copy_rename_end -
31311066Srafael.vanoni@sun.com 	    (ulong_t)sbdp_copy_rename__relocatable);
3141708Sstevel 
3151708Sstevel 	if (funclen > PAGESIZE) {
3161708Sstevel 		cmn_err(CE_WARN,
3171708Sstevel 		    "sbdp: copy-rename funclen (%d) > PAGESIZE (%d)",
3181708Sstevel 		    funclen, PAGESIZE);
3191708Sstevel 		sbdp_set_err(hp->h_err, ESGT_INTERNAL, NULL);
3201708Sstevel 		return (-1);
3211708Sstevel 	}
3221708Sstevel 
3231708Sstevel 	/*
3241708Sstevel 	 * mempage will be page aligned, since we're calling
3251708Sstevel 	 * kmem_alloc() with an exact multiple of PAGESIZE.
3261708Sstevel 	 */
3271708Sstevel 	mempage = kmem_alloc(PAGESIZE, KM_SLEEP);
3281708Sstevel 
32911311SSurya.Prakki@Sun.COM 	SBDP_DBG_MEM("mempage = 0x%p\n", (void *)mempage);
3301708Sstevel 
3311708Sstevel 	/*
3321708Sstevel 	 * Copy the code for the copy-rename routine into
3331708Sstevel 	 * a page aligned piece of memory.  We do this to guarantee
3341708Sstevel 	 * that we're executing within the same page and thus reduce
3351708Sstevel 	 * the possibility of cache collisions between different
3361708Sstevel 	 * pages.
3371708Sstevel 	 */
3381708Sstevel 	bcopy((caddr_t)sbdp_copy_rename__relocatable, mempage, funclen);
3391708Sstevel 
3401708Sstevel 	funcp = (int (*)())mempage;
3411708Sstevel 
34211311SSurya.Prakki@Sun.COM 	SBDP_DBG_MEM("copy-rename funcp = 0x%p (len = 0x%x)\n", (void *)funcp,
34311311SSurya.Prakki@Sun.COM 	    funclen);
3441708Sstevel 
3451708Sstevel 	/*
3461708Sstevel 	 * Prepare data page that will contain script of
3471708Sstevel 	 * operations to perform during copy-rename.
3481708Sstevel 	 * Allocate temporary buffer to hold script.
3491708Sstevel 	 */
3501708Sstevel 
3511708Sstevel 	size = sizeof (sbdp_rename_script_t) * SBDP_RENAME_MAXOP;
3521708Sstevel 	rsbuffer = kmem_zalloc(size, KM_SLEEP);
3531708Sstevel 
3541708Sstevel 	cph->s_bdp = s_bdp;
3551708Sstevel 	cph->t_bdp = t_bdp;
3561708Sstevel 	cph->script = rsbuffer;
3571708Sstevel 
3581708Sstevel 	/*
3591708Sstevel 	 * We need to make sure we don't switch cpus since we depend on the
3601708Sstevel 	 * correct cpu processing
3611708Sstevel 	 */
3621708Sstevel 	affinity_set(CPU_CURRENT);
3631708Sstevel 	scriptlen = sbdp_prep_rename_script(cph);
3641708Sstevel 	if (scriptlen <= 0) {
36511066Srafael.vanoni@sun.com 		cmn_err(CE_WARN, "sbdp failed to prep for copy-rename");
3661708Sstevel 		sbdp_set_err(hp->h_err, ESGT_INTERNAL, NULL);
3671708Sstevel 		err = 1;
3681708Sstevel 		goto cleanup;
3691708Sstevel 	}
3701708Sstevel 	SBDP_DBG_MEM("copy-rename script length = 0x%x\n", scriptlen);
3711708Sstevel 
3721708Sstevel 	indexlen = sizeof (*indexp) << 1;
3731708Sstevel 
3741708Sstevel 	if ((funclen + scriptlen + indexlen) > PAGESIZE) {
37511066Srafael.vanoni@sun.com 		cmn_err(CE_WARN, "sbdp: func len (%d) + script len (%d) "
37611066Srafael.vanoni@sun.com 		    "+ index len (%d) > PAGESIZE (%d)", funclen, scriptlen,
37711066Srafael.vanoni@sun.com 		    indexlen, PAGESIZE);
3781708Sstevel 		sbdp_set_err(hp->h_err, ESGT_INTERNAL, NULL);
3791708Sstevel 		err = 1;
3801708Sstevel 		goto cleanup;
3811708Sstevel 	}
3821708Sstevel 
3831708Sstevel 	linesize = cpunodes[CPU->cpu_id].ecache_linesize;
3841708Sstevel 
3851708Sstevel 	/*
3861708Sstevel 	 * Find aligned area within data page to maintain script.
3871708Sstevel 	 */
3881708Sstevel 	data_area = (ulong_t)mempage;
3891708Sstevel 	data_area += (ulong_t)funclen + (ulong_t)(linesize - 1);
3901708Sstevel 	data_area &= ~((ulong_t)(linesize - 1));
3911708Sstevel 
3921708Sstevel 	availlen = PAGESIZE - indexlen;
3931708Sstevel 	availlen -= (int)(data_area - (ulong_t)mempage);
3941708Sstevel 
3951708Sstevel 	if (availlen < scriptlen) {
39611066Srafael.vanoni@sun.com 		cmn_err(CE_WARN, "sbdp: available len (%d) < script len (%d)",
39711066Srafael.vanoni@sun.com 		    availlen, scriptlen);
3981708Sstevel 		sbdp_set_err(hp->h_err, ESGT_INTERNAL, NULL);
3991708Sstevel 		err = 1;
4001708Sstevel 		goto cleanup;
4011708Sstevel 	}
4021708Sstevel 
4031708Sstevel 	SBDP_DBG_MEM("copy-rename script data area = 0x%lx\n",
40411066Srafael.vanoni@sun.com 	    data_area);
4051708Sstevel 
4061708Sstevel 	bcopy((caddr_t)rsbuffer, (caddr_t)data_area, scriptlen);
4071708Sstevel 	rsp = (sbdp_rename_script_t *)data_area;
4081708Sstevel 
40911066Srafael.vanoni@sun.com 	index_area = data_area + (ulong_t)scriptlen + (ulong_t)(linesize - 1);
4101708Sstevel 	index_area &= ~((ulong_t)(linesize - 1));
4111708Sstevel 	indexp = (int *)index_area;
4121708Sstevel 	indexp[0] = 0;
4131708Sstevel 	indexp[1] = 0;
4141708Sstevel 
4151708Sstevel 	e_area = index_area + (ulong_t)indexlen;
4161708Sstevel 	e_page = (ulong_t)mempage + PAGESIZE;
4171708Sstevel 	if (e_area > e_page) {
4181708Sstevel 		cmn_err(CE_WARN,
41911066Srafael.vanoni@sun.com 		    "sbdp: index area size (%d) > available (%d)\n",
42011066Srafael.vanoni@sun.com 		    indexlen, (int)(e_page - index_area));
4211708Sstevel 		sbdp_set_err(hp->h_err, ESGT_INTERNAL, NULL);
4221708Sstevel 		err = 1;
4231708Sstevel 		goto cleanup;
4241708Sstevel 	}
4251708Sstevel 
42611311SSurya.Prakki@Sun.COM 	SBDP_DBG_MEM("copy-rename index area = 0x%p\n", (void *)indexp);
4271708Sstevel 
4281708Sstevel 	SBDP_DBG_MEM("cpu %d\n", CPU->cpu_id);
4291708Sstevel 
4301708Sstevel 	srhp = sbdp_get_sr_handle();
4311708Sstevel 	ASSERT(srhp);
4321708Sstevel 
4331708Sstevel 	srhp->sr_flags = hp->h_flags;
4341708Sstevel 
43511066Srafael.vanoni@sun.com 	copytime = ddi_get_lbolt();
4361708Sstevel 
4371708Sstevel 	mutex_enter(&s_bdp->bd_mutex);
4381708Sstevel 	mlist = sbdp_memlist_dup(s_bdp->ml);
4391708Sstevel 	mutex_exit(&s_bdp->bd_mutex);
4401708Sstevel 
4411708Sstevel 	if (mlist == NULL) {
4421708Sstevel 		SBDP_DBG_MEM("Didn't find memory list\n");
4431708Sstevel 	}
4441708Sstevel 	SBDP_DBG_MEM("src\n\tbd\t%d\n\tnode\t%d\n\tbpa 0x%lx\n\tnodes\t%p\n",
44511311SSurya.Prakki@Sun.COM 	    s_bdp->bd, s_bdp->wnode, s_bdp->bpa, (void *)s_bdp->nodes);
4461708Sstevel 	sbdp_memlist_dump(s_bdp->ml);
4471708Sstevel 	SBDP_DBG_MEM("tgt\n\tbd\t%d\n\tnode\t%d\n\tbpa 0x%lx\n\tnodes\t%p\n",
44811311SSurya.Prakki@Sun.COM 	    t_bdp->bd, t_bdp->wnode, t_bdp->bpa, (void *)t_bdp->nodes);
4491708Sstevel 	sbdp_memlist_dump(t_bdp->ml);
4501708Sstevel 
4511708Sstevel 	/*
4521708Sstevel 	 * Quiesce the OS.
4531708Sstevel 	 */
4541708Sstevel 	if (sbdp_suspend(srhp)) {
4551708Sstevel 		sbd_error_t	*sep;
45611066Srafael.vanoni@sun.com 		cmn_err(CE_WARN, "sbdp: failed to quiesce OS for copy-rename");
4571708Sstevel 		sep = &srhp->sep;
4581708Sstevel 		sbdp_set_err(hp->h_err, sep->e_code, sep->e_rsc);
4591708Sstevel 		sbdp_release_sr_handle(srhp);
46011311SSurya.Prakki@Sun.COM 		(void) sbdp_del_memlist(hp, mlist);
4611708Sstevel 		err = 1;
4621708Sstevel 		goto cleanup;
4631708Sstevel 	}
4641708Sstevel 
4651708Sstevel 	/*
4661708Sstevel 	 * =================================
4671708Sstevel 	 * COPY-RENAME BEGIN.
4681708Sstevel 	 * =================================
4691708Sstevel 	 */
4701708Sstevel 	SBDP_DBG_MEM("s_base 0x%lx t_base 0x%lx\n", cph->s_bdp->bpa,
4711708Sstevel 	    cph->t_bdp->bpa);
4721708Sstevel 
4731708Sstevel 	cph->ret = 0;
4741708Sstevel 
4751708Sstevel 	SBDP_DBG_MEM("cph return 0x%lx\n", cph->ret);
4761708Sstevel 
4771708Sstevel 	SBDP_DBG_MEM("Flushing all of the cpu caches\n");
4781708Sstevel 	xc_all(sbdp_flush_ecache, 0, 0);
4791708Sstevel 
4801708Sstevel 	/* disable CE reporting */
4811708Sstevel 	neer = get_error_enable();
4821708Sstevel 	set_error_enable(neer & ~EN_REG_CEEN);
4831708Sstevel 
4841708Sstevel 	cr_err = (*funcp)(cph, mlist, rsp);
4851708Sstevel 
4861708Sstevel 	/* enable CE reporting */
4871708Sstevel 	set_error_enable(neer);
4881708Sstevel 
4891708Sstevel 	SBDP_DBG_MEM("s_base 0x%lx t_base 0x%lx\n", cph->s_bdp->bpa,
4901708Sstevel 	    cph->t_bdp->bpa);
4911708Sstevel 	SBDP_DBG_MEM("cph return 0x%lx\n", cph->ret);
4921708Sstevel 	SBDP_DBG_MEM("after execking the function\n");
4931708Sstevel 
4941708Sstevel 	/*
4951708Sstevel 	 * =================================
4961708Sstevel 	 * COPY-RENAME END.
4971708Sstevel 	 * =================================
4981708Sstevel 	 */
4991708Sstevel 	SBDP_DBG_MEM("err is 0x%d\n", err);
5001708Sstevel 
5011708Sstevel 	/*
5021708Sstevel 	 * Resume the OS.
5031708Sstevel 	 */
5041708Sstevel 	sbdp_resume(srhp);
5051708Sstevel 	if (srhp->sep.e_code) {
5061708Sstevel 		sbd_error_t	*sep;
5071708Sstevel 		cmn_err(CE_WARN,
5081708Sstevel 		    "sbdp: failed to resume OS for copy-rename");
5091708Sstevel 		sep = &srhp->sep;
5101708Sstevel 		sbdp_set_err(hp->h_err, sep->e_code, sep->e_rsc);
5111708Sstevel 		err = 1;
5121708Sstevel 	}
5131708Sstevel 
51411066Srafael.vanoni@sun.com 	copytime = ddi_get_lbolt() - copytime;
5151708Sstevel 
5161708Sstevel 	sbdp_release_sr_handle(srhp);
51711311SSurya.Prakki@Sun.COM 	(void) sbdp_del_memlist(hp, mlist);
5181708Sstevel 
5191708Sstevel 	SBDP_DBG_MEM("copy-rename elapsed time = %ld ticks (%ld secs)\n",
52011066Srafael.vanoni@sun.com 	    copytime, copytime / hz);
5211708Sstevel 
5221708Sstevel 	switch (cr_err) {
5231708Sstevel 	case SBDP_CR_OK:
5241708Sstevel 		break;
5251708Sstevel 	case SBDP_CR_MC_IDLE_ERR: {
5261708Sstevel 		dev_info_t *dip;
5271708Sstevel 		pnode_t nodeid = cph->busy_mc->node;
5281708Sstevel 		char *path = kmem_alloc(MAXPATHLEN, KM_SLEEP);
5291708Sstevel 
5301708Sstevel 		dip = e_ddi_nodeid_to_dip(nodeid);
5311708Sstevel 
5321708Sstevel 		ASSERT(dip != NULL);
5331708Sstevel 
5341708Sstevel 		(void) ddi_pathname(dip, path);
5351708Sstevel 		ddi_release_devi(dip);
5361708Sstevel 		cmn_err(CE_WARN, "failed to idle memory controller %s: "
5371708Sstevel 		    "copy-rename aborted", path);
5381708Sstevel 		kmem_free(path, MAXPATHLEN);
5391708Sstevel 		sbdp_set_err(hp->h_err, ESBD_MEMFAIL, NULL);
5401708Sstevel 		err = 1;
5411708Sstevel 		break;
5421708Sstevel 	}
5431708Sstevel 	default:
5441708Sstevel 		sbdp_set_err(hp->h_err, ESGT_INTERNAL, NULL);
5451708Sstevel 		cmn_err(CE_WARN, "unknown copy-rename error code (%d)", cr_err);
5461708Sstevel 		err = 1;
5471708Sstevel 		break;
5481708Sstevel 	}
5491708Sstevel 
5501708Sstevel 	if (err)
5511708Sstevel 		goto cleanup;
5521708Sstevel 
5531708Sstevel 	/*
5541708Sstevel 	 * Rename memory for lgroup.
5551708Sstevel 	 * Source and target board numbers are packaged in arg.
5561708Sstevel 	 */
5571708Sstevel 	lgrp_plat_config(LGRP_CONFIG_MEM_RENAME,
55811066Srafael.vanoni@sun.com 	    (uintptr_t)(s_bdp->bd | (t_bdp->bd << 16)));
5591708Sstevel 
5601708Sstevel 	/*
5611708Sstevel 	 * swap list of banks
5621708Sstevel 	 */
5631708Sstevel 	sbdp_swap_list_of_banks(s_bdp, t_bdp);
5641708Sstevel 
5651708Sstevel 	/*
5661708Sstevel 	 * Update the cached board info for both the source and the target
5671708Sstevel 	 */
5681708Sstevel 	sbdp_update_bd_info(s_bdp);
5691708Sstevel 	sbdp_update_bd_info(t_bdp);
5701708Sstevel 
5711708Sstevel 	/*
5721708Sstevel 	 * Tell the sc that we have swapped slices.
5731708Sstevel 	 */
5741708Sstevel 	if (sbdp_swap_slices(s_bdp->bd, t_bdp->bd) != 0) {
5751708Sstevel 		/* This is dangerous. The in use slice could be re-used! */
5761708Sstevel 		SBDP_DBG_MEM("swaping slices failed\n");
5771708Sstevel 	}
5781708Sstevel 
5791708Sstevel cleanup:
5801708Sstevel 	kmem_free(rsbuffer, size);
5811708Sstevel 	kmem_free(mempage, PAGESIZE);
5821708Sstevel 	kmem_free(cph, sizeof (sbdp_cr_handle_t));
5831708Sstevel 	affinity_clear();
5841708Sstevel 
5851708Sstevel 	return (err ? -1 : 0);
5861708Sstevel }
5871708Sstevel 
5881708Sstevel static int
sbdp_copy_regs(pnode_t node,uint64_t bpa,uint64_t new_base,int inval,sbdp_rename_script_t * rsp,int * index)5891708Sstevel sbdp_copy_regs(pnode_t node, uint64_t bpa, uint64_t new_base, int inval,
5901708Sstevel 	sbdp_rename_script_t *rsp, int *index)
5911708Sstevel {
5921708Sstevel 	int		i, m;
5931708Sstevel 	mc_regs_t	regs;
5941708Sstevel 	uint64_t	*mc_decode;
5951708Sstevel 
5961708Sstevel 	if (mc_read_regs(node, &regs)) {
5971708Sstevel 		SBDP_DBG_MEM("sbdp_copy_regs: failed to read source Decode "
5981708Sstevel 		    "Regs");
5991708Sstevel 		return (-1);
6001708Sstevel 	}
6011708Sstevel 
6021708Sstevel 	mc_decode = regs.mc_decode;
6031708Sstevel 
6041708Sstevel 	m = *index;
6051708Sstevel 	for (i = 0; i < SBDP_MAX_MCS_PER_NODE; i++) {
6061708Sstevel 		uint64_t	offset, seg_pa, tmp_base;
6071708Sstevel 
6081708Sstevel 		/*
6091708Sstevel 		 * Skip invalid banks
6101708Sstevel 		 */
6111708Sstevel 		if ((mc_decode[i] & SG_DECODE_VALID) != SG_DECODE_VALID) {
6121708Sstevel 			continue;
6131708Sstevel 		}
6141708Sstevel 
6151708Sstevel 		tmp_base = new_base;
6161708Sstevel 		if (!inval) {
6171708Sstevel 			/*
6181708Sstevel 			 * We need to calculate the offset from the base pa
6191708Sstevel 			 * to add it appropriately to the new_base.
6201708Sstevel 			 * The offset needs to be in UM relative to the mc
6211708Sstevel 			 * decode register.  Since we are going from physical
6221708Sstevel 			 * address to UM, we need to shift it by PHYS2UM_SHIFT.
6231708Sstevel 			 * To get it ready to OR it with the MC decode reg,
6241708Sstevel 			 * we need to shift it left MC_UM_SHIFT
6251708Sstevel 			 */
6261708Sstevel 			seg_pa = MC_BASE(mc_decode[i]) << PHYS2UM_SHIFT;
6271708Sstevel 			offset = (seg_pa - bpa);
6281708Sstevel 			/* Convert tmp_base into a physical address */
6291708Sstevel 			tmp_base = (tmp_base >> MC_UM_SHIFT) << PHYS2UM_SHIFT;
6301708Sstevel 			tmp_base += offset;
6311708Sstevel 			/* Convert tmp_base to be MC reg ready */
6321708Sstevel 			tmp_base = (tmp_base >> PHYS2UM_SHIFT) << MC_UM_SHIFT;
6331708Sstevel 		}
6341708Sstevel 
6351708Sstevel 		mc_decode[i] &= ~SG_DECODE_UM;
6361708Sstevel 		mc_decode[i] |= tmp_base;
6371708Sstevel 		mc_decode[i] |= SG_DECODE_VALID;
6381708Sstevel 
6391708Sstevel 		/*
6401708Sstevel 		 * Step 1:	Write source base address to the MC
6411708Sstevel 		 *		with present bit off.
6421708Sstevel 		 */
6431708Sstevel 		rsp[m].masr_addr = mc_get_addr(node, i, &rsp[m].asi);
6441708Sstevel 		rsp[m].masr = mc_decode[i] & ~SG_DECODE_VALID;
6451708Sstevel 		m++;
6461708Sstevel 		/*
6471708Sstevel 		 * Step 2:	Now rewrite the mc reg with present bit on.
6481708Sstevel 		 */
6491708Sstevel 		rsp[m].masr_addr = rsp[m-1].masr_addr;
6501708Sstevel 		rsp[m].masr = mc_decode[i];
6511708Sstevel 		rsp[m].asi = rsp[m-1].asi;
6521708Sstevel 		m++;
6531708Sstevel 	}
6541708Sstevel 
6551708Sstevel 	*index = m;
6561708Sstevel 	return (0);
6571708Sstevel }
6581708Sstevel 
6591708Sstevel static int
sbdp_get_reg_addr(pnode_t nodeid,uint64_t * pa)6601708Sstevel sbdp_get_reg_addr(pnode_t nodeid, uint64_t *pa)
6611708Sstevel {
6621708Sstevel 	mc_regspace	reg;
6631708Sstevel 	int		len;
6641708Sstevel 
6651708Sstevel 	len = prom_getproplen(nodeid, "reg");
6661708Sstevel 	if (len != sizeof (mc_regspace))
6671708Sstevel 		return (-1);
6681708Sstevel 
6691708Sstevel 	if (prom_getprop(nodeid, "reg", (caddr_t)&reg) < 0)
6701708Sstevel 		return (-1);
6711708Sstevel 
6721708Sstevel 	ASSERT(pa != NULL);
6731708Sstevel 
6741708Sstevel 	*pa = ((uint64_t)reg.regspec_addr_hi) << 32;
6751708Sstevel 	*pa |= (uint64_t)reg.regspec_addr_lo;
6761708Sstevel 
6771708Sstevel 	return (0);
6781708Sstevel }
6791708Sstevel 
6801708Sstevel static int
mc_get_sibling_cpu_impl(pnode_t mc_node)6811708Sstevel mc_get_sibling_cpu_impl(pnode_t mc_node)
6821708Sstevel {
6831708Sstevel 	int	len, impl;
6841708Sstevel 	pnode_t	cpu_node;
6851708Sstevel 	char	namebuf[OBP_MAXPROPNAME];
6861708Sstevel 
6871708Sstevel 	cpu_node = mc_get_sibling_cpu(mc_node);
6881708Sstevel 	if (cpu_node == OBP_NONODE) {
6891708Sstevel 		SBDP_DBG_MEM("mc_get_sibling_cpu failed: dnode=0x%x\n",
6901708Sstevel 		    mc_node);
6911708Sstevel 		return (-1);
6921708Sstevel 	}
6931708Sstevel 
6941708Sstevel 	len = prom_getproplen(cpu_node, "name");
6951708Sstevel 	if (len < 0) {
6961708Sstevel 		SBDP_DBG_MEM("invalid prom_getproplen for name prop: "
6971708Sstevel 		    "len=%d, dnode=0x%x\n", len, cpu_node);
6981708Sstevel 		return (-1);
6991708Sstevel 	}
7001708Sstevel 
7011708Sstevel 	if (prom_getprop(cpu_node, "name", (caddr_t)namebuf) == -1) {
7021708Sstevel 		SBDP_DBG_MEM("failed to read name property for dnode=0x%x\n",
7031708Sstevel 		    cpu_node);
7041708Sstevel 		return (-1);
7051708Sstevel 	}
7061708Sstevel 
7071708Sstevel 	/*
7081708Sstevel 	 * If this is a CMP node, the child has the implementation
7091708Sstevel 	 * property.
7101708Sstevel 	 */
7111708Sstevel 	if (strcmp(namebuf, "cmp") == 0) {
7121708Sstevel 		cpu_node = prom_childnode(cpu_node);
7131708Sstevel 		ASSERT(cpu_node != OBP_NONODE);
7141708Sstevel 	}
7151708Sstevel 
7161708Sstevel 	if (prom_getprop(cpu_node, "implementation#", (caddr_t)&impl) == -1) {
7171708Sstevel 		SBDP_DBG_MEM("failed to read implementation# property for "
7181708Sstevel 		    "dnode=0x%x\n", cpu_node);
7191708Sstevel 		return (-1);
7201708Sstevel 	}
7211708Sstevel 
7221708Sstevel 	SBDP_DBG_MEM("mc_get_sibling_cpu_impl: found impl=0x%x, dnode=0x%x\n",
7231708Sstevel 	    impl, cpu_node);
7241708Sstevel 
7251708Sstevel 	return (impl);
7261708Sstevel }
7271708Sstevel 
7281708Sstevel /*
7291708Sstevel  * Provide EMU Activity Status register ASI and address.  Only valid for
7301708Sstevel  * Panther processors.
7311708Sstevel  */
7321708Sstevel static int
mc_get_idle_reg(pnode_t nodeid,uint64_t * addr,uint_t * asi)7331708Sstevel mc_get_idle_reg(pnode_t nodeid, uint64_t *addr, uint_t *asi)
7341708Sstevel {
7351708Sstevel 	int	portid;
7361708Sstevel 	uint64_t reg_pa;
7371708Sstevel 
7381708Sstevel 	ASSERT(nodeid != OBP_NONODE);
7391708Sstevel 	ASSERT(mc_get_sibling_cpu_impl(nodeid) == PANTHER_IMPL);
7401708Sstevel 
7411708Sstevel 	if (prom_getprop(nodeid, "portid", (caddr_t)&portid) < 0 ||
7421708Sstevel 	    portid == -1) {
7431708Sstevel 		SBDP_DBG_MEM("mc_get_idle_reg: failed to read portid prop "
7441708Sstevel 		    "for dnode=0x%x\n", nodeid);
7451708Sstevel 		return (-1);
7461708Sstevel 	}
7471708Sstevel 
7481708Sstevel 	if (sbdp_get_reg_addr(nodeid, &reg_pa) != 0) {
7491708Sstevel 		SBDP_DBG_MEM("mc_get_idle_reg: failed to read reg prop "
7501708Sstevel 		    "for dnode=0x%x\n", nodeid);
7511708Sstevel 		return (-1);
7521708Sstevel 	}
7531708Sstevel 
7541708Sstevel 	/*
7551708Sstevel 	 * Local access will be via ASI 0x4a, otherwise via Safari PIO.
7561708Sstevel 	 * This assumes the copy-rename will later run on the same proc,
7571708Sstevel 	 * hence there is an assumption we are already bound.
7581708Sstevel 	 */
7591708Sstevel 	ASSERT(curthread->t_bound_cpu == CPU);
7601708Sstevel 	if (SG_CPUID_TO_PORTID(CPU->cpu_id) == portid) {
7611708Sstevel 		*addr = ASI_EMU_ACT_STATUS_VA;
7621708Sstevel 		*asi = ASI_SAFARI_CONFIG;
7631708Sstevel 	} else {
7641708Sstevel 		*addr = MC_ACTIVITY_STATUS(reg_pa);
7651708Sstevel 		*asi = ASI_IO;
7661708Sstevel 	}
7671708Sstevel 
7681708Sstevel 	return (0);
7691708Sstevel }
7701708Sstevel 
7711708Sstevel /*
7721708Sstevel  * If non-Panther board, add phys_banks entry for each physical bank.
7731708Sstevel  * If Panther board, add mc_idle_regs entry for each EMU Activity Status
7741708Sstevel  * register.  Increment the array indices b_idx and r_idx for each entry
7751708Sstevel  * populated by this routine.
7761708Sstevel  *
7771708Sstevel  * The caller is responsible for allocating sufficient array entries.
7781708Sstevel  */
7791708Sstevel static int
sbdp_prep_mc_idle_one(sbdp_bd_t * bp,sbdp_rename_script_t phys_banks[],int * b_idx,sbdp_mc_idle_script_t mc_idle_regs[],int * r_idx)7801708Sstevel sbdp_prep_mc_idle_one(sbdp_bd_t *bp, sbdp_rename_script_t phys_banks[],
7811708Sstevel     int *b_idx, sbdp_mc_idle_script_t mc_idle_regs[], int *r_idx)
7821708Sstevel {
7831708Sstevel 	int		i, j;
7841708Sstevel 	pnode_t		*memnodes;
7851708Sstevel 	mc_regs_t	regs;
7861708Sstevel 	uint64_t	addr;
7871708Sstevel 	uint_t		asi;
7881708Sstevel 	sbd_cond_t	sibling_cpu_cond;
7891708Sstevel 	int		impl = -1;
7901708Sstevel 
7911708Sstevel 	memnodes = bp->nodes;
7921708Sstevel 
7931708Sstevel 	for (i = 0; i < SBDP_MAX_MEM_NODES_PER_BOARD; i++) {
7941708Sstevel 		if (memnodes[i] == OBP_NONODE) {
7951708Sstevel 			continue;
7961708Sstevel 		}
7971708Sstevel 
7981708Sstevel 		/* MC should not be accessed if cpu has failed  */
7991708Sstevel 		sibling_cpu_cond = mc_check_sibling_cpu(memnodes[i]);
8001708Sstevel 		if (sibling_cpu_cond == SBD_COND_FAILED ||
8011708Sstevel 		    sibling_cpu_cond == SBD_COND_UNUSABLE) {
8021708Sstevel 			SBDP_DBG_MEM("sbdp: skipping MC with failed cpu: "
8031708Sstevel 			    "board=%d, mem node=%d, condition=%d",
8041708Sstevel 			    bp->bd, i, sibling_cpu_cond);
8051708Sstevel 			continue;
8061708Sstevel 		}
8071708Sstevel 
8081708Sstevel 		/*
8091708Sstevel 		 * Initialize the board cpu type, assuming all board cpus are
8101708Sstevel 		 * the same type.  This is true of all Cheetah-based processors.
8111708Sstevel 		 * Failure to read the cpu type is considered a fatal error.
8121708Sstevel 		 */
8131708Sstevel 		if (impl == -1) {
8141708Sstevel 			impl = mc_get_sibling_cpu_impl(memnodes[i]);
8151708Sstevel 			if (impl == -1) {
8161708Sstevel 				SBDP_DBG_MEM("sbdp: failed to get cpu impl "
8171708Sstevel 				    "for MC dnode=0x%x\n", memnodes[i]);
8181708Sstevel 				return (-1);
8191708Sstevel 			}
8201708Sstevel 		}
8211708Sstevel 
8221708Sstevel 		switch (impl) {
8231708Sstevel 		case CHEETAH_IMPL:
8241708Sstevel 		case CHEETAH_PLUS_IMPL:
8251708Sstevel 		case JAGUAR_IMPL:
8261708Sstevel 			if (mc_read_regs(memnodes[i], &regs)) {
8271708Sstevel 				SBDP_DBG_MEM("sbdp: failed to read source "
8281708Sstevel 				    "Decode Regs of board %d", bp->bd);
8291708Sstevel 				return (-1);
8301708Sstevel 			}
8311708Sstevel 
8321708Sstevel 			for (j = 0; j < SBDP_MAX_MCS_PER_NODE; j++) {
8331708Sstevel 				uint64_t mc_decode = regs.mc_decode[j];
8341708Sstevel 
8351708Sstevel 				if ((mc_decode & SG_DECODE_VALID) !=
8361708Sstevel 				    SG_DECODE_VALID) {
8371708Sstevel 					continue;
8381708Sstevel 				}
8391708Sstevel 
8401708Sstevel 				addr = (MC_BASE(mc_decode) << PHYS2UM_SHIFT) |
8411708Sstevel 				    (MC_LM(mc_decode) << MC_LM_SHIFT);
8421708Sstevel 
8431708Sstevel 				phys_banks[*b_idx].masr_addr = addr;
8441708Sstevel 				phys_banks[*b_idx].masr = 0;	/* unused */
8451708Sstevel 				phys_banks[*b_idx].asi = ASI_MEM;
8461708Sstevel 				(*b_idx)++;
8471708Sstevel 			}
8481708Sstevel 			break;
8491708Sstevel 		case PANTHER_IMPL:
8501708Sstevel 			if (mc_get_idle_reg(memnodes[i], &addr, &asi)) {
8511708Sstevel 				return (-1);
8521708Sstevel 			}
8531708Sstevel 
8541708Sstevel 			mc_idle_regs[*r_idx].addr = addr;
8551708Sstevel 			mc_idle_regs[*r_idx].asi = asi;
8561708Sstevel 			mc_idle_regs[*r_idx].node = memnodes[i];
8571708Sstevel 			mc_idle_regs[*r_idx].bd_id = bp->bd;
8581708Sstevel 			(*r_idx)++;
8591708Sstevel 			break;
8601708Sstevel 		default:
8611708Sstevel 			cmn_err(CE_WARN, "Unknown cpu implementation=0x%x",
8621708Sstevel 			    impl);
8631708Sstevel 			ASSERT(0);
8641708Sstevel 			return (-1);
8651708Sstevel 		}
8661708Sstevel 	}
8671708Sstevel 
8681708Sstevel 	return (0);
8691708Sstevel }
8701708Sstevel 
8711708Sstevel /*
8721708Sstevel  * For non-Panther MCs that do not support read-bypass-write, we do a read
8731708Sstevel  * to each physical bank, relying on the reads to block until all outstanding
8741708Sstevel  * write requests have completed.  This mechanism is referred to as the bus
8751708Sstevel  * sync list and is used for Cheetah, Cheetah+, and Jaguar processors.  The
8761708Sstevel  * bus sync list PAs for the source and target are kept together and comprise
8771708Sstevel  * Section 1 of the rename script.
8781708Sstevel  *
8791708Sstevel  * For Panther processors that support the EMU Activity Status register,
8801708Sstevel  * we ensure the writes have completed by polling the MCU_ACT_STATUS
8811708Sstevel  * field several times to make sure the MC queues are empty.  The
8821708Sstevel  * EMU Activity Status register PAs for the source and target are
8831708Sstevel  * kept together and comprise Section 2 of the rename script.
8841708Sstevel  */
8851708Sstevel static int
sbdp_prep_mc_idle_script(sbdp_bd_t * s_bp,sbdp_bd_t * t_bp,sbdp_rename_script_t * rsp,int * rsp_idx)8861708Sstevel sbdp_prep_mc_idle_script(sbdp_bd_t *s_bp, sbdp_bd_t *t_bp,
8871708Sstevel     sbdp_rename_script_t *rsp, int *rsp_idx)
8881708Sstevel {
8891708Sstevel 	sbdp_rename_script_t *phys_banks;
8901708Sstevel 	sbdp_mc_idle_script_t *mc_idle_regs;
8911708Sstevel 	int	max_banks, max_regs;
8921708Sstevel 	size_t	bsize, msize;
8931708Sstevel 	int	nbanks = 0, nregs = 0;
8941708Sstevel 	int	i;
8951708Sstevel 
8961708Sstevel 	/* CONSTCOND */
8971708Sstevel 	ASSERT(sizeof (sbdp_rename_script_t) ==
8981708Sstevel 	    sizeof (sbdp_mc_idle_script_t));
8991708Sstevel 
9001708Sstevel 	/* allocate space for both source and target */
9011708Sstevel 	max_banks = SBDP_MAX_MEM_NODES_PER_BOARD *
9021708Sstevel 	    SG_MAX_BANKS_PER_MC * 2;
9031708Sstevel 	max_regs = SBDP_MAX_MEM_NODES_PER_BOARD * 2;
9041708Sstevel 
9051708Sstevel 	bsize = sizeof (sbdp_rename_script_t) * max_banks;
9061708Sstevel 	msize = sizeof (sbdp_mc_idle_script_t) * max_regs;
9071708Sstevel 
9081708Sstevel 	phys_banks = kmem_zalloc(bsize, KM_SLEEP);
9091708Sstevel 	mc_idle_regs = kmem_zalloc(msize, KM_SLEEP);
9101708Sstevel 
9111708Sstevel 	if (sbdp_prep_mc_idle_one(t_bp, phys_banks, &nbanks,
9121708Sstevel 	    mc_idle_regs, &nregs) != 0 ||
9131708Sstevel 	    sbdp_prep_mc_idle_one(s_bp, phys_banks, &nbanks,
9141708Sstevel 	    mc_idle_regs, &nregs) != 0) {
9151708Sstevel 		kmem_free(phys_banks, bsize);
9161708Sstevel 		kmem_free(mc_idle_regs, msize);
9171708Sstevel 		return (-1);
9181708Sstevel 	}
9191708Sstevel 
9201708Sstevel 	/* section 1 */
9211708Sstevel 	for (i = 0; i < nbanks; i++)
9221708Sstevel 		rsp[(*rsp_idx)++] = phys_banks[i];
9231708Sstevel 
9241708Sstevel 	/* section 2 */
9251708Sstevel 	for (i = 0; i < nregs; i++)
9261708Sstevel 		rsp[(*rsp_idx)++] = *(sbdp_rename_script_t *)&mc_idle_regs[i];
9271708Sstevel 
9281708Sstevel 	kmem_free(phys_banks, bsize);
9291708Sstevel 	kmem_free(mc_idle_regs, msize);
9301708Sstevel 
9311708Sstevel 	return (0);
9321708Sstevel }
9331708Sstevel 
9341708Sstevel /*
9351708Sstevel  * code assumes single mem-unit.
9361708Sstevel  */
9371708Sstevel static int
sbdp_prep_rename_script(sbdp_cr_handle_t * cph)9381708Sstevel sbdp_prep_rename_script(sbdp_cr_handle_t *cph)
9391708Sstevel {
9401708Sstevel 	pnode_t			*s_nodes, *t_nodes;
9411708Sstevel 	int			m = 0, i;
9421708Sstevel 	sbdp_bd_t		s_bd, t_bd, *s_bdp, *t_bdp;
9431708Sstevel 	sbdp_rename_script_t	*rsp;
9441708Sstevel 	uint64_t		new_base, old_base, temp_base;
9451708Sstevel 	int			s_num, t_num;
9461708Sstevel 
9471708Sstevel 	mutex_enter(&cph->s_bdp->bd_mutex);
9481708Sstevel 	s_bd = *cph->s_bdp;
9491708Sstevel 	mutex_exit(&cph->s_bdp->bd_mutex);
9501708Sstevel 	mutex_enter(&cph->t_bdp->bd_mutex);
9511708Sstevel 	t_bd = *cph->t_bdp;
9521708Sstevel 	mutex_exit(&cph->t_bdp->bd_mutex);
9531708Sstevel 
9541708Sstevel 	s_bdp = &s_bd;
9551708Sstevel 	t_bdp = &t_bd;
9561708Sstevel 	s_nodes = s_bdp->nodes;
9571708Sstevel 	t_nodes = t_bdp->nodes;
9581708Sstevel 	s_num = s_bdp->nnum;
9591708Sstevel 	t_num = t_bdp->nnum;
9601708Sstevel 	rsp = cph->script;
9611708Sstevel 
9621708Sstevel 	/*
9631708Sstevel 	 * Calculate the new base address for the target bd
9641708Sstevel 	 */
9651708Sstevel 
9661708Sstevel 	new_base = (s_bdp->bpa >> PHYS2UM_SHIFT) << MC_UM_SHIFT;
9671708Sstevel 
9681708Sstevel 	/*
9691708Sstevel 	 * Calculate the old base address for the source bd
9701708Sstevel 	 */
9711708Sstevel 
9721708Sstevel 	old_base = (t_bdp->bpa >> PHYS2UM_SHIFT) << MC_UM_SHIFT;
9731708Sstevel 
9741708Sstevel 	temp_base = SG_INVAL_UM;
9751708Sstevel 
9761708Sstevel 	SBDP_DBG_MEM("new 0x%lx old_base ox%lx temp_base 0x%lx\n", new_base,
9771708Sstevel 	    old_base, temp_base);
9781708Sstevel 
9791708Sstevel 	m = 0;
9801708Sstevel 
9811708Sstevel 	/*
9821708Sstevel 	 * Ensure the MC queues have been idled on the source and target
9831708Sstevel 	 * following the copy.
9841708Sstevel 	 */
9851708Sstevel 	if (sbdp_prep_mc_idle_script(s_bdp, t_bdp, rsp, &m) < 0)
9861708Sstevel 		return (-1);
9871708Sstevel 
9881708Sstevel 	/*
9891708Sstevel 	 * Script section terminator
9901708Sstevel 	 */
9911708Sstevel 	rsp[m].masr_addr = 0ull;
9921708Sstevel 	rsp[m].masr = 0;
9931708Sstevel 	rsp[m].asi = 0;
9941708Sstevel 	m++;
9951708Sstevel 
9961708Sstevel 	/*
9971708Sstevel 	 * Invalidate the base in the target mc registers
9981708Sstevel 	 */
9991708Sstevel 	for (i = 0; i < t_num; i++) {
10001708Sstevel 		if (sbdp_copy_regs(t_nodes[i], t_bdp->bpa, temp_base, 1, rsp,
10011708Sstevel 		    &m) < 0)
10021708Sstevel 			return (-1);
10031708Sstevel 	}
10041708Sstevel 	/*
10051708Sstevel 	 * Invalidate the base in the source mc registers
10061708Sstevel 	 */
10071708Sstevel 	for (i = 0; i < s_num; i++) {
10081708Sstevel 		if (sbdp_copy_regs(s_nodes[i], s_bdp->bpa, temp_base, 1, rsp,
10091708Sstevel 		    &m) < 0)
10101708Sstevel 			return (-1);
10111708Sstevel 	}
10121708Sstevel 	/*
10131708Sstevel 	 * Copy the new base into the targets mc registers
10141708Sstevel 	 */
10151708Sstevel 	for (i = 0; i < t_num; i++) {
10161708Sstevel 		if (sbdp_copy_regs(t_nodes[i], t_bdp->bpa, new_base, 0, rsp,
10171708Sstevel 		    &m) < 0)
10181708Sstevel 			return (-1);
10191708Sstevel 	}
10201708Sstevel 	/*
10211708Sstevel 	 * Copy the old base into the source mc registers
10221708Sstevel 	 */
10231708Sstevel 	for (i = 0; i < s_num; i++) {
10241708Sstevel 		if (sbdp_copy_regs(s_nodes[i], s_bdp->bpa, old_base, 0, rsp,
10251708Sstevel 		    &m) < 0)
10261708Sstevel 			return (-1);
10271708Sstevel 	}
10281708Sstevel 	/*
10291708Sstevel 	 * Zero masr_addr value indicates the END.
10301708Sstevel 	 */
10311708Sstevel 	rsp[m].masr_addr = 0ull;
10321708Sstevel 	rsp[m].masr = 0;
10331708Sstevel 	rsp[m].asi = 0;
10341708Sstevel 	m++;
10351708Sstevel 
10361708Sstevel #ifdef DEBUG
10371708Sstevel 	{
10381708Sstevel 		int	i;
10391708Sstevel 
10401708Sstevel 		SBDP_DBG_MEM("dumping copy-rename script:\n");
10411708Sstevel 		for (i = 0; i < m; i++) {
10421708Sstevel 			SBDP_DBG_MEM("0x%lx = 0x%lx, asi 0x%x\n",
104311066Srafael.vanoni@sun.com 			    rsp[i].masr_addr, rsp[i].masr, rsp[i].asi);
10441708Sstevel 		}
10451708Sstevel 		DELAY(1000000);
10461708Sstevel 	}
10471708Sstevel #endif /* DEBUG */
10481708Sstevel 
10491708Sstevel 	return (m * sizeof (sbdp_rename_script_t));
10501708Sstevel }
10511708Sstevel 
10521708Sstevel /*
10531708Sstevel  * EMU Activity Status Register needs to be read idle several times.
10541708Sstevel  * See Panther PRM 12.5.
10551708Sstevel  */
10561708Sstevel #define	SBDP_MCU_IDLE_RETRIES	10
10571708Sstevel #define	SBDP_MCU_IDLE_READS	3
10581708Sstevel 
10591708Sstevel /*
10601708Sstevel  * Using the "__relocatable" suffix informs DTrace providers (and anything
10611708Sstevel  * else, for that matter) that this function's text may be manually relocated
10621708Sstevel  * elsewhere before it is executed.  That is, it cannot be safely instrumented
10631708Sstevel  * with any methodology that is PC-relative.
10641708Sstevel  */
10651708Sstevel static int
sbdp_copy_rename__relocatable(sbdp_cr_handle_t * hp,struct memlist * mlist,register sbdp_rename_script_t * rsp)10661708Sstevel sbdp_copy_rename__relocatable(sbdp_cr_handle_t *hp, struct memlist *mlist,
10671708Sstevel 		register sbdp_rename_script_t *rsp)
10681708Sstevel {
10691708Sstevel 	sbdp_cr_err_t	err = SBDP_CR_OK;
10701708Sstevel 	size_t		csize;
10711708Sstevel 	size_t		linesize;
10721708Sstevel 	uint_t		size;
10731708Sstevel 	uint64_t	caddr;
10741708Sstevel 	uint64_t	s_base, t_base;
10751708Sstevel 	sbdp_bd_t	*s_sbp, *t_sbp;
10761708Sstevel 	struct memlist	*ml;
10771708Sstevel 	sbdp_mc_idle_script_t *isp;
10781708Sstevel 	int		i;
10791708Sstevel 
10801708Sstevel 	caddr = ecache_flushaddr;
10811708Sstevel 	csize = (size_t)(cpunodes[CPU->cpu_id].ecache_size * 2);
10821708Sstevel 	linesize = (size_t)(cpunodes[CPU->cpu_id].ecache_linesize);
10831708Sstevel 
10841708Sstevel 	size = 0;
10851708Sstevel 	s_sbp = hp->s_bdp;
10861708Sstevel 	t_sbp = hp->t_bdp;
10871708Sstevel 
10881708Sstevel 	s_base = (uint64_t)s_sbp->bpa;
10891708Sstevel 	t_base = (uint64_t)t_sbp->bpa;
10901708Sstevel 
10911708Sstevel 	hp->ret = s_base;
10921708Sstevel 	/*
10931708Sstevel 	 * DO COPY.
10941708Sstevel 	 */
1095*11474SJonathan.Adams@Sun.COM 	for (ml = mlist; ml; ml = ml->ml_next) {
10961708Sstevel 		uint64_t	s_pa, t_pa;
10971708Sstevel 		uint64_t	nbytes;
10981708Sstevel 
1099*11474SJonathan.Adams@Sun.COM 		s_pa = ml->ml_address;
1100*11474SJonathan.Adams@Sun.COM 		t_pa = t_base + (ml->ml_address - s_base);
1101*11474SJonathan.Adams@Sun.COM 		nbytes = ml->ml_size;
11021708Sstevel 
11031708Sstevel 		size += nbytes;
11041708Sstevel 		while (nbytes != 0ull) {
11051708Sstevel 			/*
11061708Sstevel 			 * This copy does NOT use an ASI
11071708Sstevel 			 * that avoids the Ecache, therefore
11081708Sstevel 			 * the dst_pa addresses may remain
11091708Sstevel 			 * in our Ecache after the dst_pa
11101708Sstevel 			 * has been removed from the system.
11111708Sstevel 			 * A subsequent write-back to memory
11121708Sstevel 			 * will cause an ARB-stop because the
11131708Sstevel 			 * physical address no longer exists
11141708Sstevel 			 * in the system. Therefore we must
11151708Sstevel 			 * flush out local Ecache after we
11161708Sstevel 			 * finish the copy.
11171708Sstevel 			 */
11181708Sstevel 
11191708Sstevel 			/* copy 32 bytes at src_pa to dst_pa */
11201708Sstevel 			bcopy32_il(s_pa, t_pa);
11211708Sstevel 
11221708Sstevel 			/* increment by 32 bytes */
11231708Sstevel 			s_pa += (4 * sizeof (uint64_t));
11241708Sstevel 			t_pa += (4 * sizeof (uint64_t));
11251708Sstevel 
11261708Sstevel 			/* decrement by 32 bytes */
11271708Sstevel 			nbytes -= (4 * sizeof (uint64_t));
11281708Sstevel 		}
11291708Sstevel 	}
11301708Sstevel 
11311708Sstevel 	/*
11321708Sstevel 	 * Since bcopy32_il() does NOT use an ASI to bypass
11331708Sstevel 	 * the Ecache, we need to flush our Ecache after
11341708Sstevel 	 * the copy is complete.
11351708Sstevel 	 */
11361708Sstevel 	flush_ecache_il(caddr, csize, linesize);	/* inline version */
11371708Sstevel 
11381708Sstevel 	/*
11391708Sstevel 	 * Non-Panther MCs are idled by reading each physical bank.
11401708Sstevel 	 */
11411708Sstevel 	for (i = 0; rsp[i].asi == ASI_MEM; i++) {
11421708Sstevel 		(void) lddphys_il(rsp[i].masr_addr);
11431708Sstevel 	}
11441708Sstevel 
11451708Sstevel 	isp = (sbdp_mc_idle_script_t *)&rsp[i];
11461708Sstevel 
11471708Sstevel 	/*
11481708Sstevel 	 * Panther MCs are idled by polling until the MCU idle state
11491708Sstevel 	 * is read SBDP_MCU_IDLE_READS times in succession.
11501708Sstevel 	 */
11511708Sstevel 	while (isp->addr != 0ull) {
11521708Sstevel 		for (i = 0; i < SBDP_MCU_IDLE_RETRIES; i++) {
11531708Sstevel 			register uint64_t v;
11541708Sstevel 			register int n_idle = 0;
11551708Sstevel 
11561708Sstevel 
11571708Sstevel 			do {
11581708Sstevel 				v = ldxasi_il(isp->addr, isp->asi) &
11591708Sstevel 				    MCU_ACT_STATUS;
11601708Sstevel 			} while (v != MCU_ACT_STATUS &&
11611708Sstevel 			    ++n_idle < SBDP_MCU_IDLE_READS);
11621708Sstevel 
11631708Sstevel 			if (n_idle == SBDP_MCU_IDLE_READS)
11641708Sstevel 				break;
11651708Sstevel 		}
11661708Sstevel 
11671708Sstevel 		if (i == SBDP_MCU_IDLE_RETRIES) {
11681708Sstevel 			/* bailout */
11691708Sstevel 			hp->busy_mc = isp;
11701708Sstevel 			return (SBDP_CR_MC_IDLE_ERR);
11711708Sstevel 		}
11721708Sstevel 
11731708Sstevel 		isp++;
11741708Sstevel 	}
11751708Sstevel 
11761708Sstevel 	/* skip terminator */
11771708Sstevel 	isp++;
11781708Sstevel 
11791708Sstevel 	/*
11801708Sstevel 	 * The following inline assembly routine caches
11811708Sstevel 	 * the rename script and then caches the code that
11821708Sstevel 	 * will do the rename.  This is necessary
11831708Sstevel 	 * so that we don't have any memory references during
11841708Sstevel 	 * the reprogramming.  We accomplish this by first
11851708Sstevel 	 * jumping through the code to guarantee it's cached
11861708Sstevel 	 * before we actually execute it.
11871708Sstevel 	 */
11881708Sstevel 	sbdp_exec_script_il((sbdp_rename_script_t *)isp);
11891708Sstevel 
11901708Sstevel 	return (err);
11911708Sstevel }
11921708Sstevel static void
_sbdp_copy_rename_end(void)11931708Sstevel _sbdp_copy_rename_end(void)
11941708Sstevel {
11951708Sstevel 	/*
11961708Sstevel 	 * IMPORTANT:   This function's location MUST be located immediately
11971708Sstevel 	 *		following sbdp_copy_rename__relocatable to accurately
11981708Sstevel 	 *		estimate its size.  Note that this assumes (!)the
11991708Sstevel 	 *		compiler keeps these functions in the order in which
12001708Sstevel 	 *		they appear :-o
12011708Sstevel 	 */
12021708Sstevel }
12031708Sstevel int
sbdp_memory_rename(sbdp_handle_t * hp)12041708Sstevel sbdp_memory_rename(sbdp_handle_t *hp)
12051708Sstevel {
12061708Sstevel #ifdef lint
12071708Sstevel 	/*
12081708Sstevel 	 * Delete when implemented
12091708Sstevel 	 */
12101708Sstevel 	hp = hp;
12111708Sstevel #endif
12121708Sstevel 	return (0);
12131708Sstevel }
12141708Sstevel 
12151708Sstevel 
12161708Sstevel /*
12171708Sstevel  * In Serengeti this is a nop
12181708Sstevel  */
12191708Sstevel int
sbdp_post_configure_mem(sbdp_handle_t * hp)12201708Sstevel sbdp_post_configure_mem(sbdp_handle_t *hp)
12211708Sstevel {
12221708Sstevel #ifdef lint
12231708Sstevel 	hp = hp;
12241708Sstevel #endif
12251708Sstevel 	return (0);
12261708Sstevel }
12271708Sstevel 
12281708Sstevel /*
12291708Sstevel  * In Serengeti this is a nop
12301708Sstevel  */
12311708Sstevel int
sbdp_post_unconfigure_mem(sbdp_handle_t * hp)12321708Sstevel sbdp_post_unconfigure_mem(sbdp_handle_t *hp)
12331708Sstevel {
12341708Sstevel #ifdef lint
12351708Sstevel 	hp = hp;
12361708Sstevel #endif
12371708Sstevel 	return (0);
12381708Sstevel }
12391708Sstevel 
12401708Sstevel /* ARGSUSED */
12411708Sstevel int
sbdphw_disable_memctrl(sbdp_handle_t * hp,dev_info_t * dip)12421708Sstevel sbdphw_disable_memctrl(sbdp_handle_t *hp, dev_info_t *dip)
12431708Sstevel {
12441708Sstevel 	return (0);
12451708Sstevel }
12461708Sstevel 
12471708Sstevel /* ARGSUSED */
12481708Sstevel int
sbdphw_enable_memctrl(sbdp_handle_t * hp,dev_info_t * dip)12491708Sstevel sbdphw_enable_memctrl(sbdp_handle_t *hp, dev_info_t *dip)
12501708Sstevel {
12511708Sstevel 	return (0);
12521708Sstevel }
12531708Sstevel 
12541708Sstevel /*
12551708Sstevel  * We are assuming one memory node therefore the base address is the lowest
12561708Sstevel  * segment possible
12571708Sstevel  */
12581708Sstevel #define	PA_ABOVE_MAX	(0x8000000000000000ull)
12591708Sstevel int
sbdphw_get_base_physaddr(sbdp_handle_t * hp,dev_info_t * dip,uint64_t * pa)12601708Sstevel sbdphw_get_base_physaddr(sbdp_handle_t *hp, dev_info_t *dip, uint64_t *pa)
12611708Sstevel {
12621708Sstevel 	_NOTE(ARGUNUSED(hp))
12631708Sstevel 
12641708Sstevel 	int i, board = -1, wnode;
12651708Sstevel 	pnode_t	nodeid;
12661708Sstevel 	struct mem_arg arg = {0};
12671708Sstevel 	uint64_t seg_pa, tmp_pa;
12681708Sstevel 	dev_info_t *list[SBDP_MAX_MEM_NODES_PER_BOARD];
12691708Sstevel 	int rc;
12701708Sstevel 
12711708Sstevel 	if (dip == NULL)
12721708Sstevel 		return (-1);
12731708Sstevel 
12741708Sstevel 	nodeid = ddi_get_nodeid(dip);
12751708Sstevel 
12761708Sstevel 	if (sbdp_get_bd_and_wnode_num(nodeid, &board, &wnode) < 0)
12771708Sstevel 		return (-1);
12781708Sstevel 
12791708Sstevel 	list[0] = NULL;
12801708Sstevel 	arg.board = board;
12811708Sstevel 	arg.list = list;
12821708Sstevel 
12831708Sstevel 	(void) sbdp_walk_prom_tree(prom_rootnode(), sbdp_get_mem_dip, &arg);
12841708Sstevel 
12851708Sstevel 	if (arg.ndips <= 0)
12861708Sstevel 		return (-1);
12871708Sstevel 
12881708Sstevel 	seg_pa = PA_ABOVE_MAX;
12891708Sstevel 
12901708Sstevel 	rc = -1;
12911708Sstevel 	for (i = 0; i < arg.ndips; i++) {
12921708Sstevel 		if (list[i] == NULL)
12931708Sstevel 			continue;
12941708Sstevel 		if (sbdp_get_lowest_addr_in_node(ddi_get_nodeid(list[i]),
12951708Sstevel 		    &tmp_pa) == 0) {
12961708Sstevel 			rc = 0;
12971708Sstevel 			if (tmp_pa < seg_pa)
12981708Sstevel 				seg_pa = tmp_pa;
12991708Sstevel 		}
13001708Sstevel 
13011708Sstevel 		/*
13021708Sstevel 		 * Release hold acquired in sbdp_get_mem_dip()
13031708Sstevel 		 */
13041708Sstevel 		ddi_release_devi(list[i]);
13051708Sstevel 	}
13061708Sstevel 
13071708Sstevel 	if (rc == 0)
13081708Sstevel 		*pa = seg_pa;
13091708Sstevel 	else {
13101708Sstevel 		/*
13111708Sstevel 		 * Record the fact that an error has occurred
13121708Sstevel 		 */
13131708Sstevel 		sbdp_set_err(hp->h_err, ESGT_INTERNAL, NULL);
13141708Sstevel 	}
13151708Sstevel 
13161708Sstevel 	return (rc);
13171708Sstevel }
13181708Sstevel 
13191708Sstevel static int
sbdp_get_lowest_addr_in_node(pnode_t node,uint64_t * pa)13201708Sstevel sbdp_get_lowest_addr_in_node(pnode_t node, uint64_t *pa)
13211708Sstevel {
13221708Sstevel 	uint64_t	mc_decode, seg_pa, tmp_pa;
13231708Sstevel 	mc_regs_t	mc_regs, *mc_regsp = &mc_regs;
13241708Sstevel 	int		i, valid;
13251708Sstevel 	int		rc;
13261708Sstevel 
13271708Sstevel 
13281708Sstevel 	seg_pa = PA_ABOVE_MAX;
13291708Sstevel 
13301708Sstevel 	if (mc_read_regs(node, mc_regsp)) {
13311708Sstevel 		SBDP_DBG_MEM("sbdp_get_lowest_addr_in_node: failed to "
13321708Sstevel 		    "read source Decode Regs\n");
13331708Sstevel 		return (-1);
13341708Sstevel 	}
13351708Sstevel 
13361708Sstevel 	rc = -1;
13371708Sstevel 	for (i = 0; i < SBDP_MAX_MCS_PER_NODE; i++) {
13381708Sstevel 		mc_decode = mc_regsp->mc_decode[i];
13391708Sstevel 		valid = mc_decode >> MC_VALID_SHIFT;
13401708Sstevel 		tmp_pa = MC_BASE(mc_decode) << PHYS2UM_SHIFT;
13411708Sstevel 		if (valid)
13421708Sstevel 			rc = 0;
13431708Sstevel 		if (valid && (tmp_pa < seg_pa))
13441708Sstevel 			seg_pa = tmp_pa;
13451708Sstevel 	}
13461708Sstevel 
13471708Sstevel 	if (rc == 0)
13481708Sstevel 		*pa = seg_pa;
13491708Sstevel 
13501708Sstevel 	return (rc);
13511708Sstevel }
13521708Sstevel 
13531708Sstevel int
sbdp_is_mem(pnode_t node,void * arg)13541708Sstevel sbdp_is_mem(pnode_t node, void *arg)
13551708Sstevel {
13561708Sstevel 	mem_op_t	*memp = (mem_op_t *)arg;
13571708Sstevel 	char		type[OBP_MAXPROPNAME];
13581708Sstevel 	int		bd;
13591708Sstevel 	pnode_t		*list;
13601708Sstevel 	int		board;
13611708Sstevel 	char		name[OBP_MAXDRVNAME];
13621708Sstevel 	int		len;
13631708Sstevel 
13641708Sstevel 	ASSERT(memp);
13651708Sstevel 
13661708Sstevel 	list = memp->nodes;
13671708Sstevel 	board = memp->board;
13681708Sstevel 
13691708Sstevel 	/*
13701708Sstevel 	 * Make sure that this node doesn't have its status
13711708Sstevel 	 * as failed
13721708Sstevel 	 */
13731708Sstevel 	if (sbdp_get_comp_status(node) != SBD_COND_OK) {
13741708Sstevel 		return (DDI_FAILURE);
13751708Sstevel 	}
13761708Sstevel 
13771708Sstevel 	len = prom_getproplen(node, "device_type");
13781708Sstevel 	if ((len > 0) && (len < OBP_MAXPROPNAME))
13791708Sstevel 		(void) prom_getprop(node, "device_type", (caddr_t)type);
13801708Sstevel 	else
13811708Sstevel 		type[0] = '\0';
13821708Sstevel 
13831708Sstevel 	if (strcmp(type, "memory-controller") == 0) {
13841708Sstevel 		int	wnode;
13851708Sstevel 
13861708Sstevel 		if (sbdp_get_bd_and_wnode_num(node, &bd, &wnode) < 0)
13871708Sstevel 			return (DDI_FAILURE);
13881708Sstevel 
13891708Sstevel 		if (bd == board) {
13901708Sstevel 			/*
13911708Sstevel 			 * Make sure we don't overwrite the array
13921708Sstevel 			 */
13931708Sstevel 			if (memp->nmem >= SBDP_MAX_MEM_NODES_PER_BOARD)
13941708Sstevel 				return (DDI_FAILURE);
13951708Sstevel 			(void) prom_getprop(node, OBP_NAME, (caddr_t)name);
13961708Sstevel 			SBDP_DBG_MEM("name %s  boot bd %d board %d\n", name,
13971708Sstevel 			    board, bd);
13981708Sstevel 			list[memp->nmem++] = node;
13991708Sstevel 			return (DDI_SUCCESS);
14001708Sstevel 		}
14011708Sstevel 	}
14021708Sstevel 
14031708Sstevel 	return (DDI_FAILURE);
14041708Sstevel }
14051708Sstevel 
14061708Sstevel static int
sbdp_get_meminfo(pnode_t nodeid,int mc,uint64_t * size,uint64_t * base_pa)14071708Sstevel sbdp_get_meminfo(pnode_t nodeid, int mc, uint64_t *size, uint64_t *base_pa)
14081708Sstevel {
14091708Sstevel 	int		board, wnode;
14101708Sstevel 	int		valid;
14111708Sstevel 	mc_regs_t	mc_regs, *mc_regsp = &mc_regs;
14121708Sstevel 	uint64_t	mc_decode = 0;
14131708Sstevel 
14141708Sstevel 	if (sbdp_get_bd_and_wnode_num(nodeid, &board, &wnode) < 0)
14151708Sstevel 		return (-1);
14161708Sstevel 
14171708Sstevel 	if (mc_read_regs(nodeid, mc_regsp)) {
14181708Sstevel 		SBDP_DBG_MEM("sbdp_get_meminfo: failed to read source "
14191708Sstevel 		    "Decode Regs");
14201708Sstevel 		return (-1);
14211708Sstevel 	}
14221708Sstevel 	/*
14231708Sstevel 	 * Calculate memory size
14241708Sstevel 	 */
14251708Sstevel 	mc_decode = mc_regsp->mc_decode[mc];
14261708Sstevel 
14271708Sstevel 	/*
14281708Sstevel 	 * Check the valid bit to see if bank is there
14291708Sstevel 	 */
14301708Sstevel 	valid = mc_decode >> MC_VALID_SHIFT;
14311708Sstevel 	if (valid) {
14321708Sstevel 		*size = MC_UK2SPAN(mc_decode);
14331708Sstevel 		*base_pa = MC_BASE(mc_decode) << PHYS2UM_SHIFT;
14341708Sstevel 	}
14351708Sstevel 
14361708Sstevel 	return (0);
14371708Sstevel }
14381708Sstevel 
14391708Sstevel 
14401708Sstevel /*
14411708Sstevel  * Luckily for us mem nodes and cpu/CMP nodes are siblings.  All we need to
14421708Sstevel  * do is search in the same branch as the mem node for its sibling cpu or
14431708Sstevel  * CMP node.
14441708Sstevel  */
14451708Sstevel pnode_t
mc_get_sibling_cpu(pnode_t nodeid)14461708Sstevel mc_get_sibling_cpu(pnode_t nodeid)
14471708Sstevel {
14481708Sstevel 	int	portid;
14491708Sstevel 
14501708Sstevel 	if (prom_getprop(nodeid, OBP_PORTID, (caddr_t)&portid) < 0)
14511708Sstevel 		return (OBP_NONODE);
14521708Sstevel 
14531708Sstevel 	/*
14541708Sstevel 	 * cpus and memory are siblings so we don't need to traverse
14551708Sstevel 	 * the whole tree, just a branch
14561708Sstevel 	 */
14571708Sstevel 	return (sbdp_find_nearby_cpu_by_portid(nodeid, portid));
14581708Sstevel }
14591708Sstevel 
14601708Sstevel /*
14611708Sstevel  * Given a memory node, check it's sibling cpu or CMP to see if
14621708Sstevel  * access to mem will be ok. We need to search for the node and
14631708Sstevel  * if found get its condition.
14641708Sstevel  */
14651708Sstevel sbd_cond_t
mc_check_sibling_cpu(pnode_t nodeid)14661708Sstevel mc_check_sibling_cpu(pnode_t nodeid)
14671708Sstevel {
14681708Sstevel 	pnode_t	cpu_node;
14691708Sstevel 	sbd_cond_t	cond;
14701708Sstevel 	int		i;
14711708Sstevel 
14721708Sstevel 	cpu_node = mc_get_sibling_cpu(nodeid);
14731708Sstevel 
14741708Sstevel 	cond = sbdp_get_comp_status(cpu_node);
14751708Sstevel 
14761708Sstevel 	if (cond == SBD_COND_OK) {
14771708Sstevel 		int 		wnode;
14781708Sstevel 		int		bd;
14791708Sstevel 		int		unit;
14801708Sstevel 		int		portid;
14811708Sstevel 
14821708Sstevel 		if (sbdp_get_bd_and_wnode_num(nodeid, &bd, &wnode) < 0)
14831708Sstevel 			return (SBD_COND_UNKNOWN);
14841708Sstevel 
14851708Sstevel 		(void) prom_getprop(nodeid, OBP_PORTID, (caddr_t)&portid);
14861708Sstevel 
14871708Sstevel 		/*
14881708Sstevel 		 * Access to the memory controller should not
14891708Sstevel 		 * be attempted if any of the cores are marked
14901708Sstevel 		 * as being in reset.
14911708Sstevel 		 */
14921772Sjl139090 		for (i = 0; i < SBDP_MAX_CORES_PER_CMP; i++) {
14931708Sstevel 			unit = SG_PORTID_TO_CPU_UNIT(portid, i);
14941708Sstevel 			if (sbdp_is_cpu_present(wnode, bd, unit) &&
14951708Sstevel 			    sbdp_is_cpu_in_reset(wnode, bd, unit)) {
14961708Sstevel 				cond = SBD_COND_UNUSABLE;
14971708Sstevel 				break;
14981708Sstevel 			}
14991708Sstevel 		}
15001708Sstevel 	}
15011708Sstevel 
15021708Sstevel 	return (cond);
15031708Sstevel }
15041708Sstevel 
15051708Sstevel int
mc_read_regs(pnode_t nodeid,mc_regs_t * mc_regsp)15061708Sstevel mc_read_regs(pnode_t nodeid, mc_regs_t *mc_regsp)
15071708Sstevel {
15081708Sstevel 	int			len;
15091708Sstevel 	uint64_t		mc_addr, mask;
15101708Sstevel 	mc_regspace		reg;
15111708Sstevel 	sbd_cond_t		sibling_cpu_cond;
15121708Sstevel 	int			local_mc;
15131708Sstevel 	int			portid;
15141708Sstevel 	int			i;
15151708Sstevel 
15161708Sstevel 	if ((prom_getprop(nodeid, "portid", (caddr_t)&portid) < 0) ||
15171708Sstevel 	    (portid == -1))
15181708Sstevel 		return (-1);
15191708Sstevel 
15201708Sstevel 	/*
15211708Sstevel 	 * mc should not be accessed if their corresponding cpu
15221708Sstevel 	 * has failed.
15231708Sstevel 	 */
15241708Sstevel 	sibling_cpu_cond = mc_check_sibling_cpu(nodeid);
15251708Sstevel 
15261708Sstevel 	if ((sibling_cpu_cond == SBD_COND_FAILED) ||
15271708Sstevel 	    (sibling_cpu_cond == SBD_COND_UNUSABLE)) {
15281708Sstevel 		return (-1);
15291708Sstevel 	}
15301708Sstevel 
15311708Sstevel 	len = prom_getproplen(nodeid, "reg");
15321708Sstevel 	if (len != sizeof (mc_regspace))
15331708Sstevel 		return (-1);
15341708Sstevel 
15351708Sstevel 	if (prom_getprop(nodeid, "reg", (caddr_t)&reg) < 0)
15361708Sstevel 		return (-1);
15371708Sstevel 
15381708Sstevel 	mc_addr = ((uint64_t)reg.regspec_addr_hi) << 32;
15391708Sstevel 	mc_addr |= (uint64_t)reg.regspec_addr_lo;
15401708Sstevel 
15411708Sstevel 	/*
15421708Sstevel 	 * Make sure we don't switch cpus
15431708Sstevel 	 */
15441708Sstevel 	affinity_set(CPU_CURRENT);
15451708Sstevel 	if (portid == cpunodes[CPU->cpu_id].portid)
15461708Sstevel 		local_mc = 1;
15471708Sstevel 	else
15481708Sstevel 		local_mc = 0;
15491708Sstevel 
15501708Sstevel 	for (i = 0; i < SG_MAX_BANKS_PER_MC; i++) {
15511708Sstevel 		mask = SG_REG_2_OFFSET(i);
15521708Sstevel 
15531708Sstevel 		/*
15541708Sstevel 		 * If the memory controller is local to this CPU, we use
15551708Sstevel 		 * the special ASI to read the decode registers.
15561708Sstevel 		 * Otherwise, we load the values from a magic address in
15571708Sstevel 		 * I/O space.
15581708Sstevel 		 */
15591708Sstevel 		if (local_mc) {
15601708Sstevel 			mc_regsp->mc_decode[i] = lddmcdecode(
15611708Sstevel 			    mask & MC_OFFSET_MASK);
15621708Sstevel 		} else {
15631708Sstevel 			mc_regsp->mc_decode[i] = lddphysio(
15641708Sstevel 			    (mc_addr | mask));
15651708Sstevel 		}
15661708Sstevel 	}
15671708Sstevel 	affinity_clear();
15681708Sstevel 
15691708Sstevel 	return (0);
15701708Sstevel }
15711708Sstevel 
15721708Sstevel uint64_t
mc_get_addr(pnode_t nodeid,int mc,uint_t * asi)15731708Sstevel mc_get_addr(pnode_t nodeid, int mc, uint_t *asi)
15741708Sstevel {
15751708Sstevel 	int			len;
15761708Sstevel 	uint64_t		mc_addr, addr;
15771708Sstevel 	mc_regspace		reg;
15781708Sstevel 	int			portid;
15791708Sstevel 	int			local_mc;
15801708Sstevel 
15811708Sstevel 	if ((prom_getprop(nodeid, "portid", (caddr_t)&portid) < 0) ||
15821708Sstevel 	    (portid == -1))
15831708Sstevel 		return (-1);
15841708Sstevel 
15851708Sstevel 	len = prom_getproplen(nodeid, "reg");
15861708Sstevel 	if (len != sizeof (mc_regspace))
15871708Sstevel 		return (-1);
15881708Sstevel 
15891708Sstevel 	if (prom_getprop(nodeid, "reg", (caddr_t)&reg) < 0)
15901708Sstevel 		return (-1);
15911708Sstevel 
15921708Sstevel 	mc_addr = ((uint64_t)reg.regspec_addr_hi) << 32;
15931708Sstevel 	mc_addr |= (uint64_t)reg.regspec_addr_lo;
15941708Sstevel 
15951708Sstevel 	/*
15961708Sstevel 	 * Make sure we don't switch cpus
15971708Sstevel 	 */
15981708Sstevel 	affinity_set(CPU_CURRENT);
15991708Sstevel 	if (portid == cpunodes[CPU->cpu_id].portid)
16001708Sstevel 		local_mc = 1;
16011708Sstevel 	else
16021708Sstevel 		local_mc = 0;
16031708Sstevel 
16041708Sstevel 	if (local_mc) {
16051708Sstevel 		*asi = ASI_MC_DECODE;
16061708Sstevel 		addr = SG_REG_2_OFFSET(mc) & MC_OFFSET_MASK;
16071708Sstevel 	} else {
16081708Sstevel 		*asi = ASI_IO;
16091708Sstevel 		addr = SG_REG_2_OFFSET(mc) | mc_addr;
16101708Sstevel 	}
16111708Sstevel 	affinity_clear();
16121708Sstevel 
16131708Sstevel 	return (addr);
16141708Sstevel }
16151708Sstevel 
16161708Sstevel /* ARGSUSED */
16171708Sstevel int
sbdp_mem_add_span(sbdp_handle_t * hp,uint64_t address,uint64_t size)16181708Sstevel sbdp_mem_add_span(sbdp_handle_t *hp, uint64_t address, uint64_t size)
16191708Sstevel {
16201708Sstevel 	return (0);
16211708Sstevel }
16221708Sstevel 
16231708Sstevel int
sbdp_mem_del_span(sbdp_handle_t * hp,uint64_t address,uint64_t size)16241708Sstevel sbdp_mem_del_span(sbdp_handle_t *hp, uint64_t address, uint64_t size)
16251708Sstevel {
16261708Sstevel 	pfn_t		 basepfn = (pfn_t)(address >> PAGESHIFT);
16271708Sstevel 	pgcnt_t		 npages = (pgcnt_t)(size >> PAGESHIFT);
16281708Sstevel 
16291708Sstevel 	if (size > 0) {
16301708Sstevel 		int rv;
16311708Sstevel 		rv = kcage_range_delete_post_mem_del(basepfn, npages);
16321708Sstevel 		if (rv != 0) {
16331708Sstevel 			cmn_err(CE_WARN,
16341708Sstevel 			    "unexpected kcage_range_delete_post_mem_del"
16351708Sstevel 			    " return value %d", rv);
16361708Sstevel 			sbdp_set_err(hp->h_err, ESGT_INTERNAL, NULL);
16371708Sstevel 			return (-1);
16381708Sstevel 		}
16391708Sstevel 	}
16401708Sstevel 	return (0);
16411708Sstevel }
16421708Sstevel 
16431708Sstevel /*
16441708Sstevel  * This routine gets the size including the
16451708Sstevel  * bad banks
16461708Sstevel  */
16471708Sstevel int
sbdp_get_mem_size(sbdp_handle_t * hp)16481708Sstevel sbdp_get_mem_size(sbdp_handle_t *hp)
16491708Sstevel {
16501708Sstevel 	uint64_t	size = 0;
16511708Sstevel 	struct memlist	*mlist, *ml;
16521708Sstevel 
16531708Sstevel 	mlist = sbdp_get_memlist(hp, (dev_info_t *)NULL);
16541708Sstevel 
1655*11474SJonathan.Adams@Sun.COM 	for (ml = mlist; ml; ml = ml->ml_next)
1656*11474SJonathan.Adams@Sun.COM 		size += ml->ml_size;
16571708Sstevel 
16581708Sstevel 	(void) sbdp_del_memlist(hp, mlist);
16591708Sstevel 
16601708Sstevel 	SBDP_DBG_MEM("sbdp_get_mem_size: size 0x%" PRIx64 "\n", size);
16611708Sstevel 
16621708Sstevel 	return (btop(size));
16631708Sstevel }
16641708Sstevel 
16651708Sstevel /*
16661708Sstevel  * This function compares the list of banks passed with the banks
16671708Sstevel  * in the segment
16681708Sstevel  */
16691708Sstevel int
sbdp_check_seg_with_banks(sbdp_seg_t * seg,sbdp_bank_t * banks)16701708Sstevel sbdp_check_seg_with_banks(sbdp_seg_t *seg, sbdp_bank_t *banks)
16711708Sstevel {
16721708Sstevel 	sbdp_bank_t	*cur_bank, *bank;
16731708Sstevel 	int		i = 0;
16741708Sstevel 
16751708Sstevel 	for (cur_bank = seg->banks; cur_bank; cur_bank = cur_bank->seg_next) {
16761708Sstevel 		for (bank = banks; bank; bank = bank->bd_next) {
16771708Sstevel 			if (!bank->valid)
16781708Sstevel 				continue;
16791708Sstevel 
16801708Sstevel 			if (cur_bank == bank) {
16811708Sstevel 				i++;
16821708Sstevel 			}
16831708Sstevel 		}
16841708Sstevel 	}
16851708Sstevel 
16861708Sstevel 	SBDP_DBG_MEM("banks found = %d total banks = %d\n", i, seg->nbanks);
16871708Sstevel 	/*
16881708Sstevel 	 * If we find the same num of banks that are equal, then this segment
16891708Sstevel 	 * is not interleaved across boards
16901708Sstevel 	 */
16911708Sstevel 	if (i == seg->nbanks)
16921708Sstevel 		return (0);
16931708Sstevel 
16941708Sstevel 	return (1);
16951708Sstevel }
16961708Sstevel 
16971708Sstevel 
16981708Sstevel /*
16991708Sstevel  * This routine determines if any of the memory banks on the board
17001708Sstevel  * participate in across board memory interleaving
17011708Sstevel  */
17021708Sstevel int
sbdp_isinterleaved(sbdp_handle_t * hp,dev_info_t * dip)17031708Sstevel sbdp_isinterleaved(sbdp_handle_t *hp, dev_info_t *dip)
17041708Sstevel {
17051708Sstevel 	_NOTE(ARGUNUSED(dip))
17061708Sstevel 
17071708Sstevel 	sbdp_bank_t	*bankp;
17081708Sstevel 	int		wnode, board;
17091708Sstevel 	int		is_interleave = 0;
17101708Sstevel 	sbdp_bd_t	*bdp;
17111708Sstevel 	uint64_t	base;
17121708Sstevel 	sbdp_seg_t	*seg;
17131708Sstevel 
17141708Sstevel 	board = hp->h_board;
17151708Sstevel 	wnode = hp->h_wnode;
17161708Sstevel 
17171708Sstevel #ifdef DEBUG
17181708Sstevel 	sbdp_print_all_segs();
17191708Sstevel #endif
17201708Sstevel 	/*
17211708Sstevel 	 * Get the banks for this board
17221708Sstevel 	 */
17231708Sstevel 	bdp = sbdp_get_bd_info(wnode, board);
17241708Sstevel 
17251708Sstevel 	if (bdp == NULL)
17261708Sstevel 		return (-1);
17271708Sstevel 
17281708Sstevel 	/*
17291708Sstevel 	 * Search for the first bank with valid memory
17301708Sstevel 	 */
17311708Sstevel 	for (bankp = bdp->banks; bankp; bankp = bankp->bd_next)
17321708Sstevel 		if (bankp->valid)
17331708Sstevel 			break;
17341708Sstevel 
17351708Sstevel 	/*
17361708Sstevel 	 * If there are no banks in the board, then the board is
17371708Sstevel 	 * not interleaved across boards
17381708Sstevel 	 */
17391708Sstevel 	if (bankp == NULL) {
17401708Sstevel 		return (0);
17411708Sstevel 	}
17421708Sstevel 
17431708Sstevel 	base = bankp->um & ~(bankp->uk);
17441708Sstevel 
17451708Sstevel 	/*
17461708Sstevel 	 * Find the segment for the first bank
17471708Sstevel 	 */
17481708Sstevel 	if ((seg = sbdp_get_seg(base)) == NULL) {
17491708Sstevel 		/*
17501708Sstevel 		 * Something bad has happened.
17511708Sstevel 		 */
17521708Sstevel 		return (-1);
17531708Sstevel 	}
17541708Sstevel 	/*
17551708Sstevel 	 * Make sure that this segment is only composed of the banks
17561708Sstevel 	 * in this board. If one is missing or we have an extra one
17571708Sstevel 	 * the board is interleaved across boards
17581708Sstevel 	 */
17591708Sstevel 	is_interleave = sbdp_check_seg_with_banks(seg, bdp->banks);
17601708Sstevel 
17611708Sstevel 	SBDP_DBG_MEM("interleave is %d\n", is_interleave);
17621708Sstevel 
17631708Sstevel 	return (is_interleave);
17641708Sstevel }
17651708Sstevel 
17661708Sstevel 
17671708Sstevel /*
17681708Sstevel  * Each node has 4 logical banks.  This routine adds all the banks (including
17691708Sstevel  * the invalid ones to the passed list. Note that we use the bd list and not
17701708Sstevel  * the seg list
17711708Sstevel  */
17721708Sstevel int
sbdp_add_nodes_banks(pnode_t node,sbdp_bank_t ** banks)17731708Sstevel sbdp_add_nodes_banks(pnode_t node, sbdp_bank_t **banks)
17741708Sstevel {
17751708Sstevel 	int		i;
17761708Sstevel 	mc_regs_t	regs;
17771708Sstevel 	uint64_t	*mc_decode;
17781708Sstevel 	sbdp_bank_t 	*bank;
17791708Sstevel 
17801708Sstevel 	if (mc_read_regs(node, &regs) == -1)
17811708Sstevel 		return (-1);
17821708Sstevel 
17831708Sstevel 	mc_decode = regs.mc_decode;
17841708Sstevel 
17851708Sstevel 	for (i = 0; i < SBDP_MAX_MCS_PER_NODE; i++) {
17861708Sstevel 		/*
17871708Sstevel 		 * This creates the mem for the new member of the list
17881708Sstevel 		 */
17891708Sstevel 		sbdp_fill_bank_info(mc_decode[i], &bank);
17901708Sstevel 
17911708Sstevel 		SBDP_DBG_MEM("adding bank %d\n", bank->id);
17921708Sstevel 
17931708Sstevel 		/*
17941708Sstevel 		 * Insert bank into the beginning of the list
17951708Sstevel 		 */
17961708Sstevel 		bank->bd_next = *banks;
17971708Sstevel 		*banks = bank;
17981708Sstevel 
17991708Sstevel 		/*
18001708Sstevel 		 * Add this bank into its corresponding
18011708Sstevel 		 * segment
18021708Sstevel 		 */
18031708Sstevel 		sbdp_add_bank_to_seg(bank);
18041708Sstevel 	}
18051708Sstevel 	return (0);
18061708Sstevel }
18071708Sstevel 
18081708Sstevel /*
18091708Sstevel  * given the info, create a new bank node and set the info
18101708Sstevel  * as appropriate. We allocate the memory for the bank. It is
18111708Sstevel  * up to the caller to ensure the mem is freed
18121708Sstevel  */
18131708Sstevel void
sbdp_fill_bank_info(uint64_t mc_decode,sbdp_bank_t ** bank)18141708Sstevel sbdp_fill_bank_info(uint64_t mc_decode, sbdp_bank_t **bank)
18151708Sstevel {
18161708Sstevel 	static int	id = 0;
18171708Sstevel 	sbdp_bank_t	*new;
18181708Sstevel 
18191708Sstevel 	new = kmem_zalloc(sizeof (sbdp_bank_t), KM_SLEEP);
18201708Sstevel 
18211708Sstevel 	new->id = id++;
18221708Sstevel 	new->valid = (mc_decode >> MC_VALID_SHIFT);
18231708Sstevel 	new->uk = MC_UK(mc_decode);
18241708Sstevel 	new->um = MC_UM(mc_decode);
18251708Sstevel 	new->lk = MC_LK(mc_decode);
18261708Sstevel 	new->lm = MC_LM(mc_decode);
18271708Sstevel 	new->bd_next = NULL;
18281708Sstevel 	new->seg_next = NULL;
18291708Sstevel 
18301708Sstevel 	*bank = new;
18311708Sstevel }
18321708Sstevel 
18331708Sstevel /*
18341708Sstevel  * Each bd has the potential of having mem banks on it.  The banks
18351708Sstevel  * may be empty or not.  This routine gets all the mem banks
18361708Sstevel  * for this bd
18371708Sstevel  */
18381708Sstevel void
sbdp_init_bd_banks(sbdp_bd_t * bdp)18391708Sstevel sbdp_init_bd_banks(sbdp_bd_t *bdp)
18401708Sstevel {
18411708Sstevel 	int		i, nmem;
18421708Sstevel 	pnode_t		*lists;
18431708Sstevel 
18441708Sstevel 	lists = bdp->nodes;
18451708Sstevel 	nmem = bdp->nnum;
18461708Sstevel 
18471708Sstevel 	if (bdp->banks != NULL) {
18481708Sstevel 		return;
18491708Sstevel 	}
18501708Sstevel 
18511708Sstevel 	bdp->banks = NULL;
18521708Sstevel 
18531708Sstevel 	for (i = 0; i < nmem; i++) {
18541708Sstevel 		(void) sbdp_add_nodes_banks(lists[i], &bdp->banks);
18551708Sstevel 	}
18561708Sstevel }
18571708Sstevel 
18581708Sstevel /*
18591708Sstevel  * swap the list of banks for the 2 boards
18601708Sstevel  */
18611708Sstevel void
sbdp_swap_list_of_banks(sbdp_bd_t * bdp1,sbdp_bd_t * bdp2)18621708Sstevel sbdp_swap_list_of_banks(sbdp_bd_t *bdp1, sbdp_bd_t *bdp2)
18631708Sstevel {
18641708Sstevel 	sbdp_bank_t	*tmp_ptr;
18651708Sstevel 
18661708Sstevel 	if ((bdp1 == NULL) || (bdp2 == NULL))
18671708Sstevel 		return;
18681708Sstevel 
18691708Sstevel 	tmp_ptr = bdp1->banks;
18701708Sstevel 	bdp1->banks = bdp2->banks;
18711708Sstevel 	bdp2->banks = tmp_ptr;
18721708Sstevel }
18731708Sstevel 
18741708Sstevel /*
18751708Sstevel  * free all the banks on the board.  Note that a bank node belongs
18761708Sstevel  * to 2 lists. The first list is the board list. The second one is
18771708Sstevel  * the seg list. We only need to remove the bank from both lists but only
18781708Sstevel  * free the node once.
18791708Sstevel  */
18801708Sstevel void
sbdp_fini_bd_banks(sbdp_bd_t * bdp)18811708Sstevel sbdp_fini_bd_banks(sbdp_bd_t *bdp)
18821708Sstevel {
18831708Sstevel 	sbdp_bank_t	*bkp, *nbkp;
18841708Sstevel 
18851708Sstevel 	for (bkp = bdp->banks; bkp; ) {
18861708Sstevel 		/*
18871708Sstevel 		 * Remove the bank from the seg list first
18881708Sstevel 		 */
18891708Sstevel 		SBDP_DBG_MEM("Removing bank %d\n", bkp->id);
18901708Sstevel 		sbdp_remove_bank_from_seg(bkp);
18911708Sstevel 		nbkp = bkp->bd_next;
18921708Sstevel 		bkp->bd_next = NULL;
18931708Sstevel 		kmem_free(bkp, sizeof (sbdp_bank_t));
18941708Sstevel 
18951708Sstevel 		bkp = nbkp;
18961708Sstevel 	}
18971708Sstevel 	bdp->banks = NULL;
18981708Sstevel }
18991708Sstevel 
19001708Sstevel #ifdef DEBUG
19011708Sstevel void
sbdp_print_bd_banks(sbdp_bd_t * bdp)19021708Sstevel sbdp_print_bd_banks(sbdp_bd_t *bdp)
19031708Sstevel {
19041708Sstevel 	sbdp_bank_t	*bp;
19051708Sstevel 	int		i;
19061708Sstevel 
19071708Sstevel 	SBDP_DBG_MEM("BOARD %d\n", bdp->bd);
19081708Sstevel 
19091708Sstevel 	for (bp = bdp->banks, i = 0; bp; bp = bp->bd_next, i++) {
19101708Sstevel 		SBDP_DBG_MEM("BANK [%d]:\n", bp->id);
19111708Sstevel 		SBDP_DBG_MEM("\tvalid %d\tuk 0x%x\tum 0x%x\tlk 0x%x"
19121708Sstevel 		    "\tlm 0x%x\n", bp->valid, bp->uk, bp->um,
19131708Sstevel 		    bp->lk, bp->lm);
19141708Sstevel 	}
19151708Sstevel }
19161708Sstevel 
19171708Sstevel void
sbdp_print_all_segs(void)19181708Sstevel sbdp_print_all_segs(void)
19191708Sstevel {
19201708Sstevel 	sbdp_seg_t	*cur_seg;
19211708Sstevel 
19221708Sstevel 	for (cur_seg = sys_seg; cur_seg; cur_seg = cur_seg->next)
19231708Sstevel 		sbdp_print_seg(cur_seg);
19241708Sstevel }
19251708Sstevel 
19261708Sstevel void
sbdp_print_seg(sbdp_seg_t * seg)19271708Sstevel sbdp_print_seg(sbdp_seg_t *seg)
19281708Sstevel {
19291708Sstevel 	sbdp_bank_t	*bp;
19301708Sstevel 	int		i;
19311708Sstevel 
19321708Sstevel 	SBDP_DBG_MEM("SEG %d\n", seg->id);
19331708Sstevel 
19341708Sstevel 	for (bp = seg->banks, i = 0; bp; bp = bp->seg_next, i++) {
19351708Sstevel 		SBDP_DBG_MEM("BANK [%d]:\n", bp->id);
19361708Sstevel 		SBDP_DBG_MEM("\tvalid %d\tuk 0x%x\tum 0x%x\tlk 0x%x"
19371708Sstevel 		    "\tlm 0x%x\n", bp->valid, bp->uk, bp->um,
19381708Sstevel 		    bp->lk, bp->lm);
19391708Sstevel 	}
19401708Sstevel }
19411708Sstevel #endif
19421708Sstevel 
19431708Sstevel void
sbdp_add_bank_to_seg(sbdp_bank_t * bank)19441708Sstevel sbdp_add_bank_to_seg(sbdp_bank_t *bank)
19451708Sstevel {
19461708Sstevel 	uint64_t	base;
19471708Sstevel 	sbdp_seg_t	*cur_seg;
19481708Sstevel 	static int	id = 0;
19491708Sstevel 
19501708Sstevel 	/*
19511708Sstevel 	 * if we got an invalid bank just skip it
19521708Sstevel 	 */
19531708Sstevel 	if (bank == NULL || !bank->valid)
19541708Sstevel 		return;
19551708Sstevel 	base = bank->um & ~(bank->uk);
19561708Sstevel 
19571708Sstevel 	if ((cur_seg = sbdp_get_seg(base)) == NULL) {
19581708Sstevel 		/*
19591708Sstevel 		 * This bank is part of a new segment, so create
19601708Sstevel 		 * a struct for it and added to the list of segments
19611708Sstevel 		 */
19621708Sstevel 		cur_seg = kmem_zalloc(sizeof (sbdp_seg_t), KM_SLEEP);
19631708Sstevel 		cur_seg->id = id++;
19641708Sstevel 		cur_seg->base = base;
19651708Sstevel 		cur_seg->size = ((bank->uk +1) << PHYS2UM_SHIFT);
19661708Sstevel 		cur_seg->intlv = ((bank->lk ^ 0xF) + 1);
19671708Sstevel 		/*
19681708Sstevel 		 * add to the seg list
19691708Sstevel 		 */
19701708Sstevel 		cur_seg->next = sys_seg;
19711708Sstevel 		sys_seg = cur_seg;
19721708Sstevel 	}
19731708Sstevel 
19741708Sstevel 	cur_seg->nbanks++;
19751708Sstevel 	/*
19761708Sstevel 	 * add bank into segs bank list.  Note we add at the head
19771708Sstevel 	 */
19781708Sstevel 	bank->seg_next = cur_seg->banks;
19791708Sstevel 	cur_seg->banks = bank;
19801708Sstevel }
19811708Sstevel 
19821708Sstevel /*
19831708Sstevel  * Remove this segment from the seg list
19841708Sstevel  */
19851708Sstevel void
sbdp_rm_seg(sbdp_seg_t * seg)19861708Sstevel sbdp_rm_seg(sbdp_seg_t *seg)
19871708Sstevel {
19881708Sstevel 	sbdp_seg_t	**curpp, *curp;
19891708Sstevel 
19901708Sstevel 	curpp = &sys_seg;
19911708Sstevel 
19921708Sstevel 	while ((curp = *curpp) != NULL) {
19931708Sstevel 		if (curp == seg) {
19941708Sstevel 			*curpp = curp->next;
19951708Sstevel 			break;
19961708Sstevel 		}
19971708Sstevel 		curpp = &curp->next;
19981708Sstevel 	}
19991708Sstevel 
20001708Sstevel 	if (curp != NULL) {
20011708Sstevel 		kmem_free(curp, sizeof (sbdp_seg_t));
20021708Sstevel 		curp = NULL;
20031708Sstevel 	}
20041708Sstevel }
20051708Sstevel 
20061708Sstevel /*
20071708Sstevel  * remove this bank from its seg list
20081708Sstevel  */
20091708Sstevel void
sbdp_remove_bank_from_seg(sbdp_bank_t * bank)20101708Sstevel sbdp_remove_bank_from_seg(sbdp_bank_t *bank)
20111708Sstevel {
20121708Sstevel 	uint64_t	base;
20131708Sstevel 	sbdp_seg_t	*cur_seg;
20141708Sstevel 	sbdp_bank_t	**curpp, *curp;
20151708Sstevel 
20161708Sstevel 	/*
20171708Sstevel 	 * if we got an invalid bank just skip it
20181708Sstevel 	 */
20191708Sstevel 	if (bank == NULL || !bank->valid)
20201708Sstevel 		return;
20211708Sstevel 	base = bank->um & ~(bank->uk);
20221708Sstevel 
20231708Sstevel 	/*
20241708Sstevel 	 * If the bank doesn't belong to any seg just return
20251708Sstevel 	 */
20261708Sstevel 	if ((cur_seg = sbdp_get_seg(base)) == NULL) {
20271708Sstevel 		SBDP_DBG_MEM("bank %d with no segment\n", bank->id);
20281708Sstevel 		return;
20291708Sstevel 	}
20301708Sstevel 
20311708Sstevel 	/*
20321708Sstevel 	 * Find bank in the seg
20331708Sstevel 	 */
20341708Sstevel 	curpp = &cur_seg->banks;
20351708Sstevel 
20361708Sstevel 	while ((curp = *curpp) != NULL) {
20371708Sstevel 		if (curp->id == bank->id) {
20381708Sstevel 			/*
20391708Sstevel 			 * found node, remove it
20401708Sstevel 			 */
20411708Sstevel 			*curpp = curp->seg_next;
20421708Sstevel 			break;
20431708Sstevel 		}
20441708Sstevel 		curpp = &curp->seg_next;
20451708Sstevel 	}
20461708Sstevel 
20471708Sstevel 	if (curp != NULL) {
20481708Sstevel 		cur_seg->nbanks--;
20491708Sstevel 	}
20501708Sstevel 
20511708Sstevel 	if (cur_seg->nbanks == 0) {
20521708Sstevel 		/*
20531708Sstevel 		 * No banks left on this segment, remove the segment
20541708Sstevel 		 */
20551708Sstevel 		SBDP_DBG_MEM("No banks left in this segment, removing it\n");
20561708Sstevel 		sbdp_rm_seg(cur_seg);
20571708Sstevel 	}
20581708Sstevel }
20591708Sstevel 
20601708Sstevel sbdp_seg_t *
sbdp_get_seg(uint64_t base)20611708Sstevel sbdp_get_seg(uint64_t base)
20621708Sstevel {
20631708Sstevel 	sbdp_seg_t	*cur_seg;
20641708Sstevel 
20651708Sstevel 	for (cur_seg = sys_seg; cur_seg; cur_seg = cur_seg->next) {
20661708Sstevel 		if (cur_seg-> base == base)
20671708Sstevel 			break;
20681708Sstevel 	}
20691708Sstevel 
20701708Sstevel 	return (cur_seg);
20711708Sstevel }
20721708Sstevel 
20731708Sstevel #ifdef DEBUG
20741708Sstevel int
sbdp_passthru_readmem(sbdp_handle_t * hp,void * arg)20751708Sstevel sbdp_passthru_readmem(sbdp_handle_t *hp, void *arg)
20761708Sstevel {
20771708Sstevel 	_NOTE(ARGUNUSED(hp))
20781708Sstevel 	_NOTE(ARGUNUSED(arg))
20791708Sstevel 
20801708Sstevel 	struct memlist	*ml;
20811708Sstevel 	uint64_t	src_pa;
20821708Sstevel 	uint64_t	dst_pa;
20831708Sstevel 	uint64_t	dst;
20841708Sstevel 
20851708Sstevel 
20861708Sstevel 	dst_pa = va_to_pa(&dst);
20871708Sstevel 
20881708Sstevel 	memlist_read_lock();
2089*11474SJonathan.Adams@Sun.COM 	for (ml = phys_install; ml; ml = ml->ml_next) {
20901708Sstevel 		uint64_t	nbytes;
20911708Sstevel 
2092*11474SJonathan.Adams@Sun.COM 		src_pa = ml->ml_address;
2093*11474SJonathan.Adams@Sun.COM 		nbytes = ml->ml_size;
20941708Sstevel 
20951708Sstevel 		while (nbytes != 0ull) {
20961708Sstevel 
20971708Sstevel 			/* copy 32 bytes at src_pa to dst_pa */
20981708Sstevel 			bcopy32_il(src_pa, dst_pa);
20991708Sstevel 
21001708Sstevel 			/* increment by 32 bytes */
21011708Sstevel 			src_pa += (4 * sizeof (uint64_t));
21021708Sstevel 
21031708Sstevel 			/* decrement by 32 bytes */
21041708Sstevel 			nbytes -= (4 * sizeof (uint64_t));
21051708Sstevel 		}
21061708Sstevel 	}
21071708Sstevel 	memlist_read_unlock();
21081708Sstevel 
21091708Sstevel 	return (0);
21101708Sstevel }
21111708Sstevel 
21121708Sstevel static int
isdigit(int ch)21131708Sstevel isdigit(int ch)
21141708Sstevel {
21151708Sstevel 	return (ch >= '0' && ch <= '9');
21161708Sstevel }
21171708Sstevel 
21181708Sstevel #define	isspace(c)	((c) == ' ' || (c) == '\t' || (c) == '\n')
21191708Sstevel 
21201708Sstevel int
sbdp_strtoi(char * p,char ** pos)21211708Sstevel sbdp_strtoi(char *p, char **pos)
21221708Sstevel {
21231708Sstevel 	int n;
21241708Sstevel 	int c, neg = 0;
21251708Sstevel 
21261708Sstevel 	if (!isdigit(c = *p)) {
21271708Sstevel 		while (isspace(c))
21281708Sstevel 			c = *++p;
21291708Sstevel 		switch (c) {
21301708Sstevel 			case '-':
21311708Sstevel 				neg++;
21321708Sstevel 				/* FALLTHROUGH */
21331708Sstevel 			case '+':
21341708Sstevel 				c = *++p;
21351708Sstevel 		}
21361708Sstevel 		if (!isdigit(c)) {
21371708Sstevel 			if (pos != NULL)
21381708Sstevel 				*pos = p;
21391708Sstevel 			return (0);
21401708Sstevel 		}
21411708Sstevel 	}
21421708Sstevel 	for (n = '0' - c; isdigit(c = *++p); ) {
21431708Sstevel 		n *= 10; /* two steps to avoid unnecessary overflow */
21441708Sstevel 		n += '0' - c; /* accum neg to avoid surprises at MAX */
21451708Sstevel 	}
21461708Sstevel 	if (pos != NULL)
21471708Sstevel 		*pos = p;
21481708Sstevel 	return (neg ? n : -n);
21491708Sstevel }
21501708Sstevel 
21511708Sstevel int
sbdp_passthru_prep_script(sbdp_handle_t * hp,void * arg)21521708Sstevel sbdp_passthru_prep_script(sbdp_handle_t *hp, void *arg)
21531708Sstevel {
21541708Sstevel 	int			board, i;
21551708Sstevel 	sbdp_bd_t		*t_bdp, *s_bdp;
21561708Sstevel 	char			*opts;
21571708Sstevel 	int			t_board;
21581708Sstevel 	sbdp_rename_script_t	*rsbuffer;
21591708Sstevel 	sbdp_cr_handle_t	*cph;
21601708Sstevel 	int			scriptlen, size;
21611708Sstevel 
21621708Sstevel 	opts = (char *)arg;
21631708Sstevel 	board = hp->h_board;
21641708Sstevel 
21651708Sstevel 	opts += strlen("prep-script=");
21661708Sstevel 	t_board = sbdp_strtoi(opts, NULL);
21671708Sstevel 
21681708Sstevel 	cph =  kmem_zalloc(sizeof (sbdp_cr_handle_t), KM_SLEEP);
21691708Sstevel 
21701708Sstevel 	size = sizeof (sbdp_rename_script_t) * SBDP_RENAME_MAXOP;
21711708Sstevel 	rsbuffer = kmem_zalloc(size, KM_SLEEP);
21721708Sstevel 
21731708Sstevel 	s_bdp = sbdp_get_bd_info(hp->h_wnode, board);
21741708Sstevel 	t_bdp = sbdp_get_bd_info(hp->h_wnode, t_board);
21751708Sstevel 
21761708Sstevel 	cph->s_bdp = s_bdp;
21771708Sstevel 	cph->t_bdp = t_bdp;
21781708Sstevel 	cph->script = rsbuffer;
21791708Sstevel 
21801708Sstevel 	affinity_set(CPU_CURRENT);
21811708Sstevel 	scriptlen = sbdp_prep_rename_script(cph);
21821708Sstevel 
21831708Sstevel 	if (scriptlen <= 0) {
21841708Sstevel 		cmn_err(CE_WARN,
21851708Sstevel 		"sbdp failed to prep for copy-rename");
21861708Sstevel 	}
21871708Sstevel 	prom_printf("SCRIPT from board %d to board %d ->\n", board, t_board);
21881708Sstevel 	for (i = 0;  i < (scriptlen / (sizeof (sbdp_rename_script_t))); i++) {
21891708Sstevel 		prom_printf("0x%lx = 0x%lx, asi 0x%x\n",
21901708Sstevel 		    rsbuffer[i].masr_addr, rsbuffer[i].masr, rsbuffer[i].asi);
21911708Sstevel 	}
21921708Sstevel 	prom_printf("\n");
21931708Sstevel 
21941708Sstevel 	affinity_clear();
21951708Sstevel 	kmem_free(rsbuffer, size);
21961708Sstevel 	kmem_free(cph, sizeof (sbdp_cr_handle_t));
21971708Sstevel 
21981708Sstevel 	return (0);
21991708Sstevel }
22001708Sstevel #endif
2201