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 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 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 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 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 * 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 * 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 124203831d35Sstevel sbdphw_disable_memctrl(sbdp_handle_t *hp, dev_info_t *dip) 124303831d35Sstevel { 124403831d35Sstevel return (0); 124503831d35Sstevel } 124603831d35Sstevel 124703831d35Sstevel /* ARGSUSED */ 124803831d35Sstevel int 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 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 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 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 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 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 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 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 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 161803831d35Sstevel sbdp_mem_add_span(sbdp_handle_t *hp, uint64_t address, uint64_t size) 161903831d35Sstevel { 162003831d35Sstevel return (0); 162103831d35Sstevel } 162203831d35Sstevel 162303831d35Sstevel int 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 * 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 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 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 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 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