103831d35Sstevel /*
203831d35Sstevel * CDDL HEADER START
303831d35Sstevel *
403831d35Sstevel * The contents of this file are subject to the terms of the
503831d35Sstevel * Common Development and Distribution License (the "License").
603831d35Sstevel * You may not use this file except in compliance with the License.
703831d35Sstevel *
803831d35Sstevel * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
903831d35Sstevel * or http://www.opensolaris.org/os/licensing.
1003831d35Sstevel * See the License for the specific language governing permissions
1103831d35Sstevel * and limitations under the License.
1203831d35Sstevel *
1303831d35Sstevel * When distributing Covered Code, include this CDDL HEADER in each
1403831d35Sstevel * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
1503831d35Sstevel * If applicable, add the following below this CDDL HEADER, with the
1603831d35Sstevel * fields enclosed by brackets "[]" replaced with your own identifying
1703831d35Sstevel * information: Portions Copyright [yyyy] [name of copyright owner]
1803831d35Sstevel *
1903831d35Sstevel * CDDL HEADER END
2003831d35Sstevel */
2103831d35Sstevel
2203831d35Sstevel /*
23*56f33205SJonathan Adams * Copyright 2010 Sun Microsystems, Inc. All rights reserved.
2403831d35Sstevel * Use is subject to license terms.
2503831d35Sstevel */
2603831d35Sstevel
2703831d35Sstevel /*
2803831d35Sstevel * memory management for serengeti dr memory
2903831d35Sstevel */
3003831d35Sstevel
3103831d35Sstevel #include <sys/obpdefs.h>
3203831d35Sstevel #include <sys/types.h>
3303831d35Sstevel #include <sys/conf.h>
3403831d35Sstevel #include <sys/ddi.h>
3503831d35Sstevel #include <sys/cpuvar.h>
3603831d35Sstevel #include <sys/memlist_impl.h>
3703831d35Sstevel #include <sys/machsystm.h>
3803831d35Sstevel #include <sys/promif.h>
3903831d35Sstevel #include <sys/mem_cage.h>
4003831d35Sstevel #include <sys/kmem.h>
4103831d35Sstevel #include <sys/note.h>
4203831d35Sstevel #include <sys/lgrp.h>
4303831d35Sstevel
4403831d35Sstevel #include <sys/sbd_ioctl.h>
4503831d35Sstevel #include <sys/sbd.h>
4603831d35Sstevel #include <sys/sbdp_priv.h>
4703831d35Sstevel #include <sys/sbdp_mem.h>
4803831d35Sstevel #include <sys/sun4asi.h>
4903831d35Sstevel #include <sys/cheetahregs.h>
5003831d35Sstevel #include <sys/cpu_module.h>
5103831d35Sstevel #include <sys/esunddi.h>
5203831d35Sstevel
5303831d35Sstevel #include <vm/page.h>
5403831d35Sstevel
5503831d35Sstevel static int sbdp_get_meminfo(pnode_t, int, uint64_t *, uint64_t *);
5603831d35Sstevel int mc_read_regs(pnode_t, mc_regs_t *);
5703831d35Sstevel uint64_t mc_get_addr(pnode_t, int, uint_t *);
5803831d35Sstevel static pnode_t mc_get_sibling_cpu(pnode_t nodeid);
5903831d35Sstevel static int mc_get_sibling_cpu_impl(pnode_t nodeid);
6003831d35Sstevel static sbd_cond_t mc_check_sibling_cpu(pnode_t nodeid);
6103831d35Sstevel static void _sbdp_copy_rename_end(void);
6203831d35Sstevel static int sbdp_copy_rename__relocatable(sbdp_cr_handle_t *,
6303831d35Sstevel struct memlist *, sbdp_rename_script_t *);
6403831d35Sstevel static int sbdp_prep_rename_script(sbdp_cr_handle_t *);
6503831d35Sstevel static int sbdp_get_lowest_addr_in_node(pnode_t, uint64_t *);
6603831d35Sstevel
6703831d35Sstevel extern void bcopy32_il(uint64_t, uint64_t);
6803831d35Sstevel extern void flush_ecache_il(uint64_t physaddr, size_t size, size_t linesize);
6903831d35Sstevel extern uint64_t lddphys_il(uint64_t physaddr);
7003831d35Sstevel extern uint64_t ldxasi_il(uint64_t physaddr, uint_t asi);
7103831d35Sstevel extern void sbdp_exec_script_il(sbdp_rename_script_t *rsp);
7203831d35Sstevel void sbdp_fill_bank_info(uint64_t, sbdp_bank_t **);
7303831d35Sstevel int sbdp_add_nodes_banks(pnode_t node, sbdp_bank_t **banks);
7403831d35Sstevel void sbdp_add_bank_to_seg(sbdp_bank_t *);
7503831d35Sstevel void sbdp_remove_bank_from_seg(sbdp_bank_t *);
7603831d35Sstevel uint64_t sbdp_determine_slice(sbdp_handle_t *);
7703831d35Sstevel sbdp_seg_t *sbdp_get_seg(uint64_t);
7803831d35Sstevel #ifdef DEBUG
7903831d35Sstevel void sbdp_print_seg(sbdp_seg_t *);
8003831d35Sstevel #endif
8103831d35Sstevel
8203831d35Sstevel /*
8303831d35Sstevel * Head to the system segments link list
8403831d35Sstevel */
8503831d35Sstevel sbdp_seg_t *sys_seg = NULL;
8603831d35Sstevel
8703831d35Sstevel uint64_t
sbdp_determine_slice(sbdp_handle_t * hp)8803831d35Sstevel sbdp_determine_slice(sbdp_handle_t *hp)
8903831d35Sstevel {
9003831d35Sstevel int size;
9103831d35Sstevel
9203831d35Sstevel size = sbdp_get_mem_size(hp);
9303831d35Sstevel
9403831d35Sstevel if (size <= SG_SLICE_16G_SIZE) {
9503831d35Sstevel return (SG_SLICE_16G_SIZE);
9603831d35Sstevel } else if (size <= SG_SLICE_32G_SIZE) {
9703831d35Sstevel return (SG_SLICE_32G_SIZE);
9803831d35Sstevel } else {
9903831d35Sstevel return (SG_SLICE_64G_SIZE);
10003831d35Sstevel }
10103831d35Sstevel }
10203831d35Sstevel
10303831d35Sstevel /* ARGSUSED */
10403831d35Sstevel int
sbdp_get_mem_alignment(sbdp_handle_t * hp,dev_info_t * dip,uint64_t * align)10503831d35Sstevel sbdp_get_mem_alignment(sbdp_handle_t *hp, dev_info_t *dip, uint64_t *align)
10603831d35Sstevel {
10703831d35Sstevel *align = sbdp_determine_slice(hp);
10803831d35Sstevel return (0);
10903831d35Sstevel }
11003831d35Sstevel
11103831d35Sstevel
11203831d35Sstevel void
sbdp_memlist_dump(struct memlist * mlist)11303831d35Sstevel sbdp_memlist_dump(struct memlist *mlist)
11403831d35Sstevel {
11503831d35Sstevel register struct memlist *ml;
11603831d35Sstevel
11703831d35Sstevel if (mlist == NULL) {
11803831d35Sstevel SBDP_DBG_MEM("memlist> EMPTY\n");
11903831d35Sstevel } else {
120*56f33205SJonathan Adams for (ml = mlist; ml; ml = ml->ml_next)
12103831d35Sstevel SBDP_DBG_MEM("memlist> 0x%" PRIx64", 0x%" PRIx64"\n",
122*56f33205SJonathan Adams ml->ml_address, ml->ml_size);
12303831d35Sstevel }
12403831d35Sstevel }
12503831d35Sstevel
12603831d35Sstevel struct mem_arg {
12703831d35Sstevel int board;
12803831d35Sstevel int ndips;
12903831d35Sstevel dev_info_t **list;
13003831d35Sstevel };
13103831d35Sstevel
13203831d35Sstevel /*
13303831d35Sstevel * Returns mem dip held
13403831d35Sstevel */
13503831d35Sstevel static int
sbdp_get_mem_dip(pnode_t node,void * arg,uint_t flags)13603831d35Sstevel sbdp_get_mem_dip(pnode_t node, void *arg, uint_t flags)
13703831d35Sstevel {
13803831d35Sstevel _NOTE(ARGUNUSED(flags))
13903831d35Sstevel
14003831d35Sstevel dev_info_t *dip;
14103831d35Sstevel pnode_t nodeid;
14203831d35Sstevel mem_op_t mem = {0};
14303831d35Sstevel struct mem_arg *ap = arg;
14403831d35Sstevel
14503831d35Sstevel if (node == OBP_BADNODE || node == OBP_NONODE)
14603831d35Sstevel return (DDI_FAILURE);
14703831d35Sstevel
14803831d35Sstevel mem.nodes = &nodeid;
14903831d35Sstevel mem.board = ap->board;
15003831d35Sstevel mem.nmem = 0;
15103831d35Sstevel
15203831d35Sstevel (void) sbdp_is_mem(node, &mem);
15303831d35Sstevel
15403831d35Sstevel ASSERT(mem.nmem == 0 || mem.nmem == 1);
15503831d35Sstevel
15603831d35Sstevel if (mem.nmem == 0 || nodeid != node)
15703831d35Sstevel return (DDI_FAILURE);
15803831d35Sstevel
15903831d35Sstevel dip = e_ddi_nodeid_to_dip(nodeid);
16003831d35Sstevel if (dip) {
16103831d35Sstevel ASSERT(ap->ndips < SBDP_MAX_MEM_NODES_PER_BOARD);
16203831d35Sstevel ap->list[ap->ndips++] = dip;
16303831d35Sstevel }
16403831d35Sstevel return (DDI_SUCCESS);
16503831d35Sstevel }
16603831d35Sstevel
16703831d35Sstevel struct memlist *
sbdp_get_memlist(sbdp_handle_t * hp,dev_info_t * dip)16803831d35Sstevel sbdp_get_memlist(sbdp_handle_t *hp, dev_info_t *dip)
16903831d35Sstevel {
17003831d35Sstevel _NOTE(ARGUNUSED(dip))
17103831d35Sstevel
17203831d35Sstevel int i, j, skip = 0;
17303831d35Sstevel dev_info_t *list[SBDP_MAX_MEM_NODES_PER_BOARD];
17403831d35Sstevel struct mem_arg arg = {0};
17503831d35Sstevel uint64_t base_pa, size;
17603831d35Sstevel struct memlist *mlist = NULL;
17703831d35Sstevel
17803831d35Sstevel list[0] = NULL;
17903831d35Sstevel arg.board = hp->h_board;
18003831d35Sstevel arg.list = list;
18103831d35Sstevel
18203831d35Sstevel sbdp_walk_prom_tree(prom_rootnode(), sbdp_get_mem_dip, &arg);
18303831d35Sstevel
18403831d35Sstevel for (i = 0; i < arg.ndips; i++) {
18503831d35Sstevel if (list[i] == NULL)
18603831d35Sstevel continue;
18703831d35Sstevel
18803831d35Sstevel size = 0;
18903831d35Sstevel for (j = 0; j < SBDP_MAX_MCS_PER_NODE; j++) {
19003831d35Sstevel if (sbdp_get_meminfo(ddi_get_nodeid(list[i]), j,
19103831d35Sstevel &size, &base_pa)) {
19203831d35Sstevel skip++;
19303831d35Sstevel continue;
19403831d35Sstevel }
19503831d35Sstevel if (size == -1 || size == 0)
19603831d35Sstevel continue;
19703831d35Sstevel
19803831d35Sstevel (void) memlist_add_span(base_pa, size, &mlist);
19903831d35Sstevel }
20003831d35Sstevel
20103831d35Sstevel /*
20203831d35Sstevel * Release hold acquired in sbdp_get_mem_dip()
20303831d35Sstevel */
20403831d35Sstevel ddi_release_devi(list[i]);
20503831d35Sstevel }
20603831d35Sstevel
20703831d35Sstevel /*
20803831d35Sstevel * XXX - The following two lines are from existing code.
20903831d35Sstevel * However, this appears to be incorrect - this check should be
21003831d35Sstevel * made for each dip in list i.e within the for(i) loop.
21103831d35Sstevel */
21203831d35Sstevel if (skip == SBDP_MAX_MCS_PER_NODE)
21303831d35Sstevel sbdp_set_err(hp->h_err, ESGT_INTERNAL, NULL);
21403831d35Sstevel
21503831d35Sstevel SBDP_DBG_MEM("memlist for board %d\n", hp->h_board);
21603831d35Sstevel sbdp_memlist_dump(mlist);
21703831d35Sstevel return (mlist);
21803831d35Sstevel }
21903831d35Sstevel
22003831d35Sstevel struct memlist *
sbdp_memlist_dup(struct memlist * mlist)22103831d35Sstevel sbdp_memlist_dup(struct memlist *mlist)
22203831d35Sstevel {
22303831d35Sstevel struct memlist *hl, *prev;
22403831d35Sstevel
22503831d35Sstevel if (mlist == NULL)
22603831d35Sstevel return (NULL);
22703831d35Sstevel
22803831d35Sstevel prev = NULL;
22903831d35Sstevel hl = NULL;
230*56f33205SJonathan Adams for (; mlist; mlist = mlist->ml_next) {
23103831d35Sstevel struct memlist *mp;
23203831d35Sstevel
23303831d35Sstevel mp = memlist_get_one();
23403831d35Sstevel if (mp == NULL) {
23503831d35Sstevel if (hl != NULL)
23603831d35Sstevel memlist_free_list(hl);
23703831d35Sstevel hl = NULL;
23803831d35Sstevel break;
23903831d35Sstevel }
240*56f33205SJonathan Adams mp->ml_address = mlist->ml_address;
241*56f33205SJonathan Adams mp->ml_size = mlist->ml_size;
242*56f33205SJonathan Adams mp->ml_next = NULL;
243*56f33205SJonathan Adams mp->ml_prev = prev;
24403831d35Sstevel
24503831d35Sstevel if (prev == NULL)
24603831d35Sstevel hl = mp;
24703831d35Sstevel else
248*56f33205SJonathan Adams prev->ml_next = mp;
24903831d35Sstevel prev = mp;
25003831d35Sstevel }
25103831d35Sstevel
25203831d35Sstevel return (hl);
25303831d35Sstevel }
25403831d35Sstevel
25503831d35Sstevel int
sbdp_del_memlist(sbdp_handle_t * hp,struct memlist * mlist)25603831d35Sstevel sbdp_del_memlist(sbdp_handle_t *hp, struct memlist *mlist)
25703831d35Sstevel {
25803831d35Sstevel _NOTE(ARGUNUSED(hp))
25903831d35Sstevel
26003831d35Sstevel memlist_free_list(mlist);
26103831d35Sstevel
26203831d35Sstevel return (0);
26303831d35Sstevel }
26403831d35Sstevel
26503831d35Sstevel /*ARGSUSED*/
26603831d35Sstevel static void
sbdp_flush_ecache(uint64_t a,uint64_t b)26703831d35Sstevel sbdp_flush_ecache(uint64_t a, uint64_t b)
26803831d35Sstevel {
26903831d35Sstevel cpu_flush_ecache();
27003831d35Sstevel }
27103831d35Sstevel
27203831d35Sstevel typedef enum {
27303831d35Sstevel SBDP_CR_OK,
27403831d35Sstevel SBDP_CR_MC_IDLE_ERR
27503831d35Sstevel } sbdp_cr_err_t;
27603831d35Sstevel
27703831d35Sstevel int
sbdp_move_memory(sbdp_handle_t * hp,int t_bd)27803831d35Sstevel sbdp_move_memory(sbdp_handle_t *hp, int t_bd)
27903831d35Sstevel {
28003831d35Sstevel sbdp_bd_t *s_bdp, *t_bdp;
28103831d35Sstevel int err = 0;
28203831d35Sstevel caddr_t mempage;
28303831d35Sstevel ulong_t data_area, index_area;
28403831d35Sstevel ulong_t e_area, e_page;
28503831d35Sstevel int availlen, indexlen, funclen, scriptlen;
28603831d35Sstevel int *indexp;
28703831d35Sstevel time_t copytime;
28803831d35Sstevel int (*funcp)();
28903831d35Sstevel size_t size;
29003831d35Sstevel struct memlist *mlist;
29103831d35Sstevel sbdp_sr_handle_t *srhp;
29203831d35Sstevel sbdp_rename_script_t *rsp;
29303831d35Sstevel sbdp_rename_script_t *rsbuffer;
29403831d35Sstevel sbdp_cr_handle_t *cph;
29503831d35Sstevel int linesize;
29603831d35Sstevel uint64_t neer;
29703831d35Sstevel sbdp_cr_err_t cr_err;
29803831d35Sstevel
29903831d35Sstevel cph = kmem_zalloc(sizeof (sbdp_cr_handle_t), KM_SLEEP);
30003831d35Sstevel
30103831d35Sstevel SBDP_DBG_MEM("moving memory from memory board %d to board %d\n",
30203831d35Sstevel hp->h_board, t_bd);
30303831d35Sstevel
30403831d35Sstevel s_bdp = sbdp_get_bd_info(hp->h_wnode, hp->h_board);
30503831d35Sstevel t_bdp = sbdp_get_bd_info(hp->h_wnode, t_bd);
30603831d35Sstevel
30703831d35Sstevel if ((s_bdp == NULL) || (t_bdp == NULL)) {
30803831d35Sstevel sbdp_set_err(hp->h_err, ESGT_INTERNAL, NULL);
30903831d35Sstevel return (-1);
31003831d35Sstevel }
31103831d35Sstevel
31203831d35Sstevel funclen = (int)((ulong_t)_sbdp_copy_rename_end -
31303831d35Sstevel (ulong_t)sbdp_copy_rename__relocatable);
31403831d35Sstevel
31503831d35Sstevel if (funclen > PAGESIZE) {
31603831d35Sstevel cmn_err(CE_WARN,
31703831d35Sstevel "sbdp: copy-rename funclen (%d) > PAGESIZE (%d)",
31803831d35Sstevel funclen, PAGESIZE);
31903831d35Sstevel sbdp_set_err(hp->h_err, ESGT_INTERNAL, NULL);
32003831d35Sstevel return (-1);
32103831d35Sstevel }
32203831d35Sstevel
32303831d35Sstevel /*
32403831d35Sstevel * mempage will be page aligned, since we're calling
32503831d35Sstevel * kmem_alloc() with an exact multiple of PAGESIZE.
32603831d35Sstevel */
32703831d35Sstevel mempage = kmem_alloc(PAGESIZE, KM_SLEEP);
32803831d35Sstevel
32907d06da5SSurya Prakki SBDP_DBG_MEM("mempage = 0x%p\n", (void *)mempage);
33003831d35Sstevel
33103831d35Sstevel /*
33203831d35Sstevel * Copy the code for the copy-rename routine into
33303831d35Sstevel * a page aligned piece of memory. We do this to guarantee
33403831d35Sstevel * that we're executing within the same page and thus reduce
33503831d35Sstevel * the possibility of cache collisions between different
33603831d35Sstevel * pages.
33703831d35Sstevel */
33803831d35Sstevel bcopy((caddr_t)sbdp_copy_rename__relocatable, mempage, funclen);
33903831d35Sstevel
34003831d35Sstevel funcp = (int (*)())mempage;
34103831d35Sstevel
34207d06da5SSurya Prakki SBDP_DBG_MEM("copy-rename funcp = 0x%p (len = 0x%x)\n", (void *)funcp,
34307d06da5SSurya Prakki funclen);
34403831d35Sstevel
34503831d35Sstevel /*
34603831d35Sstevel * Prepare data page that will contain script of
34703831d35Sstevel * operations to perform during copy-rename.
34803831d35Sstevel * Allocate temporary buffer to hold script.
34903831d35Sstevel */
35003831d35Sstevel
35103831d35Sstevel size = sizeof (sbdp_rename_script_t) * SBDP_RENAME_MAXOP;
35203831d35Sstevel rsbuffer = kmem_zalloc(size, KM_SLEEP);
35303831d35Sstevel
35403831d35Sstevel cph->s_bdp = s_bdp;
35503831d35Sstevel cph->t_bdp = t_bdp;
35603831d35Sstevel cph->script = rsbuffer;
35703831d35Sstevel
35803831d35Sstevel /*
35903831d35Sstevel * We need to make sure we don't switch cpus since we depend on the
36003831d35Sstevel * correct cpu processing
36103831d35Sstevel */
36203831d35Sstevel affinity_set(CPU_CURRENT);
36303831d35Sstevel scriptlen = sbdp_prep_rename_script(cph);
36403831d35Sstevel if (scriptlen <= 0) {
365d3d50737SRafael Vanoni cmn_err(CE_WARN, "sbdp failed to prep for copy-rename");
36603831d35Sstevel sbdp_set_err(hp->h_err, ESGT_INTERNAL, NULL);
36703831d35Sstevel err = 1;
36803831d35Sstevel goto cleanup;
36903831d35Sstevel }
37003831d35Sstevel SBDP_DBG_MEM("copy-rename script length = 0x%x\n", scriptlen);
37103831d35Sstevel
37203831d35Sstevel indexlen = sizeof (*indexp) << 1;
37303831d35Sstevel
37403831d35Sstevel if ((funclen + scriptlen + indexlen) > PAGESIZE) {
375d3d50737SRafael Vanoni cmn_err(CE_WARN, "sbdp: func len (%d) + script len (%d) "
376d3d50737SRafael Vanoni "+ index len (%d) > PAGESIZE (%d)", funclen, scriptlen,
377d3d50737SRafael Vanoni indexlen, PAGESIZE);
37803831d35Sstevel sbdp_set_err(hp->h_err, ESGT_INTERNAL, NULL);
37903831d35Sstevel err = 1;
38003831d35Sstevel goto cleanup;
38103831d35Sstevel }
38203831d35Sstevel
38303831d35Sstevel linesize = cpunodes[CPU->cpu_id].ecache_linesize;
38403831d35Sstevel
38503831d35Sstevel /*
38603831d35Sstevel * Find aligned area within data page to maintain script.
38703831d35Sstevel */
38803831d35Sstevel data_area = (ulong_t)mempage;
38903831d35Sstevel data_area += (ulong_t)funclen + (ulong_t)(linesize - 1);
39003831d35Sstevel data_area &= ~((ulong_t)(linesize - 1));
39103831d35Sstevel
39203831d35Sstevel availlen = PAGESIZE - indexlen;
39303831d35Sstevel availlen -= (int)(data_area - (ulong_t)mempage);
39403831d35Sstevel
39503831d35Sstevel if (availlen < scriptlen) {
396d3d50737SRafael Vanoni cmn_err(CE_WARN, "sbdp: available len (%d) < script len (%d)",
39703831d35Sstevel availlen, scriptlen);
39803831d35Sstevel sbdp_set_err(hp->h_err, ESGT_INTERNAL, NULL);
39903831d35Sstevel err = 1;
40003831d35Sstevel goto cleanup;
40103831d35Sstevel }
40203831d35Sstevel
40303831d35Sstevel SBDP_DBG_MEM("copy-rename script data area = 0x%lx\n",
40403831d35Sstevel data_area);
40503831d35Sstevel
40603831d35Sstevel bcopy((caddr_t)rsbuffer, (caddr_t)data_area, scriptlen);
40703831d35Sstevel rsp = (sbdp_rename_script_t *)data_area;
40803831d35Sstevel
409d3d50737SRafael Vanoni index_area = data_area + (ulong_t)scriptlen + (ulong_t)(linesize - 1);
41003831d35Sstevel index_area &= ~((ulong_t)(linesize - 1));
41103831d35Sstevel indexp = (int *)index_area;
41203831d35Sstevel indexp[0] = 0;
41303831d35Sstevel indexp[1] = 0;
41403831d35Sstevel
41503831d35Sstevel e_area = index_area + (ulong_t)indexlen;
41603831d35Sstevel e_page = (ulong_t)mempage + PAGESIZE;
41703831d35Sstevel if (e_area > e_page) {
41803831d35Sstevel cmn_err(CE_WARN,
41903831d35Sstevel "sbdp: index area size (%d) > available (%d)\n",
42003831d35Sstevel indexlen, (int)(e_page - index_area));
42103831d35Sstevel sbdp_set_err(hp->h_err, ESGT_INTERNAL, NULL);
42203831d35Sstevel err = 1;
42303831d35Sstevel goto cleanup;
42403831d35Sstevel }
42503831d35Sstevel
42607d06da5SSurya Prakki SBDP_DBG_MEM("copy-rename index area = 0x%p\n", (void *)indexp);
42703831d35Sstevel
42803831d35Sstevel SBDP_DBG_MEM("cpu %d\n", CPU->cpu_id);
42903831d35Sstevel
43003831d35Sstevel srhp = sbdp_get_sr_handle();
43103831d35Sstevel ASSERT(srhp);
43203831d35Sstevel
43303831d35Sstevel srhp->sr_flags = hp->h_flags;
43403831d35Sstevel
435d3d50737SRafael Vanoni copytime = ddi_get_lbolt();
43603831d35Sstevel
43703831d35Sstevel mutex_enter(&s_bdp->bd_mutex);
43803831d35Sstevel mlist = sbdp_memlist_dup(s_bdp->ml);
43903831d35Sstevel mutex_exit(&s_bdp->bd_mutex);
44003831d35Sstevel
44103831d35Sstevel if (mlist == NULL) {
44203831d35Sstevel SBDP_DBG_MEM("Didn't find memory list\n");
44303831d35Sstevel }
44403831d35Sstevel SBDP_DBG_MEM("src\n\tbd\t%d\n\tnode\t%d\n\tbpa 0x%lx\n\tnodes\t%p\n",
44507d06da5SSurya Prakki s_bdp->bd, s_bdp->wnode, s_bdp->bpa, (void *)s_bdp->nodes);
44603831d35Sstevel sbdp_memlist_dump(s_bdp->ml);
44703831d35Sstevel SBDP_DBG_MEM("tgt\n\tbd\t%d\n\tnode\t%d\n\tbpa 0x%lx\n\tnodes\t%p\n",
44807d06da5SSurya Prakki t_bdp->bd, t_bdp->wnode, t_bdp->bpa, (void *)t_bdp->nodes);
44903831d35Sstevel sbdp_memlist_dump(t_bdp->ml);
45003831d35Sstevel
45103831d35Sstevel /*
45203831d35Sstevel * Quiesce the OS.
45303831d35Sstevel */
45403831d35Sstevel if (sbdp_suspend(srhp)) {
45503831d35Sstevel sbd_error_t *sep;
456d3d50737SRafael Vanoni cmn_err(CE_WARN, "sbdp: failed to quiesce OS for copy-rename");
45703831d35Sstevel sep = &srhp->sep;
45803831d35Sstevel sbdp_set_err(hp->h_err, sep->e_code, sep->e_rsc);
45903831d35Sstevel sbdp_release_sr_handle(srhp);
46007d06da5SSurya Prakki (void) sbdp_del_memlist(hp, mlist);
46103831d35Sstevel err = 1;
46203831d35Sstevel goto cleanup;
46303831d35Sstevel }
46403831d35Sstevel
46503831d35Sstevel /*
46603831d35Sstevel * =================================
46703831d35Sstevel * COPY-RENAME BEGIN.
46803831d35Sstevel * =================================
46903831d35Sstevel */
47003831d35Sstevel SBDP_DBG_MEM("s_base 0x%lx t_base 0x%lx\n", cph->s_bdp->bpa,
47103831d35Sstevel cph->t_bdp->bpa);
47203831d35Sstevel
47303831d35Sstevel cph->ret = 0;
47403831d35Sstevel
47503831d35Sstevel SBDP_DBG_MEM("cph return 0x%lx\n", cph->ret);
47603831d35Sstevel
47703831d35Sstevel SBDP_DBG_MEM("Flushing all of the cpu caches\n");
47803831d35Sstevel xc_all(sbdp_flush_ecache, 0, 0);
47903831d35Sstevel
48003831d35Sstevel /* disable CE reporting */
48103831d35Sstevel neer = get_error_enable();
48203831d35Sstevel set_error_enable(neer & ~EN_REG_CEEN);
48303831d35Sstevel
48403831d35Sstevel cr_err = (*funcp)(cph, mlist, rsp);
48503831d35Sstevel
48603831d35Sstevel /* enable CE reporting */
48703831d35Sstevel set_error_enable(neer);
48803831d35Sstevel
48903831d35Sstevel SBDP_DBG_MEM("s_base 0x%lx t_base 0x%lx\n", cph->s_bdp->bpa,
49003831d35Sstevel cph->t_bdp->bpa);
49103831d35Sstevel SBDP_DBG_MEM("cph return 0x%lx\n", cph->ret);
49203831d35Sstevel SBDP_DBG_MEM("after execking the function\n");
49303831d35Sstevel
49403831d35Sstevel /*
49503831d35Sstevel * =================================
49603831d35Sstevel * COPY-RENAME END.
49703831d35Sstevel * =================================
49803831d35Sstevel */
49903831d35Sstevel SBDP_DBG_MEM("err is 0x%d\n", err);
50003831d35Sstevel
50103831d35Sstevel /*
50203831d35Sstevel * Resume the OS.
50303831d35Sstevel */
50403831d35Sstevel sbdp_resume(srhp);
50503831d35Sstevel if (srhp->sep.e_code) {
50603831d35Sstevel sbd_error_t *sep;
50703831d35Sstevel cmn_err(CE_WARN,
50803831d35Sstevel "sbdp: failed to resume OS for copy-rename");
50903831d35Sstevel sep = &srhp->sep;
51003831d35Sstevel sbdp_set_err(hp->h_err, sep->e_code, sep->e_rsc);
51103831d35Sstevel err = 1;
51203831d35Sstevel }
51303831d35Sstevel
514d3d50737SRafael Vanoni copytime = ddi_get_lbolt() - copytime;
51503831d35Sstevel
51603831d35Sstevel sbdp_release_sr_handle(srhp);
51707d06da5SSurya Prakki (void) sbdp_del_memlist(hp, mlist);
51803831d35Sstevel
51903831d35Sstevel SBDP_DBG_MEM("copy-rename elapsed time = %ld ticks (%ld secs)\n",
52003831d35Sstevel copytime, copytime / hz);
52103831d35Sstevel
52203831d35Sstevel switch (cr_err) {
52303831d35Sstevel case SBDP_CR_OK:
52403831d35Sstevel break;
52503831d35Sstevel case SBDP_CR_MC_IDLE_ERR: {
52603831d35Sstevel dev_info_t *dip;
52703831d35Sstevel pnode_t nodeid = cph->busy_mc->node;
52803831d35Sstevel char *path = kmem_alloc(MAXPATHLEN, KM_SLEEP);
52903831d35Sstevel
53003831d35Sstevel dip = e_ddi_nodeid_to_dip(nodeid);
53103831d35Sstevel
53203831d35Sstevel ASSERT(dip != NULL);
53303831d35Sstevel
53403831d35Sstevel (void) ddi_pathname(dip, path);
53503831d35Sstevel ddi_release_devi(dip);
53603831d35Sstevel cmn_err(CE_WARN, "failed to idle memory controller %s: "
53703831d35Sstevel "copy-rename aborted", path);
53803831d35Sstevel kmem_free(path, MAXPATHLEN);
53903831d35Sstevel sbdp_set_err(hp->h_err, ESBD_MEMFAIL, NULL);
54003831d35Sstevel err = 1;
54103831d35Sstevel break;
54203831d35Sstevel }
54303831d35Sstevel default:
54403831d35Sstevel sbdp_set_err(hp->h_err, ESGT_INTERNAL, NULL);
54503831d35Sstevel cmn_err(CE_WARN, "unknown copy-rename error code (%d)", cr_err);
54603831d35Sstevel err = 1;
54703831d35Sstevel break;
54803831d35Sstevel }
54903831d35Sstevel
55003831d35Sstevel if (err)
55103831d35Sstevel goto cleanup;
55203831d35Sstevel
55303831d35Sstevel /*
55403831d35Sstevel * Rename memory for lgroup.
55503831d35Sstevel * Source and target board numbers are packaged in arg.
55603831d35Sstevel */
55703831d35Sstevel lgrp_plat_config(LGRP_CONFIG_MEM_RENAME,
55803831d35Sstevel (uintptr_t)(s_bdp->bd | (t_bdp->bd << 16)));
55903831d35Sstevel
56003831d35Sstevel /*
56103831d35Sstevel * swap list of banks
56203831d35Sstevel */
56303831d35Sstevel sbdp_swap_list_of_banks(s_bdp, t_bdp);
56403831d35Sstevel
56503831d35Sstevel /*
56603831d35Sstevel * Update the cached board info for both the source and the target
56703831d35Sstevel */
56803831d35Sstevel sbdp_update_bd_info(s_bdp);
56903831d35Sstevel sbdp_update_bd_info(t_bdp);
57003831d35Sstevel
57103831d35Sstevel /*
57203831d35Sstevel * Tell the sc that we have swapped slices.
57303831d35Sstevel */
57403831d35Sstevel if (sbdp_swap_slices(s_bdp->bd, t_bdp->bd) != 0) {
57503831d35Sstevel /* This is dangerous. The in use slice could be re-used! */
57603831d35Sstevel SBDP_DBG_MEM("swaping slices failed\n");
57703831d35Sstevel }
57803831d35Sstevel
57903831d35Sstevel cleanup:
58003831d35Sstevel kmem_free(rsbuffer, size);
58103831d35Sstevel kmem_free(mempage, PAGESIZE);
58203831d35Sstevel kmem_free(cph, sizeof (sbdp_cr_handle_t));
58303831d35Sstevel affinity_clear();
58403831d35Sstevel
58503831d35Sstevel return (err ? -1 : 0);
58603831d35Sstevel }
58703831d35Sstevel
58803831d35Sstevel static int
sbdp_copy_regs(pnode_t node,uint64_t bpa,uint64_t new_base,int inval,sbdp_rename_script_t * rsp,int * index)58903831d35Sstevel sbdp_copy_regs(pnode_t node, uint64_t bpa, uint64_t new_base, int inval,
59003831d35Sstevel sbdp_rename_script_t *rsp, int *index)
59103831d35Sstevel {
59203831d35Sstevel int i, m;
59303831d35Sstevel mc_regs_t regs;
59403831d35Sstevel uint64_t *mc_decode;
59503831d35Sstevel
59603831d35Sstevel if (mc_read_regs(node, ®s)) {
59703831d35Sstevel SBDP_DBG_MEM("sbdp_copy_regs: failed to read source Decode "
59803831d35Sstevel "Regs");
59903831d35Sstevel return (-1);
60003831d35Sstevel }
60103831d35Sstevel
60203831d35Sstevel mc_decode = regs.mc_decode;
60303831d35Sstevel
60403831d35Sstevel m = *index;
60503831d35Sstevel for (i = 0; i < SBDP_MAX_MCS_PER_NODE; i++) {
60603831d35Sstevel uint64_t offset, seg_pa, tmp_base;
60703831d35Sstevel
60803831d35Sstevel /*
60903831d35Sstevel * Skip invalid banks
61003831d35Sstevel */
61103831d35Sstevel if ((mc_decode[i] & SG_DECODE_VALID) != SG_DECODE_VALID) {
61203831d35Sstevel continue;
61303831d35Sstevel }
61403831d35Sstevel
61503831d35Sstevel tmp_base = new_base;
61603831d35Sstevel if (!inval) {
61703831d35Sstevel /*
61803831d35Sstevel * We need to calculate the offset from the base pa
61903831d35Sstevel * to add it appropriately to the new_base.
62003831d35Sstevel * The offset needs to be in UM relative to the mc
62103831d35Sstevel * decode register. Since we are going from physical
62203831d35Sstevel * address to UM, we need to shift it by PHYS2UM_SHIFT.
62303831d35Sstevel * To get it ready to OR it with the MC decode reg,
62403831d35Sstevel * we need to shift it left MC_UM_SHIFT
62503831d35Sstevel */
62603831d35Sstevel seg_pa = MC_BASE(mc_decode[i]) << PHYS2UM_SHIFT;
62703831d35Sstevel offset = (seg_pa - bpa);
62803831d35Sstevel /* Convert tmp_base into a physical address */
62903831d35Sstevel tmp_base = (tmp_base >> MC_UM_SHIFT) << PHYS2UM_SHIFT;
63003831d35Sstevel tmp_base += offset;
63103831d35Sstevel /* Convert tmp_base to be MC reg ready */
63203831d35Sstevel tmp_base = (tmp_base >> PHYS2UM_SHIFT) << MC_UM_SHIFT;
63303831d35Sstevel }
63403831d35Sstevel
63503831d35Sstevel mc_decode[i] &= ~SG_DECODE_UM;
63603831d35Sstevel mc_decode[i] |= tmp_base;
63703831d35Sstevel mc_decode[i] |= SG_DECODE_VALID;
63803831d35Sstevel
63903831d35Sstevel /*
64003831d35Sstevel * Step 1: Write source base address to the MC
64103831d35Sstevel * with present bit off.
64203831d35Sstevel */
64303831d35Sstevel rsp[m].masr_addr = mc_get_addr(node, i, &rsp[m].asi);
64403831d35Sstevel rsp[m].masr = mc_decode[i] & ~SG_DECODE_VALID;
64503831d35Sstevel m++;
64603831d35Sstevel /*
64703831d35Sstevel * Step 2: Now rewrite the mc reg with present bit on.
64803831d35Sstevel */
64903831d35Sstevel rsp[m].masr_addr = rsp[m-1].masr_addr;
65003831d35Sstevel rsp[m].masr = mc_decode[i];
65103831d35Sstevel rsp[m].asi = rsp[m-1].asi;
65203831d35Sstevel m++;
65303831d35Sstevel }
65403831d35Sstevel
65503831d35Sstevel *index = m;
65603831d35Sstevel return (0);
65703831d35Sstevel }
65803831d35Sstevel
65903831d35Sstevel static int
sbdp_get_reg_addr(pnode_t nodeid,uint64_t * pa)66003831d35Sstevel sbdp_get_reg_addr(pnode_t nodeid, uint64_t *pa)
66103831d35Sstevel {
66203831d35Sstevel mc_regspace reg;
66303831d35Sstevel int len;
66403831d35Sstevel
66503831d35Sstevel len = prom_getproplen(nodeid, "reg");
66603831d35Sstevel if (len != sizeof (mc_regspace))
66703831d35Sstevel return (-1);
66803831d35Sstevel
66903831d35Sstevel if (prom_getprop(nodeid, "reg", (caddr_t)®) < 0)
67003831d35Sstevel return (-1);
67103831d35Sstevel
67203831d35Sstevel ASSERT(pa != NULL);
67303831d35Sstevel
67403831d35Sstevel *pa = ((uint64_t)reg.regspec_addr_hi) << 32;
67503831d35Sstevel *pa |= (uint64_t)reg.regspec_addr_lo;
67603831d35Sstevel
67703831d35Sstevel return (0);
67803831d35Sstevel }
67903831d35Sstevel
68003831d35Sstevel static int
mc_get_sibling_cpu_impl(pnode_t mc_node)68103831d35Sstevel mc_get_sibling_cpu_impl(pnode_t mc_node)
68203831d35Sstevel {
68303831d35Sstevel int len, impl;
68403831d35Sstevel pnode_t cpu_node;
68503831d35Sstevel char namebuf[OBP_MAXPROPNAME];
68603831d35Sstevel
68703831d35Sstevel cpu_node = mc_get_sibling_cpu(mc_node);
68803831d35Sstevel if (cpu_node == OBP_NONODE) {
68903831d35Sstevel SBDP_DBG_MEM("mc_get_sibling_cpu failed: dnode=0x%x\n",
69003831d35Sstevel mc_node);
69103831d35Sstevel return (-1);
69203831d35Sstevel }
69303831d35Sstevel
69403831d35Sstevel len = prom_getproplen(cpu_node, "name");
69503831d35Sstevel if (len < 0) {
69603831d35Sstevel SBDP_DBG_MEM("invalid prom_getproplen for name prop: "
69703831d35Sstevel "len=%d, dnode=0x%x\n", len, cpu_node);
69803831d35Sstevel return (-1);
69903831d35Sstevel }
70003831d35Sstevel
70103831d35Sstevel if (prom_getprop(cpu_node, "name", (caddr_t)namebuf) == -1) {
70203831d35Sstevel SBDP_DBG_MEM("failed to read name property for dnode=0x%x\n",
70303831d35Sstevel cpu_node);
70403831d35Sstevel return (-1);
70503831d35Sstevel }
70603831d35Sstevel
70703831d35Sstevel /*
70803831d35Sstevel * If this is a CMP node, the child has the implementation
70903831d35Sstevel * property.
71003831d35Sstevel */
71103831d35Sstevel if (strcmp(namebuf, "cmp") == 0) {
71203831d35Sstevel cpu_node = prom_childnode(cpu_node);
71303831d35Sstevel ASSERT(cpu_node != OBP_NONODE);
71403831d35Sstevel }
71503831d35Sstevel
71603831d35Sstevel if (prom_getprop(cpu_node, "implementation#", (caddr_t)&impl) == -1) {
71703831d35Sstevel SBDP_DBG_MEM("failed to read implementation# property for "
71803831d35Sstevel "dnode=0x%x\n", cpu_node);
71903831d35Sstevel return (-1);
72003831d35Sstevel }
72103831d35Sstevel
72203831d35Sstevel SBDP_DBG_MEM("mc_get_sibling_cpu_impl: found impl=0x%x, dnode=0x%x\n",
72303831d35Sstevel impl, cpu_node);
72403831d35Sstevel
72503831d35Sstevel return (impl);
72603831d35Sstevel }
72703831d35Sstevel
72803831d35Sstevel /*
72903831d35Sstevel * Provide EMU Activity Status register ASI and address. Only valid for
73003831d35Sstevel * Panther processors.
73103831d35Sstevel */
73203831d35Sstevel static int
mc_get_idle_reg(pnode_t nodeid,uint64_t * addr,uint_t * asi)73303831d35Sstevel mc_get_idle_reg(pnode_t nodeid, uint64_t *addr, uint_t *asi)
73403831d35Sstevel {
73503831d35Sstevel int portid;
73603831d35Sstevel uint64_t reg_pa;
73703831d35Sstevel
73803831d35Sstevel ASSERT(nodeid != OBP_NONODE);
73903831d35Sstevel ASSERT(mc_get_sibling_cpu_impl(nodeid) == PANTHER_IMPL);
74003831d35Sstevel
74103831d35Sstevel if (prom_getprop(nodeid, "portid", (caddr_t)&portid) < 0 ||
74203831d35Sstevel portid == -1) {
74303831d35Sstevel SBDP_DBG_MEM("mc_get_idle_reg: failed to read portid prop "
74403831d35Sstevel "for dnode=0x%x\n", nodeid);
74503831d35Sstevel return (-1);
74603831d35Sstevel }
74703831d35Sstevel
74803831d35Sstevel if (sbdp_get_reg_addr(nodeid, ®_pa) != 0) {
74903831d35Sstevel SBDP_DBG_MEM("mc_get_idle_reg: failed to read reg prop "
75003831d35Sstevel "for dnode=0x%x\n", nodeid);
75103831d35Sstevel return (-1);
75203831d35Sstevel }
75303831d35Sstevel
75403831d35Sstevel /*
75503831d35Sstevel * Local access will be via ASI 0x4a, otherwise via Safari PIO.
75603831d35Sstevel * This assumes the copy-rename will later run on the same proc,
75703831d35Sstevel * hence there is an assumption we are already bound.
75803831d35Sstevel */
75903831d35Sstevel ASSERT(curthread->t_bound_cpu == CPU);
76003831d35Sstevel if (SG_CPUID_TO_PORTID(CPU->cpu_id) == portid) {
76103831d35Sstevel *addr = ASI_EMU_ACT_STATUS_VA;
76203831d35Sstevel *asi = ASI_SAFARI_CONFIG;
76303831d35Sstevel } else {
76403831d35Sstevel *addr = MC_ACTIVITY_STATUS(reg_pa);
76503831d35Sstevel *asi = ASI_IO;
76603831d35Sstevel }
76703831d35Sstevel
76803831d35Sstevel return (0);
76903831d35Sstevel }
77003831d35Sstevel
77103831d35Sstevel /*
77203831d35Sstevel * If non-Panther board, add phys_banks entry for each physical bank.
77303831d35Sstevel * If Panther board, add mc_idle_regs entry for each EMU Activity Status
77403831d35Sstevel * register. Increment the array indices b_idx and r_idx for each entry
77503831d35Sstevel * populated by this routine.
77603831d35Sstevel *
77703831d35Sstevel * The caller is responsible for allocating sufficient array entries.
77803831d35Sstevel */
77903831d35Sstevel 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)78003831d35Sstevel sbdp_prep_mc_idle_one(sbdp_bd_t *bp, sbdp_rename_script_t phys_banks[],
78103831d35Sstevel int *b_idx, sbdp_mc_idle_script_t mc_idle_regs[], int *r_idx)
78203831d35Sstevel {
78303831d35Sstevel int i, j;
78403831d35Sstevel pnode_t *memnodes;
78503831d35Sstevel mc_regs_t regs;
78603831d35Sstevel uint64_t addr;
78703831d35Sstevel uint_t asi;
78803831d35Sstevel sbd_cond_t sibling_cpu_cond;
78903831d35Sstevel int impl = -1;
79003831d35Sstevel
79103831d35Sstevel memnodes = bp->nodes;
79203831d35Sstevel
79303831d35Sstevel for (i = 0; i < SBDP_MAX_MEM_NODES_PER_BOARD; i++) {
79403831d35Sstevel if (memnodes[i] == OBP_NONODE) {
79503831d35Sstevel continue;
79603831d35Sstevel }
79703831d35Sstevel
79803831d35Sstevel /* MC should not be accessed if cpu has failed */
79903831d35Sstevel sibling_cpu_cond = mc_check_sibling_cpu(memnodes[i]);
80003831d35Sstevel if (sibling_cpu_cond == SBD_COND_FAILED ||
80103831d35Sstevel sibling_cpu_cond == SBD_COND_UNUSABLE) {
80203831d35Sstevel SBDP_DBG_MEM("sbdp: skipping MC with failed cpu: "
80303831d35Sstevel "board=%d, mem node=%d, condition=%d",
80403831d35Sstevel bp->bd, i, sibling_cpu_cond);
80503831d35Sstevel continue;
80603831d35Sstevel }
80703831d35Sstevel
80803831d35Sstevel /*
80903831d35Sstevel * Initialize the board cpu type, assuming all board cpus are
81003831d35Sstevel * the same type. This is true of all Cheetah-based processors.
81103831d35Sstevel * Failure to read the cpu type is considered a fatal error.
81203831d35Sstevel */
81303831d35Sstevel if (impl == -1) {
81403831d35Sstevel impl = mc_get_sibling_cpu_impl(memnodes[i]);
81503831d35Sstevel if (impl == -1) {
81603831d35Sstevel SBDP_DBG_MEM("sbdp: failed to get cpu impl "
81703831d35Sstevel "for MC dnode=0x%x\n", memnodes[i]);
81803831d35Sstevel return (-1);
81903831d35Sstevel }
82003831d35Sstevel }
82103831d35Sstevel
82203831d35Sstevel switch (impl) {
82303831d35Sstevel case CHEETAH_IMPL:
82403831d35Sstevel case CHEETAH_PLUS_IMPL:
82503831d35Sstevel case JAGUAR_IMPL:
82603831d35Sstevel if (mc_read_regs(memnodes[i], ®s)) {
82703831d35Sstevel SBDP_DBG_MEM("sbdp: failed to read source "
82803831d35Sstevel "Decode Regs of board %d", bp->bd);
82903831d35Sstevel return (-1);
83003831d35Sstevel }
83103831d35Sstevel
83203831d35Sstevel for (j = 0; j < SBDP_MAX_MCS_PER_NODE; j++) {
83303831d35Sstevel uint64_t mc_decode = regs.mc_decode[j];
83403831d35Sstevel
83503831d35Sstevel if ((mc_decode & SG_DECODE_VALID) !=
83603831d35Sstevel SG_DECODE_VALID) {
83703831d35Sstevel continue;
83803831d35Sstevel }
83903831d35Sstevel
84003831d35Sstevel addr = (MC_BASE(mc_decode) << PHYS2UM_SHIFT) |
84103831d35Sstevel (MC_LM(mc_decode) << MC_LM_SHIFT);
84203831d35Sstevel
84303831d35Sstevel phys_banks[*b_idx].masr_addr = addr;
84403831d35Sstevel phys_banks[*b_idx].masr = 0; /* unused */
84503831d35Sstevel phys_banks[*b_idx].asi = ASI_MEM;
84603831d35Sstevel (*b_idx)++;
84703831d35Sstevel }
84803831d35Sstevel break;
84903831d35Sstevel case PANTHER_IMPL:
85003831d35Sstevel if (mc_get_idle_reg(memnodes[i], &addr, &asi)) {
85103831d35Sstevel return (-1);
85203831d35Sstevel }
85303831d35Sstevel
85403831d35Sstevel mc_idle_regs[*r_idx].addr = addr;
85503831d35Sstevel mc_idle_regs[*r_idx].asi = asi;
85603831d35Sstevel mc_idle_regs[*r_idx].node = memnodes[i];
85703831d35Sstevel mc_idle_regs[*r_idx].bd_id = bp->bd;
85803831d35Sstevel (*r_idx)++;
85903831d35Sstevel break;
86003831d35Sstevel default:
86103831d35Sstevel cmn_err(CE_WARN, "Unknown cpu implementation=0x%x",
86203831d35Sstevel impl);
86303831d35Sstevel ASSERT(0);
86403831d35Sstevel return (-1);
86503831d35Sstevel }
86603831d35Sstevel }
86703831d35Sstevel
86803831d35Sstevel return (0);
86903831d35Sstevel }
87003831d35Sstevel
87103831d35Sstevel /*
87203831d35Sstevel * For non-Panther MCs that do not support read-bypass-write, we do a read
87303831d35Sstevel * to each physical bank, relying on the reads to block until all outstanding
87403831d35Sstevel * write requests have completed. This mechanism is referred to as the bus
87503831d35Sstevel * sync list and is used for Cheetah, Cheetah+, and Jaguar processors. The
87603831d35Sstevel * bus sync list PAs for the source and target are kept together and comprise
87703831d35Sstevel * Section 1 of the rename script.
87803831d35Sstevel *
87903831d35Sstevel * For Panther processors that support the EMU Activity Status register,
88003831d35Sstevel * we ensure the writes have completed by polling the MCU_ACT_STATUS
88103831d35Sstevel * field several times to make sure the MC queues are empty. The
88203831d35Sstevel * EMU Activity Status register PAs for the source and target are
88303831d35Sstevel * kept together and comprise Section 2 of the rename script.
88403831d35Sstevel */
88503831d35Sstevel 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)88603831d35Sstevel sbdp_prep_mc_idle_script(sbdp_bd_t *s_bp, sbdp_bd_t *t_bp,
88703831d35Sstevel sbdp_rename_script_t *rsp, int *rsp_idx)
88803831d35Sstevel {
88903831d35Sstevel sbdp_rename_script_t *phys_banks;
89003831d35Sstevel sbdp_mc_idle_script_t *mc_idle_regs;
89103831d35Sstevel int max_banks, max_regs;
89203831d35Sstevel size_t bsize, msize;
89303831d35Sstevel int nbanks = 0, nregs = 0;
89403831d35Sstevel int i;
89503831d35Sstevel
89603831d35Sstevel /* CONSTCOND */
89703831d35Sstevel ASSERT(sizeof (sbdp_rename_script_t) ==
89803831d35Sstevel sizeof (sbdp_mc_idle_script_t));
89903831d35Sstevel
90003831d35Sstevel /* allocate space for both source and target */
90103831d35Sstevel max_banks = SBDP_MAX_MEM_NODES_PER_BOARD *
90203831d35Sstevel SG_MAX_BANKS_PER_MC * 2;
90303831d35Sstevel max_regs = SBDP_MAX_MEM_NODES_PER_BOARD * 2;
90403831d35Sstevel
90503831d35Sstevel bsize = sizeof (sbdp_rename_script_t) * max_banks;
90603831d35Sstevel msize = sizeof (sbdp_mc_idle_script_t) * max_regs;
90703831d35Sstevel
90803831d35Sstevel phys_banks = kmem_zalloc(bsize, KM_SLEEP);
90903831d35Sstevel mc_idle_regs = kmem_zalloc(msize, KM_SLEEP);
91003831d35Sstevel
91103831d35Sstevel if (sbdp_prep_mc_idle_one(t_bp, phys_banks, &nbanks,
91203831d35Sstevel mc_idle_regs, &nregs) != 0 ||
91303831d35Sstevel sbdp_prep_mc_idle_one(s_bp, phys_banks, &nbanks,
91403831d35Sstevel mc_idle_regs, &nregs) != 0) {
91503831d35Sstevel kmem_free(phys_banks, bsize);
91603831d35Sstevel kmem_free(mc_idle_regs, msize);
91703831d35Sstevel return (-1);
91803831d35Sstevel }
91903831d35Sstevel
92003831d35Sstevel /* section 1 */
92103831d35Sstevel for (i = 0; i < nbanks; i++)
92203831d35Sstevel rsp[(*rsp_idx)++] = phys_banks[i];
92303831d35Sstevel
92403831d35Sstevel /* section 2 */
92503831d35Sstevel for (i = 0; i < nregs; i++)
92603831d35Sstevel rsp[(*rsp_idx)++] = *(sbdp_rename_script_t *)&mc_idle_regs[i];
92703831d35Sstevel
92803831d35Sstevel kmem_free(phys_banks, bsize);
92903831d35Sstevel kmem_free(mc_idle_regs, msize);
93003831d35Sstevel
93103831d35Sstevel return (0);
93203831d35Sstevel }
93303831d35Sstevel
93403831d35Sstevel /*
93503831d35Sstevel * code assumes single mem-unit.
93603831d35Sstevel */
93703831d35Sstevel static int
sbdp_prep_rename_script(sbdp_cr_handle_t * cph)93803831d35Sstevel sbdp_prep_rename_script(sbdp_cr_handle_t *cph)
93903831d35Sstevel {
94003831d35Sstevel pnode_t *s_nodes, *t_nodes;
94103831d35Sstevel int m = 0, i;
94203831d35Sstevel sbdp_bd_t s_bd, t_bd, *s_bdp, *t_bdp;
94303831d35Sstevel sbdp_rename_script_t *rsp;
94403831d35Sstevel uint64_t new_base, old_base, temp_base;
94503831d35Sstevel int s_num, t_num;
94603831d35Sstevel
94703831d35Sstevel mutex_enter(&cph->s_bdp->bd_mutex);
94803831d35Sstevel s_bd = *cph->s_bdp;
94903831d35Sstevel mutex_exit(&cph->s_bdp->bd_mutex);
95003831d35Sstevel mutex_enter(&cph->t_bdp->bd_mutex);
95103831d35Sstevel t_bd = *cph->t_bdp;
95203831d35Sstevel mutex_exit(&cph->t_bdp->bd_mutex);
95303831d35Sstevel
95403831d35Sstevel s_bdp = &s_bd;
95503831d35Sstevel t_bdp = &t_bd;
95603831d35Sstevel s_nodes = s_bdp->nodes;
95703831d35Sstevel t_nodes = t_bdp->nodes;
95803831d35Sstevel s_num = s_bdp->nnum;
95903831d35Sstevel t_num = t_bdp->nnum;
96003831d35Sstevel rsp = cph->script;
96103831d35Sstevel
96203831d35Sstevel /*
96303831d35Sstevel * Calculate the new base address for the target bd
96403831d35Sstevel */
96503831d35Sstevel
96603831d35Sstevel new_base = (s_bdp->bpa >> PHYS2UM_SHIFT) << MC_UM_SHIFT;
96703831d35Sstevel
96803831d35Sstevel /*
96903831d35Sstevel * Calculate the old base address for the source bd
97003831d35Sstevel */
97103831d35Sstevel
97203831d35Sstevel old_base = (t_bdp->bpa >> PHYS2UM_SHIFT) << MC_UM_SHIFT;
97303831d35Sstevel
97403831d35Sstevel temp_base = SG_INVAL_UM;
97503831d35Sstevel
97603831d35Sstevel SBDP_DBG_MEM("new 0x%lx old_base ox%lx temp_base 0x%lx\n", new_base,
97703831d35Sstevel old_base, temp_base);
97803831d35Sstevel
97903831d35Sstevel m = 0;
98003831d35Sstevel
98103831d35Sstevel /*
98203831d35Sstevel * Ensure the MC queues have been idled on the source and target
98303831d35Sstevel * following the copy.
98403831d35Sstevel */
98503831d35Sstevel if (sbdp_prep_mc_idle_script(s_bdp, t_bdp, rsp, &m) < 0)
98603831d35Sstevel return (-1);
98703831d35Sstevel
98803831d35Sstevel /*
98903831d35Sstevel * Script section terminator
99003831d35Sstevel */
99103831d35Sstevel rsp[m].masr_addr = 0ull;
99203831d35Sstevel rsp[m].masr = 0;
99303831d35Sstevel rsp[m].asi = 0;
99403831d35Sstevel m++;
99503831d35Sstevel
99603831d35Sstevel /*
99703831d35Sstevel * Invalidate the base in the target mc registers
99803831d35Sstevel */
99903831d35Sstevel for (i = 0; i < t_num; i++) {
100003831d35Sstevel if (sbdp_copy_regs(t_nodes[i], t_bdp->bpa, temp_base, 1, rsp,
100103831d35Sstevel &m) < 0)
100203831d35Sstevel return (-1);
100303831d35Sstevel }
100403831d35Sstevel /*
100503831d35Sstevel * Invalidate the base in the source mc registers
100603831d35Sstevel */
100703831d35Sstevel for (i = 0; i < s_num; i++) {
100803831d35Sstevel if (sbdp_copy_regs(s_nodes[i], s_bdp->bpa, temp_base, 1, rsp,
100903831d35Sstevel &m) < 0)
101003831d35Sstevel return (-1);
101103831d35Sstevel }
101203831d35Sstevel /*
101303831d35Sstevel * Copy the new base into the targets mc registers
101403831d35Sstevel */
101503831d35Sstevel for (i = 0; i < t_num; i++) {
101603831d35Sstevel if (sbdp_copy_regs(t_nodes[i], t_bdp->bpa, new_base, 0, rsp,
101703831d35Sstevel &m) < 0)
101803831d35Sstevel return (-1);
101903831d35Sstevel }
102003831d35Sstevel /*
102103831d35Sstevel * Copy the old base into the source mc registers
102203831d35Sstevel */
102303831d35Sstevel for (i = 0; i < s_num; i++) {
102403831d35Sstevel if (sbdp_copy_regs(s_nodes[i], s_bdp->bpa, old_base, 0, rsp,
102503831d35Sstevel &m) < 0)
102603831d35Sstevel return (-1);
102703831d35Sstevel }
102803831d35Sstevel /*
102903831d35Sstevel * Zero masr_addr value indicates the END.
103003831d35Sstevel */
103103831d35Sstevel rsp[m].masr_addr = 0ull;
103203831d35Sstevel rsp[m].masr = 0;
103303831d35Sstevel rsp[m].asi = 0;
103403831d35Sstevel m++;
103503831d35Sstevel
103603831d35Sstevel #ifdef DEBUG
103703831d35Sstevel {
103803831d35Sstevel int i;
103903831d35Sstevel
104003831d35Sstevel SBDP_DBG_MEM("dumping copy-rename script:\n");
104103831d35Sstevel for (i = 0; i < m; i++) {
104203831d35Sstevel SBDP_DBG_MEM("0x%lx = 0x%lx, asi 0x%x\n",
104303831d35Sstevel rsp[i].masr_addr, rsp[i].masr, rsp[i].asi);
104403831d35Sstevel }
104503831d35Sstevel DELAY(1000000);
104603831d35Sstevel }
104703831d35Sstevel #endif /* DEBUG */
104803831d35Sstevel
104903831d35Sstevel return (m * sizeof (sbdp_rename_script_t));
105003831d35Sstevel }
105103831d35Sstevel
105203831d35Sstevel /*
105303831d35Sstevel * EMU Activity Status Register needs to be read idle several times.
105403831d35Sstevel * See Panther PRM 12.5.
105503831d35Sstevel */
105603831d35Sstevel #define SBDP_MCU_IDLE_RETRIES 10
105703831d35Sstevel #define SBDP_MCU_IDLE_READS 3
105803831d35Sstevel
105903831d35Sstevel /*
106003831d35Sstevel * Using the "__relocatable" suffix informs DTrace providers (and anything
106103831d35Sstevel * else, for that matter) that this function's text may be manually relocated
106203831d35Sstevel * elsewhere before it is executed. That is, it cannot be safely instrumented
106303831d35Sstevel * with any methodology that is PC-relative.
106403831d35Sstevel */
106503831d35Sstevel static int
sbdp_copy_rename__relocatable(sbdp_cr_handle_t * hp,struct memlist * mlist,register sbdp_rename_script_t * rsp)106603831d35Sstevel sbdp_copy_rename__relocatable(sbdp_cr_handle_t *hp, struct memlist *mlist,
106703831d35Sstevel register sbdp_rename_script_t *rsp)
106803831d35Sstevel {
106903831d35Sstevel sbdp_cr_err_t err = SBDP_CR_OK;
107003831d35Sstevel size_t csize;
107103831d35Sstevel size_t linesize;
107203831d35Sstevel uint_t size;
107303831d35Sstevel uint64_t caddr;
107403831d35Sstevel uint64_t s_base, t_base;
107503831d35Sstevel sbdp_bd_t *s_sbp, *t_sbp;
107603831d35Sstevel struct memlist *ml;
107703831d35Sstevel sbdp_mc_idle_script_t *isp;
107803831d35Sstevel int i;
107903831d35Sstevel
108003831d35Sstevel caddr = ecache_flushaddr;
108103831d35Sstevel csize = (size_t)(cpunodes[CPU->cpu_id].ecache_size * 2);
108203831d35Sstevel linesize = (size_t)(cpunodes[CPU->cpu_id].ecache_linesize);
108303831d35Sstevel
108403831d35Sstevel size = 0;
108503831d35Sstevel s_sbp = hp->s_bdp;
108603831d35Sstevel t_sbp = hp->t_bdp;
108703831d35Sstevel
108803831d35Sstevel s_base = (uint64_t)s_sbp->bpa;
108903831d35Sstevel t_base = (uint64_t)t_sbp->bpa;
109003831d35Sstevel
109103831d35Sstevel hp->ret = s_base;
109203831d35Sstevel /*
109303831d35Sstevel * DO COPY.
109403831d35Sstevel */
1095*56f33205SJonathan Adams for (ml = mlist; ml; ml = ml->ml_next) {
109603831d35Sstevel uint64_t s_pa, t_pa;
109703831d35Sstevel uint64_t nbytes;
109803831d35Sstevel
1099*56f33205SJonathan Adams s_pa = ml->ml_address;
1100*56f33205SJonathan Adams t_pa = t_base + (ml->ml_address - s_base);
1101*56f33205SJonathan Adams nbytes = ml->ml_size;
110203831d35Sstevel
110303831d35Sstevel size += nbytes;
110403831d35Sstevel while (nbytes != 0ull) {
110503831d35Sstevel /*
110603831d35Sstevel * This copy does NOT use an ASI
110703831d35Sstevel * that avoids the Ecache, therefore
110803831d35Sstevel * the dst_pa addresses may remain
110903831d35Sstevel * in our Ecache after the dst_pa
111003831d35Sstevel * has been removed from the system.
111103831d35Sstevel * A subsequent write-back to memory
111203831d35Sstevel * will cause an ARB-stop because the
111303831d35Sstevel * physical address no longer exists
111403831d35Sstevel * in the system. Therefore we must
111503831d35Sstevel * flush out local Ecache after we
111603831d35Sstevel * finish the copy.
111703831d35Sstevel */
111803831d35Sstevel
111903831d35Sstevel /* copy 32 bytes at src_pa to dst_pa */
112003831d35Sstevel bcopy32_il(s_pa, t_pa);
112103831d35Sstevel
112203831d35Sstevel /* increment by 32 bytes */
112303831d35Sstevel s_pa += (4 * sizeof (uint64_t));
112403831d35Sstevel t_pa += (4 * sizeof (uint64_t));
112503831d35Sstevel
112603831d35Sstevel /* decrement by 32 bytes */
112703831d35Sstevel nbytes -= (4 * sizeof (uint64_t));
112803831d35Sstevel }
112903831d35Sstevel }
113003831d35Sstevel
113103831d35Sstevel /*
113203831d35Sstevel * Since bcopy32_il() does NOT use an ASI to bypass
113303831d35Sstevel * the Ecache, we need to flush our Ecache after
113403831d35Sstevel * the copy is complete.
113503831d35Sstevel */
113603831d35Sstevel flush_ecache_il(caddr, csize, linesize); /* inline version */
113703831d35Sstevel
113803831d35Sstevel /*
113903831d35Sstevel * Non-Panther MCs are idled by reading each physical bank.
114003831d35Sstevel */
114103831d35Sstevel for (i = 0; rsp[i].asi == ASI_MEM; i++) {
114203831d35Sstevel (void) lddphys_il(rsp[i].masr_addr);
114303831d35Sstevel }
114403831d35Sstevel
114503831d35Sstevel isp = (sbdp_mc_idle_script_t *)&rsp[i];
114603831d35Sstevel
114703831d35Sstevel /*
114803831d35Sstevel * Panther MCs are idled by polling until the MCU idle state
114903831d35Sstevel * is read SBDP_MCU_IDLE_READS times in succession.
115003831d35Sstevel */
115103831d35Sstevel while (isp->addr != 0ull) {
115203831d35Sstevel for (i = 0; i < SBDP_MCU_IDLE_RETRIES; i++) {
115303831d35Sstevel register uint64_t v;
115403831d35Sstevel register int n_idle = 0;
115503831d35Sstevel
115603831d35Sstevel
115703831d35Sstevel do {
115803831d35Sstevel v = ldxasi_il(isp->addr, isp->asi) &
115903831d35Sstevel MCU_ACT_STATUS;
116003831d35Sstevel } while (v != MCU_ACT_STATUS &&
116103831d35Sstevel ++n_idle < SBDP_MCU_IDLE_READS);
116203831d35Sstevel
116303831d35Sstevel if (n_idle == SBDP_MCU_IDLE_READS)
116403831d35Sstevel break;
116503831d35Sstevel }
116603831d35Sstevel
116703831d35Sstevel if (i == SBDP_MCU_IDLE_RETRIES) {
116803831d35Sstevel /* bailout */
116903831d35Sstevel hp->busy_mc = isp;
117003831d35Sstevel return (SBDP_CR_MC_IDLE_ERR);
117103831d35Sstevel }
117203831d35Sstevel
117303831d35Sstevel isp++;
117403831d35Sstevel }
117503831d35Sstevel
117603831d35Sstevel /* skip terminator */
117703831d35Sstevel isp++;
117803831d35Sstevel
117903831d35Sstevel /*
118003831d35Sstevel * The following inline assembly routine caches
118103831d35Sstevel * the rename script and then caches the code that
118203831d35Sstevel * will do the rename. This is necessary
118303831d35Sstevel * so that we don't have any memory references during
118403831d35Sstevel * the reprogramming. We accomplish this by first
118503831d35Sstevel * jumping through the code to guarantee it's cached
118603831d35Sstevel * before we actually execute it.
118703831d35Sstevel */
118803831d35Sstevel sbdp_exec_script_il((sbdp_rename_script_t *)isp);
118903831d35Sstevel
119003831d35Sstevel return (err);
119103831d35Sstevel }
119203831d35Sstevel static void
_sbdp_copy_rename_end(void)119303831d35Sstevel _sbdp_copy_rename_end(void)
119403831d35Sstevel {
119503831d35Sstevel /*
119603831d35Sstevel * IMPORTANT: This function's location MUST be located immediately
119703831d35Sstevel * following sbdp_copy_rename__relocatable to accurately
119803831d35Sstevel * estimate its size. Note that this assumes (!)the
119903831d35Sstevel * compiler keeps these functions in the order in which
120003831d35Sstevel * they appear :-o
120103831d35Sstevel */
120203831d35Sstevel }
120303831d35Sstevel int
sbdp_memory_rename(sbdp_handle_t * hp)120403831d35Sstevel sbdp_memory_rename(sbdp_handle_t *hp)
120503831d35Sstevel {
120603831d35Sstevel #ifdef lint
120703831d35Sstevel /*
120803831d35Sstevel * Delete when implemented
120903831d35Sstevel */
121003831d35Sstevel hp = hp;
121103831d35Sstevel #endif
121203831d35Sstevel return (0);
121303831d35Sstevel }
121403831d35Sstevel
121503831d35Sstevel
121603831d35Sstevel /*
121703831d35Sstevel * In Serengeti this is a nop
121803831d35Sstevel */
121903831d35Sstevel int
sbdp_post_configure_mem(sbdp_handle_t * hp)122003831d35Sstevel sbdp_post_configure_mem(sbdp_handle_t *hp)
122103831d35Sstevel {
122203831d35Sstevel #ifdef lint
122303831d35Sstevel hp = hp;
122403831d35Sstevel #endif
122503831d35Sstevel return (0);
122603831d35Sstevel }
122703831d35Sstevel
122803831d35Sstevel /*
122903831d35Sstevel * In Serengeti this is a nop
123003831d35Sstevel */
123103831d35Sstevel int
sbdp_post_unconfigure_mem(sbdp_handle_t * hp)123203831d35Sstevel sbdp_post_unconfigure_mem(sbdp_handle_t *hp)
123303831d35Sstevel {
123403831d35Sstevel #ifdef lint
123503831d35Sstevel hp = hp;
123603831d35Sstevel #endif
123703831d35Sstevel return (0);
123803831d35Sstevel }
123903831d35Sstevel
124003831d35Sstevel /* ARGSUSED */
124103831d35Sstevel int
sbdphw_disable_memctrl(sbdp_handle_t * hp,dev_info_t * dip)124203831d35Sstevel sbdphw_disable_memctrl(sbdp_handle_t *hp, dev_info_t *dip)
124303831d35Sstevel {
124403831d35Sstevel return (0);
124503831d35Sstevel }
124603831d35Sstevel
124703831d35Sstevel /* ARGSUSED */
124803831d35Sstevel int
sbdphw_enable_memctrl(sbdp_handle_t * hp,dev_info_t * dip)124903831d35Sstevel sbdphw_enable_memctrl(sbdp_handle_t *hp, dev_info_t *dip)
125003831d35Sstevel {
125103831d35Sstevel return (0);
125203831d35Sstevel }
125303831d35Sstevel
125403831d35Sstevel /*
125503831d35Sstevel * We are assuming one memory node therefore the base address is the lowest
125603831d35Sstevel * segment possible
125703831d35Sstevel */
125803831d35Sstevel #define PA_ABOVE_MAX (0x8000000000000000ull)
125903831d35Sstevel int
sbdphw_get_base_physaddr(sbdp_handle_t * hp,dev_info_t * dip,uint64_t * pa)126003831d35Sstevel sbdphw_get_base_physaddr(sbdp_handle_t *hp, dev_info_t *dip, uint64_t *pa)
126103831d35Sstevel {
126203831d35Sstevel _NOTE(ARGUNUSED(hp))
126303831d35Sstevel
126403831d35Sstevel int i, board = -1, wnode;
126503831d35Sstevel pnode_t nodeid;
126603831d35Sstevel struct mem_arg arg = {0};
126703831d35Sstevel uint64_t seg_pa, tmp_pa;
126803831d35Sstevel dev_info_t *list[SBDP_MAX_MEM_NODES_PER_BOARD];
126903831d35Sstevel int rc;
127003831d35Sstevel
127103831d35Sstevel if (dip == NULL)
127203831d35Sstevel return (-1);
127303831d35Sstevel
127403831d35Sstevel nodeid = ddi_get_nodeid(dip);
127503831d35Sstevel
127603831d35Sstevel if (sbdp_get_bd_and_wnode_num(nodeid, &board, &wnode) < 0)
127703831d35Sstevel return (-1);
127803831d35Sstevel
127903831d35Sstevel list[0] = NULL;
128003831d35Sstevel arg.board = board;
128103831d35Sstevel arg.list = list;
128203831d35Sstevel
128303831d35Sstevel (void) sbdp_walk_prom_tree(prom_rootnode(), sbdp_get_mem_dip, &arg);
128403831d35Sstevel
128503831d35Sstevel if (arg.ndips <= 0)
128603831d35Sstevel return (-1);
128703831d35Sstevel
128803831d35Sstevel seg_pa = PA_ABOVE_MAX;
128903831d35Sstevel
129003831d35Sstevel rc = -1;
129103831d35Sstevel for (i = 0; i < arg.ndips; i++) {
129203831d35Sstevel if (list[i] == NULL)
129303831d35Sstevel continue;
129403831d35Sstevel if (sbdp_get_lowest_addr_in_node(ddi_get_nodeid(list[i]),
129503831d35Sstevel &tmp_pa) == 0) {
129603831d35Sstevel rc = 0;
129703831d35Sstevel if (tmp_pa < seg_pa)
129803831d35Sstevel seg_pa = tmp_pa;
129903831d35Sstevel }
130003831d35Sstevel
130103831d35Sstevel /*
130203831d35Sstevel * Release hold acquired in sbdp_get_mem_dip()
130303831d35Sstevel */
130403831d35Sstevel ddi_release_devi(list[i]);
130503831d35Sstevel }
130603831d35Sstevel
130703831d35Sstevel if (rc == 0)
130803831d35Sstevel *pa = seg_pa;
130903831d35Sstevel else {
131003831d35Sstevel /*
131103831d35Sstevel * Record the fact that an error has occurred
131203831d35Sstevel */
131303831d35Sstevel sbdp_set_err(hp->h_err, ESGT_INTERNAL, NULL);
131403831d35Sstevel }
131503831d35Sstevel
131603831d35Sstevel return (rc);
131703831d35Sstevel }
131803831d35Sstevel
131903831d35Sstevel static int
sbdp_get_lowest_addr_in_node(pnode_t node,uint64_t * pa)132003831d35Sstevel sbdp_get_lowest_addr_in_node(pnode_t node, uint64_t *pa)
132103831d35Sstevel {
132203831d35Sstevel uint64_t mc_decode, seg_pa, tmp_pa;
132303831d35Sstevel mc_regs_t mc_regs, *mc_regsp = &mc_regs;
132403831d35Sstevel int i, valid;
132503831d35Sstevel int rc;
132603831d35Sstevel
132703831d35Sstevel
132803831d35Sstevel seg_pa = PA_ABOVE_MAX;
132903831d35Sstevel
133003831d35Sstevel if (mc_read_regs(node, mc_regsp)) {
133103831d35Sstevel SBDP_DBG_MEM("sbdp_get_lowest_addr_in_node: failed to "
133203831d35Sstevel "read source Decode Regs\n");
133303831d35Sstevel return (-1);
133403831d35Sstevel }
133503831d35Sstevel
133603831d35Sstevel rc = -1;
133703831d35Sstevel for (i = 0; i < SBDP_MAX_MCS_PER_NODE; i++) {
133803831d35Sstevel mc_decode = mc_regsp->mc_decode[i];
133903831d35Sstevel valid = mc_decode >> MC_VALID_SHIFT;
134003831d35Sstevel tmp_pa = MC_BASE(mc_decode) << PHYS2UM_SHIFT;
134103831d35Sstevel if (valid)
134203831d35Sstevel rc = 0;
134303831d35Sstevel if (valid && (tmp_pa < seg_pa))
134403831d35Sstevel seg_pa = tmp_pa;
134503831d35Sstevel }
134603831d35Sstevel
134703831d35Sstevel if (rc == 0)
134803831d35Sstevel *pa = seg_pa;
134903831d35Sstevel
135003831d35Sstevel return (rc);
135103831d35Sstevel }
135203831d35Sstevel
135303831d35Sstevel int
sbdp_is_mem(pnode_t node,void * arg)135403831d35Sstevel sbdp_is_mem(pnode_t node, void *arg)
135503831d35Sstevel {
135603831d35Sstevel mem_op_t *memp = (mem_op_t *)arg;
135703831d35Sstevel char type[OBP_MAXPROPNAME];
135803831d35Sstevel int bd;
135903831d35Sstevel pnode_t *list;
136003831d35Sstevel int board;
136103831d35Sstevel char name[OBP_MAXDRVNAME];
136203831d35Sstevel int len;
136303831d35Sstevel
136403831d35Sstevel ASSERT(memp);
136503831d35Sstevel
136603831d35Sstevel list = memp->nodes;
136703831d35Sstevel board = memp->board;
136803831d35Sstevel
136903831d35Sstevel /*
137003831d35Sstevel * Make sure that this node doesn't have its status
137103831d35Sstevel * as failed
137203831d35Sstevel */
137303831d35Sstevel if (sbdp_get_comp_status(node) != SBD_COND_OK) {
137403831d35Sstevel return (DDI_FAILURE);
137503831d35Sstevel }
137603831d35Sstevel
137703831d35Sstevel len = prom_getproplen(node, "device_type");
137803831d35Sstevel if ((len > 0) && (len < OBP_MAXPROPNAME))
137903831d35Sstevel (void) prom_getprop(node, "device_type", (caddr_t)type);
138003831d35Sstevel else
138103831d35Sstevel type[0] = '\0';
138203831d35Sstevel
138303831d35Sstevel if (strcmp(type, "memory-controller") == 0) {
138403831d35Sstevel int wnode;
138503831d35Sstevel
138603831d35Sstevel if (sbdp_get_bd_and_wnode_num(node, &bd, &wnode) < 0)
138703831d35Sstevel return (DDI_FAILURE);
138803831d35Sstevel
138903831d35Sstevel if (bd == board) {
139003831d35Sstevel /*
139103831d35Sstevel * Make sure we don't overwrite the array
139203831d35Sstevel */
139303831d35Sstevel if (memp->nmem >= SBDP_MAX_MEM_NODES_PER_BOARD)
139403831d35Sstevel return (DDI_FAILURE);
139503831d35Sstevel (void) prom_getprop(node, OBP_NAME, (caddr_t)name);
139603831d35Sstevel SBDP_DBG_MEM("name %s boot bd %d board %d\n", name,
139703831d35Sstevel board, bd);
139803831d35Sstevel list[memp->nmem++] = node;
139903831d35Sstevel return (DDI_SUCCESS);
140003831d35Sstevel }
140103831d35Sstevel }
140203831d35Sstevel
140303831d35Sstevel return (DDI_FAILURE);
140403831d35Sstevel }
140503831d35Sstevel
140603831d35Sstevel static int
sbdp_get_meminfo(pnode_t nodeid,int mc,uint64_t * size,uint64_t * base_pa)140703831d35Sstevel sbdp_get_meminfo(pnode_t nodeid, int mc, uint64_t *size, uint64_t *base_pa)
140803831d35Sstevel {
140903831d35Sstevel int board, wnode;
141003831d35Sstevel int valid;
141103831d35Sstevel mc_regs_t mc_regs, *mc_regsp = &mc_regs;
141203831d35Sstevel uint64_t mc_decode = 0;
141303831d35Sstevel
141403831d35Sstevel if (sbdp_get_bd_and_wnode_num(nodeid, &board, &wnode) < 0)
141503831d35Sstevel return (-1);
141603831d35Sstevel
141703831d35Sstevel if (mc_read_regs(nodeid, mc_regsp)) {
141803831d35Sstevel SBDP_DBG_MEM("sbdp_get_meminfo: failed to read source "
141903831d35Sstevel "Decode Regs");
142003831d35Sstevel return (-1);
142103831d35Sstevel }
142203831d35Sstevel /*
142303831d35Sstevel * Calculate memory size
142403831d35Sstevel */
142503831d35Sstevel mc_decode = mc_regsp->mc_decode[mc];
142603831d35Sstevel
142703831d35Sstevel /*
142803831d35Sstevel * Check the valid bit to see if bank is there
142903831d35Sstevel */
143003831d35Sstevel valid = mc_decode >> MC_VALID_SHIFT;
143103831d35Sstevel if (valid) {
143203831d35Sstevel *size = MC_UK2SPAN(mc_decode);
143303831d35Sstevel *base_pa = MC_BASE(mc_decode) << PHYS2UM_SHIFT;
143403831d35Sstevel }
143503831d35Sstevel
143603831d35Sstevel return (0);
143703831d35Sstevel }
143803831d35Sstevel
143903831d35Sstevel
144003831d35Sstevel /*
144103831d35Sstevel * Luckily for us mem nodes and cpu/CMP nodes are siblings. All we need to
144203831d35Sstevel * do is search in the same branch as the mem node for its sibling cpu or
144303831d35Sstevel * CMP node.
144403831d35Sstevel */
144503831d35Sstevel pnode_t
mc_get_sibling_cpu(pnode_t nodeid)144603831d35Sstevel mc_get_sibling_cpu(pnode_t nodeid)
144703831d35Sstevel {
144803831d35Sstevel int portid;
144903831d35Sstevel
145003831d35Sstevel if (prom_getprop(nodeid, OBP_PORTID, (caddr_t)&portid) < 0)
145103831d35Sstevel return (OBP_NONODE);
145203831d35Sstevel
145303831d35Sstevel /*
145403831d35Sstevel * cpus and memory are siblings so we don't need to traverse
145503831d35Sstevel * the whole tree, just a branch
145603831d35Sstevel */
145703831d35Sstevel return (sbdp_find_nearby_cpu_by_portid(nodeid, portid));
145803831d35Sstevel }
145903831d35Sstevel
146003831d35Sstevel /*
146103831d35Sstevel * Given a memory node, check it's sibling cpu or CMP to see if
146203831d35Sstevel * access to mem will be ok. We need to search for the node and
146303831d35Sstevel * if found get its condition.
146403831d35Sstevel */
146503831d35Sstevel sbd_cond_t
mc_check_sibling_cpu(pnode_t nodeid)146603831d35Sstevel mc_check_sibling_cpu(pnode_t nodeid)
146703831d35Sstevel {
146803831d35Sstevel pnode_t cpu_node;
146903831d35Sstevel sbd_cond_t cond;
147003831d35Sstevel int i;
147103831d35Sstevel
147203831d35Sstevel cpu_node = mc_get_sibling_cpu(nodeid);
147303831d35Sstevel
147403831d35Sstevel cond = sbdp_get_comp_status(cpu_node);
147503831d35Sstevel
147603831d35Sstevel if (cond == SBD_COND_OK) {
147703831d35Sstevel int wnode;
147803831d35Sstevel int bd;
147903831d35Sstevel int unit;
148003831d35Sstevel int portid;
148103831d35Sstevel
148203831d35Sstevel if (sbdp_get_bd_and_wnode_num(nodeid, &bd, &wnode) < 0)
148303831d35Sstevel return (SBD_COND_UNKNOWN);
148403831d35Sstevel
148503831d35Sstevel (void) prom_getprop(nodeid, OBP_PORTID, (caddr_t)&portid);
148603831d35Sstevel
148703831d35Sstevel /*
148803831d35Sstevel * Access to the memory controller should not
148903831d35Sstevel * be attempted if any of the cores are marked
149003831d35Sstevel * as being in reset.
149103831d35Sstevel */
149225cf1a30Sjl139090 for (i = 0; i < SBDP_MAX_CORES_PER_CMP; i++) {
149303831d35Sstevel unit = SG_PORTID_TO_CPU_UNIT(portid, i);
149403831d35Sstevel if (sbdp_is_cpu_present(wnode, bd, unit) &&
149503831d35Sstevel sbdp_is_cpu_in_reset(wnode, bd, unit)) {
149603831d35Sstevel cond = SBD_COND_UNUSABLE;
149703831d35Sstevel break;
149803831d35Sstevel }
149903831d35Sstevel }
150003831d35Sstevel }
150103831d35Sstevel
150203831d35Sstevel return (cond);
150303831d35Sstevel }
150403831d35Sstevel
150503831d35Sstevel int
mc_read_regs(pnode_t nodeid,mc_regs_t * mc_regsp)150603831d35Sstevel mc_read_regs(pnode_t nodeid, mc_regs_t *mc_regsp)
150703831d35Sstevel {
150803831d35Sstevel int len;
150903831d35Sstevel uint64_t mc_addr, mask;
151003831d35Sstevel mc_regspace reg;
151103831d35Sstevel sbd_cond_t sibling_cpu_cond;
151203831d35Sstevel int local_mc;
151303831d35Sstevel int portid;
151403831d35Sstevel int i;
151503831d35Sstevel
151603831d35Sstevel if ((prom_getprop(nodeid, "portid", (caddr_t)&portid) < 0) ||
151703831d35Sstevel (portid == -1))
151803831d35Sstevel return (-1);
151903831d35Sstevel
152003831d35Sstevel /*
152103831d35Sstevel * mc should not be accessed if their corresponding cpu
152203831d35Sstevel * has failed.
152303831d35Sstevel */
152403831d35Sstevel sibling_cpu_cond = mc_check_sibling_cpu(nodeid);
152503831d35Sstevel
152603831d35Sstevel if ((sibling_cpu_cond == SBD_COND_FAILED) ||
152703831d35Sstevel (sibling_cpu_cond == SBD_COND_UNUSABLE)) {
152803831d35Sstevel return (-1);
152903831d35Sstevel }
153003831d35Sstevel
153103831d35Sstevel len = prom_getproplen(nodeid, "reg");
153203831d35Sstevel if (len != sizeof (mc_regspace))
153303831d35Sstevel return (-1);
153403831d35Sstevel
153503831d35Sstevel if (prom_getprop(nodeid, "reg", (caddr_t)®) < 0)
153603831d35Sstevel return (-1);
153703831d35Sstevel
153803831d35Sstevel mc_addr = ((uint64_t)reg.regspec_addr_hi) << 32;
153903831d35Sstevel mc_addr |= (uint64_t)reg.regspec_addr_lo;
154003831d35Sstevel
154103831d35Sstevel /*
154203831d35Sstevel * Make sure we don't switch cpus
154303831d35Sstevel */
154403831d35Sstevel affinity_set(CPU_CURRENT);
154503831d35Sstevel if (portid == cpunodes[CPU->cpu_id].portid)
154603831d35Sstevel local_mc = 1;
154703831d35Sstevel else
154803831d35Sstevel local_mc = 0;
154903831d35Sstevel
155003831d35Sstevel for (i = 0; i < SG_MAX_BANKS_PER_MC; i++) {
155103831d35Sstevel mask = SG_REG_2_OFFSET(i);
155203831d35Sstevel
155303831d35Sstevel /*
155403831d35Sstevel * If the memory controller is local to this CPU, we use
155503831d35Sstevel * the special ASI to read the decode registers.
155603831d35Sstevel * Otherwise, we load the values from a magic address in
155703831d35Sstevel * I/O space.
155803831d35Sstevel */
155903831d35Sstevel if (local_mc) {
156003831d35Sstevel mc_regsp->mc_decode[i] = lddmcdecode(
156103831d35Sstevel mask & MC_OFFSET_MASK);
156203831d35Sstevel } else {
156303831d35Sstevel mc_regsp->mc_decode[i] = lddphysio(
156403831d35Sstevel (mc_addr | mask));
156503831d35Sstevel }
156603831d35Sstevel }
156703831d35Sstevel affinity_clear();
156803831d35Sstevel
156903831d35Sstevel return (0);
157003831d35Sstevel }
157103831d35Sstevel
157203831d35Sstevel uint64_t
mc_get_addr(pnode_t nodeid,int mc,uint_t * asi)157303831d35Sstevel mc_get_addr(pnode_t nodeid, int mc, uint_t *asi)
157403831d35Sstevel {
157503831d35Sstevel int len;
157603831d35Sstevel uint64_t mc_addr, addr;
157703831d35Sstevel mc_regspace reg;
157803831d35Sstevel int portid;
157903831d35Sstevel int local_mc;
158003831d35Sstevel
158103831d35Sstevel if ((prom_getprop(nodeid, "portid", (caddr_t)&portid) < 0) ||
158203831d35Sstevel (portid == -1))
158303831d35Sstevel return (-1);
158403831d35Sstevel
158503831d35Sstevel len = prom_getproplen(nodeid, "reg");
158603831d35Sstevel if (len != sizeof (mc_regspace))
158703831d35Sstevel return (-1);
158803831d35Sstevel
158903831d35Sstevel if (prom_getprop(nodeid, "reg", (caddr_t)®) < 0)
159003831d35Sstevel return (-1);
159103831d35Sstevel
159203831d35Sstevel mc_addr = ((uint64_t)reg.regspec_addr_hi) << 32;
159303831d35Sstevel mc_addr |= (uint64_t)reg.regspec_addr_lo;
159403831d35Sstevel
159503831d35Sstevel /*
159603831d35Sstevel * Make sure we don't switch cpus
159703831d35Sstevel */
159803831d35Sstevel affinity_set(CPU_CURRENT);
159903831d35Sstevel if (portid == cpunodes[CPU->cpu_id].portid)
160003831d35Sstevel local_mc = 1;
160103831d35Sstevel else
160203831d35Sstevel local_mc = 0;
160303831d35Sstevel
160403831d35Sstevel if (local_mc) {
160503831d35Sstevel *asi = ASI_MC_DECODE;
160603831d35Sstevel addr = SG_REG_2_OFFSET(mc) & MC_OFFSET_MASK;
160703831d35Sstevel } else {
160803831d35Sstevel *asi = ASI_IO;
160903831d35Sstevel addr = SG_REG_2_OFFSET(mc) | mc_addr;
161003831d35Sstevel }
161103831d35Sstevel affinity_clear();
161203831d35Sstevel
161303831d35Sstevel return (addr);
161403831d35Sstevel }
161503831d35Sstevel
161603831d35Sstevel /* ARGSUSED */
161703831d35Sstevel int
sbdp_mem_add_span(sbdp_handle_t * hp,uint64_t address,uint64_t size)161803831d35Sstevel sbdp_mem_add_span(sbdp_handle_t *hp, uint64_t address, uint64_t size)
161903831d35Sstevel {
162003831d35Sstevel return (0);
162103831d35Sstevel }
162203831d35Sstevel
162303831d35Sstevel int
sbdp_mem_del_span(sbdp_handle_t * hp,uint64_t address,uint64_t size)162403831d35Sstevel sbdp_mem_del_span(sbdp_handle_t *hp, uint64_t address, uint64_t size)
162503831d35Sstevel {
162603831d35Sstevel pfn_t basepfn = (pfn_t)(address >> PAGESHIFT);
162703831d35Sstevel pgcnt_t npages = (pgcnt_t)(size >> PAGESHIFT);
162803831d35Sstevel
162903831d35Sstevel if (size > 0) {
163003831d35Sstevel int rv;
163103831d35Sstevel rv = kcage_range_delete_post_mem_del(basepfn, npages);
163203831d35Sstevel if (rv != 0) {
163303831d35Sstevel cmn_err(CE_WARN,
163403831d35Sstevel "unexpected kcage_range_delete_post_mem_del"
163503831d35Sstevel " return value %d", rv);
163603831d35Sstevel sbdp_set_err(hp->h_err, ESGT_INTERNAL, NULL);
163703831d35Sstevel return (-1);
163803831d35Sstevel }
163903831d35Sstevel }
164003831d35Sstevel return (0);
164103831d35Sstevel }
164203831d35Sstevel
164303831d35Sstevel /*
164403831d35Sstevel * This routine gets the size including the
164503831d35Sstevel * bad banks
164603831d35Sstevel */
164703831d35Sstevel int
sbdp_get_mem_size(sbdp_handle_t * hp)164803831d35Sstevel sbdp_get_mem_size(sbdp_handle_t *hp)
164903831d35Sstevel {
165003831d35Sstevel uint64_t size = 0;
165103831d35Sstevel struct memlist *mlist, *ml;
165203831d35Sstevel
165303831d35Sstevel mlist = sbdp_get_memlist(hp, (dev_info_t *)NULL);
165403831d35Sstevel
1655*56f33205SJonathan Adams for (ml = mlist; ml; ml = ml->ml_next)
1656*56f33205SJonathan Adams size += ml->ml_size;
165703831d35Sstevel
165803831d35Sstevel (void) sbdp_del_memlist(hp, mlist);
165903831d35Sstevel
166003831d35Sstevel SBDP_DBG_MEM("sbdp_get_mem_size: size 0x%" PRIx64 "\n", size);
166103831d35Sstevel
166203831d35Sstevel return (btop(size));
166303831d35Sstevel }
166403831d35Sstevel
166503831d35Sstevel /*
166603831d35Sstevel * This function compares the list of banks passed with the banks
166703831d35Sstevel * in the segment
166803831d35Sstevel */
166903831d35Sstevel int
sbdp_check_seg_with_banks(sbdp_seg_t * seg,sbdp_bank_t * banks)167003831d35Sstevel sbdp_check_seg_with_banks(sbdp_seg_t *seg, sbdp_bank_t *banks)
167103831d35Sstevel {
167203831d35Sstevel sbdp_bank_t *cur_bank, *bank;
167303831d35Sstevel int i = 0;
167403831d35Sstevel
167503831d35Sstevel for (cur_bank = seg->banks; cur_bank; cur_bank = cur_bank->seg_next) {
167603831d35Sstevel for (bank = banks; bank; bank = bank->bd_next) {
167703831d35Sstevel if (!bank->valid)
167803831d35Sstevel continue;
167903831d35Sstevel
168003831d35Sstevel if (cur_bank == bank) {
168103831d35Sstevel i++;
168203831d35Sstevel }
168303831d35Sstevel }
168403831d35Sstevel }
168503831d35Sstevel
168603831d35Sstevel SBDP_DBG_MEM("banks found = %d total banks = %d\n", i, seg->nbanks);
168703831d35Sstevel /*
168803831d35Sstevel * If we find the same num of banks that are equal, then this segment
168903831d35Sstevel * is not interleaved across boards
169003831d35Sstevel */
169103831d35Sstevel if (i == seg->nbanks)
169203831d35Sstevel return (0);
169303831d35Sstevel
169403831d35Sstevel return (1);
169503831d35Sstevel }
169603831d35Sstevel
169703831d35Sstevel
169803831d35Sstevel /*
169903831d35Sstevel * This routine determines if any of the memory banks on the board
170003831d35Sstevel * participate in across board memory interleaving
170103831d35Sstevel */
170203831d35Sstevel int
sbdp_isinterleaved(sbdp_handle_t * hp,dev_info_t * dip)170303831d35Sstevel sbdp_isinterleaved(sbdp_handle_t *hp, dev_info_t *dip)
170403831d35Sstevel {
170503831d35Sstevel _NOTE(ARGUNUSED(dip))
170603831d35Sstevel
170703831d35Sstevel sbdp_bank_t *bankp;
170803831d35Sstevel int wnode, board;
170903831d35Sstevel int is_interleave = 0;
171003831d35Sstevel sbdp_bd_t *bdp;
171103831d35Sstevel uint64_t base;
171203831d35Sstevel sbdp_seg_t *seg;
171303831d35Sstevel
171403831d35Sstevel board = hp->h_board;
171503831d35Sstevel wnode = hp->h_wnode;
171603831d35Sstevel
171703831d35Sstevel #ifdef DEBUG
171803831d35Sstevel sbdp_print_all_segs();
171903831d35Sstevel #endif
172003831d35Sstevel /*
172103831d35Sstevel * Get the banks for this board
172203831d35Sstevel */
172303831d35Sstevel bdp = sbdp_get_bd_info(wnode, board);
172403831d35Sstevel
172503831d35Sstevel if (bdp == NULL)
172603831d35Sstevel return (-1);
172703831d35Sstevel
172803831d35Sstevel /*
172903831d35Sstevel * Search for the first bank with valid memory
173003831d35Sstevel */
173103831d35Sstevel for (bankp = bdp->banks; bankp; bankp = bankp->bd_next)
173203831d35Sstevel if (bankp->valid)
173303831d35Sstevel break;
173403831d35Sstevel
173503831d35Sstevel /*
173603831d35Sstevel * If there are no banks in the board, then the board is
173703831d35Sstevel * not interleaved across boards
173803831d35Sstevel */
173903831d35Sstevel if (bankp == NULL) {
174003831d35Sstevel return (0);
174103831d35Sstevel }
174203831d35Sstevel
174303831d35Sstevel base = bankp->um & ~(bankp->uk);
174403831d35Sstevel
174503831d35Sstevel /*
174603831d35Sstevel * Find the segment for the first bank
174703831d35Sstevel */
174803831d35Sstevel if ((seg = sbdp_get_seg(base)) == NULL) {
174903831d35Sstevel /*
175003831d35Sstevel * Something bad has happened.
175103831d35Sstevel */
175203831d35Sstevel return (-1);
175303831d35Sstevel }
175403831d35Sstevel /*
175503831d35Sstevel * Make sure that this segment is only composed of the banks
175603831d35Sstevel * in this board. If one is missing or we have an extra one
175703831d35Sstevel * the board is interleaved across boards
175803831d35Sstevel */
175903831d35Sstevel is_interleave = sbdp_check_seg_with_banks(seg, bdp->banks);
176003831d35Sstevel
176103831d35Sstevel SBDP_DBG_MEM("interleave is %d\n", is_interleave);
176203831d35Sstevel
176303831d35Sstevel return (is_interleave);
176403831d35Sstevel }
176503831d35Sstevel
176603831d35Sstevel
176703831d35Sstevel /*
176803831d35Sstevel * Each node has 4 logical banks. This routine adds all the banks (including
176903831d35Sstevel * the invalid ones to the passed list. Note that we use the bd list and not
177003831d35Sstevel * the seg list
177103831d35Sstevel */
177203831d35Sstevel int
sbdp_add_nodes_banks(pnode_t node,sbdp_bank_t ** banks)177303831d35Sstevel sbdp_add_nodes_banks(pnode_t node, sbdp_bank_t **banks)
177403831d35Sstevel {
177503831d35Sstevel int i;
177603831d35Sstevel mc_regs_t regs;
177703831d35Sstevel uint64_t *mc_decode;
177803831d35Sstevel sbdp_bank_t *bank;
177903831d35Sstevel
178003831d35Sstevel if (mc_read_regs(node, ®s) == -1)
178103831d35Sstevel return (-1);
178203831d35Sstevel
178303831d35Sstevel mc_decode = regs.mc_decode;
178403831d35Sstevel
178503831d35Sstevel for (i = 0; i < SBDP_MAX_MCS_PER_NODE; i++) {
178603831d35Sstevel /*
178703831d35Sstevel * This creates the mem for the new member of the list
178803831d35Sstevel */
178903831d35Sstevel sbdp_fill_bank_info(mc_decode[i], &bank);
179003831d35Sstevel
179103831d35Sstevel SBDP_DBG_MEM("adding bank %d\n", bank->id);
179203831d35Sstevel
179303831d35Sstevel /*
179403831d35Sstevel * Insert bank into the beginning of the list
179503831d35Sstevel */
179603831d35Sstevel bank->bd_next = *banks;
179703831d35Sstevel *banks = bank;
179803831d35Sstevel
179903831d35Sstevel /*
180003831d35Sstevel * Add this bank into its corresponding
180103831d35Sstevel * segment
180203831d35Sstevel */
180303831d35Sstevel sbdp_add_bank_to_seg(bank);
180403831d35Sstevel }
180503831d35Sstevel return (0);
180603831d35Sstevel }
180703831d35Sstevel
180803831d35Sstevel /*
180903831d35Sstevel * given the info, create a new bank node and set the info
181003831d35Sstevel * as appropriate. We allocate the memory for the bank. It is
181103831d35Sstevel * up to the caller to ensure the mem is freed
181203831d35Sstevel */
181303831d35Sstevel void
sbdp_fill_bank_info(uint64_t mc_decode,sbdp_bank_t ** bank)181403831d35Sstevel sbdp_fill_bank_info(uint64_t mc_decode, sbdp_bank_t **bank)
181503831d35Sstevel {
181603831d35Sstevel static int id = 0;
181703831d35Sstevel sbdp_bank_t *new;
181803831d35Sstevel
181903831d35Sstevel new = kmem_zalloc(sizeof (sbdp_bank_t), KM_SLEEP);
182003831d35Sstevel
182103831d35Sstevel new->id = id++;
182203831d35Sstevel new->valid = (mc_decode >> MC_VALID_SHIFT);
182303831d35Sstevel new->uk = MC_UK(mc_decode);
182403831d35Sstevel new->um = MC_UM(mc_decode);
182503831d35Sstevel new->lk = MC_LK(mc_decode);
182603831d35Sstevel new->lm = MC_LM(mc_decode);
182703831d35Sstevel new->bd_next = NULL;
182803831d35Sstevel new->seg_next = NULL;
182903831d35Sstevel
183003831d35Sstevel *bank = new;
183103831d35Sstevel }
183203831d35Sstevel
183303831d35Sstevel /*
183403831d35Sstevel * Each bd has the potential of having mem banks on it. The banks
183503831d35Sstevel * may be empty or not. This routine gets all the mem banks
183603831d35Sstevel * for this bd
183703831d35Sstevel */
183803831d35Sstevel void
sbdp_init_bd_banks(sbdp_bd_t * bdp)183903831d35Sstevel sbdp_init_bd_banks(sbdp_bd_t *bdp)
184003831d35Sstevel {
184103831d35Sstevel int i, nmem;
184203831d35Sstevel pnode_t *lists;
184303831d35Sstevel
184403831d35Sstevel lists = bdp->nodes;
184503831d35Sstevel nmem = bdp->nnum;
184603831d35Sstevel
184703831d35Sstevel if (bdp->banks != NULL) {
184803831d35Sstevel return;
184903831d35Sstevel }
185003831d35Sstevel
185103831d35Sstevel bdp->banks = NULL;
185203831d35Sstevel
185303831d35Sstevel for (i = 0; i < nmem; i++) {
185403831d35Sstevel (void) sbdp_add_nodes_banks(lists[i], &bdp->banks);
185503831d35Sstevel }
185603831d35Sstevel }
185703831d35Sstevel
185803831d35Sstevel /*
185903831d35Sstevel * swap the list of banks for the 2 boards
186003831d35Sstevel */
186103831d35Sstevel void
sbdp_swap_list_of_banks(sbdp_bd_t * bdp1,sbdp_bd_t * bdp2)186203831d35Sstevel sbdp_swap_list_of_banks(sbdp_bd_t *bdp1, sbdp_bd_t *bdp2)
186303831d35Sstevel {
186403831d35Sstevel sbdp_bank_t *tmp_ptr;
186503831d35Sstevel
186603831d35Sstevel if ((bdp1 == NULL) || (bdp2 == NULL))
186703831d35Sstevel return;
186803831d35Sstevel
186903831d35Sstevel tmp_ptr = bdp1->banks;
187003831d35Sstevel bdp1->banks = bdp2->banks;
187103831d35Sstevel bdp2->banks = tmp_ptr;
187203831d35Sstevel }
187303831d35Sstevel
187403831d35Sstevel /*
187503831d35Sstevel * free all the banks on the board. Note that a bank node belongs
187603831d35Sstevel * to 2 lists. The first list is the board list. The second one is
187703831d35Sstevel * the seg list. We only need to remove the bank from both lists but only
187803831d35Sstevel * free the node once.
187903831d35Sstevel */
188003831d35Sstevel void
sbdp_fini_bd_banks(sbdp_bd_t * bdp)188103831d35Sstevel sbdp_fini_bd_banks(sbdp_bd_t *bdp)
188203831d35Sstevel {
188303831d35Sstevel sbdp_bank_t *bkp, *nbkp;
188403831d35Sstevel
188503831d35Sstevel for (bkp = bdp->banks; bkp; ) {
188603831d35Sstevel /*
188703831d35Sstevel * Remove the bank from the seg list first
188803831d35Sstevel */
188903831d35Sstevel SBDP_DBG_MEM("Removing bank %d\n", bkp->id);
189003831d35Sstevel sbdp_remove_bank_from_seg(bkp);
189103831d35Sstevel nbkp = bkp->bd_next;
189203831d35Sstevel bkp->bd_next = NULL;
189303831d35Sstevel kmem_free(bkp, sizeof (sbdp_bank_t));
189403831d35Sstevel
189503831d35Sstevel bkp = nbkp;
189603831d35Sstevel }
189703831d35Sstevel bdp->banks = NULL;
189803831d35Sstevel }
189903831d35Sstevel
190003831d35Sstevel #ifdef DEBUG
190103831d35Sstevel void
sbdp_print_bd_banks(sbdp_bd_t * bdp)190203831d35Sstevel sbdp_print_bd_banks(sbdp_bd_t *bdp)
190303831d35Sstevel {
190403831d35Sstevel sbdp_bank_t *bp;
190503831d35Sstevel int i;
190603831d35Sstevel
190703831d35Sstevel SBDP_DBG_MEM("BOARD %d\n", bdp->bd);
190803831d35Sstevel
190903831d35Sstevel for (bp = bdp->banks, i = 0; bp; bp = bp->bd_next, i++) {
191003831d35Sstevel SBDP_DBG_MEM("BANK [%d]:\n", bp->id);
191103831d35Sstevel SBDP_DBG_MEM("\tvalid %d\tuk 0x%x\tum 0x%x\tlk 0x%x"
191203831d35Sstevel "\tlm 0x%x\n", bp->valid, bp->uk, bp->um,
191303831d35Sstevel bp->lk, bp->lm);
191403831d35Sstevel }
191503831d35Sstevel }
191603831d35Sstevel
191703831d35Sstevel void
sbdp_print_all_segs(void)191803831d35Sstevel sbdp_print_all_segs(void)
191903831d35Sstevel {
192003831d35Sstevel sbdp_seg_t *cur_seg;
192103831d35Sstevel
192203831d35Sstevel for (cur_seg = sys_seg; cur_seg; cur_seg = cur_seg->next)
192303831d35Sstevel sbdp_print_seg(cur_seg);
192403831d35Sstevel }
192503831d35Sstevel
192603831d35Sstevel void
sbdp_print_seg(sbdp_seg_t * seg)192703831d35Sstevel sbdp_print_seg(sbdp_seg_t *seg)
192803831d35Sstevel {
192903831d35Sstevel sbdp_bank_t *bp;
193003831d35Sstevel int i;
193103831d35Sstevel
193203831d35Sstevel SBDP_DBG_MEM("SEG %d\n", seg->id);
193303831d35Sstevel
193403831d35Sstevel for (bp = seg->banks, i = 0; bp; bp = bp->seg_next, i++) {
193503831d35Sstevel SBDP_DBG_MEM("BANK [%d]:\n", bp->id);
193603831d35Sstevel SBDP_DBG_MEM("\tvalid %d\tuk 0x%x\tum 0x%x\tlk 0x%x"
193703831d35Sstevel "\tlm 0x%x\n", bp->valid, bp->uk, bp->um,
193803831d35Sstevel bp->lk, bp->lm);
193903831d35Sstevel }
194003831d35Sstevel }
194103831d35Sstevel #endif
194203831d35Sstevel
194303831d35Sstevel void
sbdp_add_bank_to_seg(sbdp_bank_t * bank)194403831d35Sstevel sbdp_add_bank_to_seg(sbdp_bank_t *bank)
194503831d35Sstevel {
194603831d35Sstevel uint64_t base;
194703831d35Sstevel sbdp_seg_t *cur_seg;
194803831d35Sstevel static int id = 0;
194903831d35Sstevel
195003831d35Sstevel /*
195103831d35Sstevel * if we got an invalid bank just skip it
195203831d35Sstevel */
195303831d35Sstevel if (bank == NULL || !bank->valid)
195403831d35Sstevel return;
195503831d35Sstevel base = bank->um & ~(bank->uk);
195603831d35Sstevel
195703831d35Sstevel if ((cur_seg = sbdp_get_seg(base)) == NULL) {
195803831d35Sstevel /*
195903831d35Sstevel * This bank is part of a new segment, so create
196003831d35Sstevel * a struct for it and added to the list of segments
196103831d35Sstevel */
196203831d35Sstevel cur_seg = kmem_zalloc(sizeof (sbdp_seg_t), KM_SLEEP);
196303831d35Sstevel cur_seg->id = id++;
196403831d35Sstevel cur_seg->base = base;
196503831d35Sstevel cur_seg->size = ((bank->uk +1) << PHYS2UM_SHIFT);
196603831d35Sstevel cur_seg->intlv = ((bank->lk ^ 0xF) + 1);
196703831d35Sstevel /*
196803831d35Sstevel * add to the seg list
196903831d35Sstevel */
197003831d35Sstevel cur_seg->next = sys_seg;
197103831d35Sstevel sys_seg = cur_seg;
197203831d35Sstevel }
197303831d35Sstevel
197403831d35Sstevel cur_seg->nbanks++;
197503831d35Sstevel /*
197603831d35Sstevel * add bank into segs bank list. Note we add at the head
197703831d35Sstevel */
197803831d35Sstevel bank->seg_next = cur_seg->banks;
197903831d35Sstevel cur_seg->banks = bank;
198003831d35Sstevel }
198103831d35Sstevel
198203831d35Sstevel /*
198303831d35Sstevel * Remove this segment from the seg list
198403831d35Sstevel */
198503831d35Sstevel void
sbdp_rm_seg(sbdp_seg_t * seg)198603831d35Sstevel sbdp_rm_seg(sbdp_seg_t *seg)
198703831d35Sstevel {
198803831d35Sstevel sbdp_seg_t **curpp, *curp;
198903831d35Sstevel
199003831d35Sstevel curpp = &sys_seg;
199103831d35Sstevel
199203831d35Sstevel while ((curp = *curpp) != NULL) {
199303831d35Sstevel if (curp == seg) {
199403831d35Sstevel *curpp = curp->next;
199503831d35Sstevel break;
199603831d35Sstevel }
199703831d35Sstevel curpp = &curp->next;
199803831d35Sstevel }
199903831d35Sstevel
200003831d35Sstevel if (curp != NULL) {
200103831d35Sstevel kmem_free(curp, sizeof (sbdp_seg_t));
200203831d35Sstevel curp = NULL;
200303831d35Sstevel }
200403831d35Sstevel }
200503831d35Sstevel
200603831d35Sstevel /*
200703831d35Sstevel * remove this bank from its seg list
200803831d35Sstevel */
200903831d35Sstevel void
sbdp_remove_bank_from_seg(sbdp_bank_t * bank)201003831d35Sstevel sbdp_remove_bank_from_seg(sbdp_bank_t *bank)
201103831d35Sstevel {
201203831d35Sstevel uint64_t base;
201303831d35Sstevel sbdp_seg_t *cur_seg;
201403831d35Sstevel sbdp_bank_t **curpp, *curp;
201503831d35Sstevel
201603831d35Sstevel /*
201703831d35Sstevel * if we got an invalid bank just skip it
201803831d35Sstevel */
201903831d35Sstevel if (bank == NULL || !bank->valid)
202003831d35Sstevel return;
202103831d35Sstevel base = bank->um & ~(bank->uk);
202203831d35Sstevel
202303831d35Sstevel /*
202403831d35Sstevel * If the bank doesn't belong to any seg just return
202503831d35Sstevel */
202603831d35Sstevel if ((cur_seg = sbdp_get_seg(base)) == NULL) {
202703831d35Sstevel SBDP_DBG_MEM("bank %d with no segment\n", bank->id);
202803831d35Sstevel return;
202903831d35Sstevel }
203003831d35Sstevel
203103831d35Sstevel /*
203203831d35Sstevel * Find bank in the seg
203303831d35Sstevel */
203403831d35Sstevel curpp = &cur_seg->banks;
203503831d35Sstevel
203603831d35Sstevel while ((curp = *curpp) != NULL) {
203703831d35Sstevel if (curp->id == bank->id) {
203803831d35Sstevel /*
203903831d35Sstevel * found node, remove it
204003831d35Sstevel */
204103831d35Sstevel *curpp = curp->seg_next;
204203831d35Sstevel break;
204303831d35Sstevel }
204403831d35Sstevel curpp = &curp->seg_next;
204503831d35Sstevel }
204603831d35Sstevel
204703831d35Sstevel if (curp != NULL) {
204803831d35Sstevel cur_seg->nbanks--;
204903831d35Sstevel }
205003831d35Sstevel
205103831d35Sstevel if (cur_seg->nbanks == 0) {
205203831d35Sstevel /*
205303831d35Sstevel * No banks left on this segment, remove the segment
205403831d35Sstevel */
205503831d35Sstevel SBDP_DBG_MEM("No banks left in this segment, removing it\n");
205603831d35Sstevel sbdp_rm_seg(cur_seg);
205703831d35Sstevel }
205803831d35Sstevel }
205903831d35Sstevel
206003831d35Sstevel sbdp_seg_t *
sbdp_get_seg(uint64_t base)206103831d35Sstevel sbdp_get_seg(uint64_t base)
206203831d35Sstevel {
206303831d35Sstevel sbdp_seg_t *cur_seg;
206403831d35Sstevel
206503831d35Sstevel for (cur_seg = sys_seg; cur_seg; cur_seg = cur_seg->next) {
206603831d35Sstevel if (cur_seg-> base == base)
206703831d35Sstevel break;
206803831d35Sstevel }
206903831d35Sstevel
207003831d35Sstevel return (cur_seg);
207103831d35Sstevel }
207203831d35Sstevel
207303831d35Sstevel #ifdef DEBUG
207403831d35Sstevel int
sbdp_passthru_readmem(sbdp_handle_t * hp,void * arg)207503831d35Sstevel sbdp_passthru_readmem(sbdp_handle_t *hp, void *arg)
207603831d35Sstevel {
207703831d35Sstevel _NOTE(ARGUNUSED(hp))
207803831d35Sstevel _NOTE(ARGUNUSED(arg))
207903831d35Sstevel
208003831d35Sstevel struct memlist *ml;
208103831d35Sstevel uint64_t src_pa;
208203831d35Sstevel uint64_t dst_pa;
208303831d35Sstevel uint64_t dst;
208403831d35Sstevel
208503831d35Sstevel
208603831d35Sstevel dst_pa = va_to_pa(&dst);
208703831d35Sstevel
208803831d35Sstevel memlist_read_lock();
2089*56f33205SJonathan Adams for (ml = phys_install; ml; ml = ml->ml_next) {
209003831d35Sstevel uint64_t nbytes;
209103831d35Sstevel
2092*56f33205SJonathan Adams src_pa = ml->ml_address;
2093*56f33205SJonathan Adams nbytes = ml->ml_size;
209403831d35Sstevel
209503831d35Sstevel while (nbytes != 0ull) {
209603831d35Sstevel
209703831d35Sstevel /* copy 32 bytes at src_pa to dst_pa */
209803831d35Sstevel bcopy32_il(src_pa, dst_pa);
209903831d35Sstevel
210003831d35Sstevel /* increment by 32 bytes */
210103831d35Sstevel src_pa += (4 * sizeof (uint64_t));
210203831d35Sstevel
210303831d35Sstevel /* decrement by 32 bytes */
210403831d35Sstevel nbytes -= (4 * sizeof (uint64_t));
210503831d35Sstevel }
210603831d35Sstevel }
210703831d35Sstevel memlist_read_unlock();
210803831d35Sstevel
210903831d35Sstevel return (0);
211003831d35Sstevel }
211103831d35Sstevel
211203831d35Sstevel static int
isdigit(int ch)211303831d35Sstevel isdigit(int ch)
211403831d35Sstevel {
211503831d35Sstevel return (ch >= '0' && ch <= '9');
211603831d35Sstevel }
211703831d35Sstevel
211803831d35Sstevel #define isspace(c) ((c) == ' ' || (c) == '\t' || (c) == '\n')
211903831d35Sstevel
212003831d35Sstevel int
sbdp_strtoi(char * p,char ** pos)212103831d35Sstevel sbdp_strtoi(char *p, char **pos)
212203831d35Sstevel {
212303831d35Sstevel int n;
212403831d35Sstevel int c, neg = 0;
212503831d35Sstevel
212603831d35Sstevel if (!isdigit(c = *p)) {
212703831d35Sstevel while (isspace(c))
212803831d35Sstevel c = *++p;
212903831d35Sstevel switch (c) {
213003831d35Sstevel case '-':
213103831d35Sstevel neg++;
213203831d35Sstevel /* FALLTHROUGH */
213303831d35Sstevel case '+':
213403831d35Sstevel c = *++p;
213503831d35Sstevel }
213603831d35Sstevel if (!isdigit(c)) {
213703831d35Sstevel if (pos != NULL)
213803831d35Sstevel *pos = p;
213903831d35Sstevel return (0);
214003831d35Sstevel }
214103831d35Sstevel }
214203831d35Sstevel for (n = '0' - c; isdigit(c = *++p); ) {
214303831d35Sstevel n *= 10; /* two steps to avoid unnecessary overflow */
214403831d35Sstevel n += '0' - c; /* accum neg to avoid surprises at MAX */
214503831d35Sstevel }
214603831d35Sstevel if (pos != NULL)
214703831d35Sstevel *pos = p;
214803831d35Sstevel return (neg ? n : -n);
214903831d35Sstevel }
215003831d35Sstevel
215103831d35Sstevel int
sbdp_passthru_prep_script(sbdp_handle_t * hp,void * arg)215203831d35Sstevel sbdp_passthru_prep_script(sbdp_handle_t *hp, void *arg)
215303831d35Sstevel {
215403831d35Sstevel int board, i;
215503831d35Sstevel sbdp_bd_t *t_bdp, *s_bdp;
215603831d35Sstevel char *opts;
215703831d35Sstevel int t_board;
215803831d35Sstevel sbdp_rename_script_t *rsbuffer;
215903831d35Sstevel sbdp_cr_handle_t *cph;
216003831d35Sstevel int scriptlen, size;
216103831d35Sstevel
216203831d35Sstevel opts = (char *)arg;
216303831d35Sstevel board = hp->h_board;
216403831d35Sstevel
216503831d35Sstevel opts += strlen("prep-script=");
216603831d35Sstevel t_board = sbdp_strtoi(opts, NULL);
216703831d35Sstevel
216803831d35Sstevel cph = kmem_zalloc(sizeof (sbdp_cr_handle_t), KM_SLEEP);
216903831d35Sstevel
217003831d35Sstevel size = sizeof (sbdp_rename_script_t) * SBDP_RENAME_MAXOP;
217103831d35Sstevel rsbuffer = kmem_zalloc(size, KM_SLEEP);
217203831d35Sstevel
217303831d35Sstevel s_bdp = sbdp_get_bd_info(hp->h_wnode, board);
217403831d35Sstevel t_bdp = sbdp_get_bd_info(hp->h_wnode, t_board);
217503831d35Sstevel
217603831d35Sstevel cph->s_bdp = s_bdp;
217703831d35Sstevel cph->t_bdp = t_bdp;
217803831d35Sstevel cph->script = rsbuffer;
217903831d35Sstevel
218003831d35Sstevel affinity_set(CPU_CURRENT);
218103831d35Sstevel scriptlen = sbdp_prep_rename_script(cph);
218203831d35Sstevel
218303831d35Sstevel if (scriptlen <= 0) {
218403831d35Sstevel cmn_err(CE_WARN,
218503831d35Sstevel "sbdp failed to prep for copy-rename");
218603831d35Sstevel }
218703831d35Sstevel prom_printf("SCRIPT from board %d to board %d ->\n", board, t_board);
218803831d35Sstevel for (i = 0; i < (scriptlen / (sizeof (sbdp_rename_script_t))); i++) {
218903831d35Sstevel prom_printf("0x%lx = 0x%lx, asi 0x%x\n",
219003831d35Sstevel rsbuffer[i].masr_addr, rsbuffer[i].masr, rsbuffer[i].asi);
219103831d35Sstevel }
219203831d35Sstevel prom_printf("\n");
219303831d35Sstevel
219403831d35Sstevel affinity_clear();
219503831d35Sstevel kmem_free(rsbuffer, size);
219603831d35Sstevel kmem_free(cph, sizeof (sbdp_cr_handle_t));
219703831d35Sstevel
219803831d35Sstevel return (0);
219903831d35Sstevel }
220003831d35Sstevel #endif
2201