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 
277