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, ®s)) {
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)®) < 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, ®_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], ®s)) {
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)®) < 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)®) < 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, ®s) == -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