xref: /illumos-gate/usr/src/uts/sun4u/sunfire/io/ac_add.c (revision 85f58038)
129949e86Sstevel /*
229949e86Sstevel  * CDDL HEADER START
329949e86Sstevel  *
429949e86Sstevel  * The contents of this file are subject to the terms of the
529949e86Sstevel  * Common Development and Distribution License (the "License").
629949e86Sstevel  * You may not use this file except in compliance with the License.
729949e86Sstevel  *
829949e86Sstevel  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
929949e86Sstevel  * or http://www.opensolaris.org/os/licensing.
1029949e86Sstevel  * See the License for the specific language governing permissions
1129949e86Sstevel  * and limitations under the License.
1229949e86Sstevel  *
1329949e86Sstevel  * When distributing Covered Code, include this CDDL HEADER in each
1429949e86Sstevel  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
1529949e86Sstevel  * If applicable, add the following below this CDDL HEADER, with the
1629949e86Sstevel  * fields enclosed by brackets "[]" replaced with your own identifying
1729949e86Sstevel  * information: Portions Copyright [yyyy] [name of copyright owner]
1829949e86Sstevel  *
1929949e86Sstevel  * CDDL HEADER END
2029949e86Sstevel  */
2229949e86Sstevel /*
23*85f58038Sdp  * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
2429949e86Sstevel  * Use is subject to license terms.
2529949e86Sstevel  */
2729949e86Sstevel #pragma ident	"%Z%%M%	%I%	%E% SMI"
2929949e86Sstevel #include <sys/types.h>
3029949e86Sstevel #include <sys/systm.h>
3129949e86Sstevel #include <sys/ddi.h>
3229949e86Sstevel #include <sys/sunddi.h>
3329949e86Sstevel #include <sys/ddi_impldefs.h>
3429949e86Sstevel #include <sys/obpdefs.h>
3529949e86Sstevel #include <sys/errno.h>
3629949e86Sstevel #include <sys/kmem.h>
3729949e86Sstevel #include <sys/vmem.h>
3829949e86Sstevel #include <sys/debug.h>
3929949e86Sstevel #include <sys/sysmacros.h>
4029949e86Sstevel #include <sys/machsystm.h>
4129949e86Sstevel #include <sys/machparam.h>
4229949e86Sstevel #include <sys/modctl.h>
4329949e86Sstevel #include <sys/fhc.h>
4429949e86Sstevel #include <sys/ac.h>
4529949e86Sstevel #include <sys/vm.h>
4629949e86Sstevel #include <sys/cpu_module.h>
4729949e86Sstevel #include <vm/seg_kmem.h>
4829949e86Sstevel #include <vm/hat_sfmmu.h>
4929949e86Sstevel #include <sys/mem_config.h>
5029949e86Sstevel #include <sys/mem_cage.h>
5229949e86Sstevel /*
5329949e86Sstevel  * Default to always clean memory on add to reduce chance
5429949e86Sstevel  * of uncorrectable errors.
5529949e86Sstevel  */
5629949e86Sstevel int ac_add_clean = 1;
5829949e86Sstevel #define	ADD_PAGESIZE	MMU_PAGESIZE
6029949e86Sstevel ac_err_t
ac_kpm_err_cvt(int err)6129949e86Sstevel ac_kpm_err_cvt(int err)
6229949e86Sstevel {
6329949e86Sstevel 	switch (err) {
6429949e86Sstevel 	case KPHYSM_ESPAN:
6529949e86Sstevel 		return (AC_ERR_KPM_SPAN);
6629949e86Sstevel 	case KPHYSM_EFAULT:
6729949e86Sstevel 		return (AC_ERR_KPM_FAULT);
6829949e86Sstevel 	case KPHYSM_ERESOURCE:
6929949e86Sstevel 		return (AC_ERR_KPM_RESOURCE);
7029949e86Sstevel 	case KPHYSM_ENOTSUP:
7129949e86Sstevel 		return (AC_ERR_KPM_NOTSUP);
7229949e86Sstevel 	case KPHYSM_ENOHANDLES:
7329949e86Sstevel 		return (AC_ERR_KPM_NOHANDLES);
7429949e86Sstevel 	case KPHYSM_ENONRELOC:
7529949e86Sstevel 		return (AC_ERR_KPM_NONRELOC);
7629949e86Sstevel 	case KPHYSM_EHANDLE:
7729949e86Sstevel 		return (AC_ERR_KPM_HANDLE);
7829949e86Sstevel 	case KPHYSM_EBUSY:
7929949e86Sstevel 		return (AC_ERR_KPM_BUSY);
8029949e86Sstevel 	case KPHYSM_ENOTVIABLE:
8129949e86Sstevel 		return (AC_ERR_KPM_NOTVIABLE);
8229949e86Sstevel 	case KPHYSM_ESEQUENCE:
8329949e86Sstevel 		return (AC_ERR_KPM_SEQUENCE);
8429949e86Sstevel 	case KPHYSM_ENOWORK:
8529949e86Sstevel 		return (AC_ERR_KPM_NOWORK);
8629949e86Sstevel 	case KPHYSM_ECANCELLED:
8729949e86Sstevel 		return (AC_ERR_KPM_CANCELLED);
8829949e86Sstevel 	case KPHYSM_ENOTFINISHED:
8929949e86Sstevel 		return (AC_ERR_KPM_NOTFINISHED);
9029949e86Sstevel 	case KPHYSM_ENOTRUNNING:
9129949e86Sstevel 		return (AC_ERR_KPM_NOTRUNNING);
9229949e86Sstevel 	case KPHYSM_EREFUSED:
9329949e86Sstevel 		return (AC_ERR_KPM_REFUSED);
9429949e86Sstevel 	case KPHYSM_EDUP:
9529949e86Sstevel 		return (AC_ERR_KPM_DUP);
9629949e86Sstevel 	default:
9729949e86Sstevel 		return (AC_ERR_DEFAULT);
9829949e86Sstevel 	}
9929949e86Sstevel }
10129949e86Sstevel static int
ac_add_bank(struct bd_list * add,ac_cfga_pkt_t * pkt)10229949e86Sstevel ac_add_bank(struct bd_list *add, ac_cfga_pkt_t *pkt)
10329949e86Sstevel {
10429949e86Sstevel 	uint64_t		decode;
10529949e86Sstevel 	uint64_t		base_pa;
10629949e86Sstevel 	uint64_t		limit_pa;
10729949e86Sstevel 	uint64_t		current_pa;
10829949e86Sstevel 	int			errs;
10929949e86Sstevel 	uint64_t		bank_size;
11029949e86Sstevel 	struct ac_mem_info	*mem_info;
11129949e86Sstevel 	struct ac_soft_state	*asp = pkt->softsp;
11229949e86Sstevel 	uint_t			ilv;
11429949e86Sstevel 	/*
11529949e86Sstevel 	 * Cannot add interleaved banks at the moment.
11629949e86Sstevel 	 */
11729949e86Sstevel 	ilv = (pkt->bank == Bank0) ?
11829949e86Sstevel 	    INTLV0(*asp->ac_memctl) : INTLV1(*asp->ac_memctl);
11929949e86Sstevel 	if (ilv != 1) {
12029949e86Sstevel 		AC_ERR_SET(pkt, AC_ERR_MEM_DEINTLV);
12129949e86Sstevel 		return (EINVAL);
12229949e86Sstevel 	}
12329949e86Sstevel 	/*
12429949e86Sstevel 	 * Determine the physical location of the selected bank
12529949e86Sstevel 	 */
12629949e86Sstevel 	decode = (pkt->bank == Bank0) ?
12729949e86Sstevel 	    *asp->ac_memdecode0 : *asp->ac_memdecode1;
12829949e86Sstevel 	base_pa = GRP_REALBASE(decode);
12929949e86Sstevel 	bank_size = GRP_UK2SPAN(decode);
13029949e86Sstevel 	limit_pa = base_pa + bank_size;
13229949e86Sstevel 	mem_info = &asp->bank[pkt->bank];
13329949e86Sstevel 	if (ac_add_clean || mem_info->condition != SYSC_CFGA_COND_OK) {
13429949e86Sstevel 		caddr_t			base_va;
13529949e86Sstevel 		caddr_t			fill_buf;
13629949e86Sstevel 		int			linesize;
13829949e86Sstevel 		/*
13929949e86Sstevel 		 * We need a page_va and a fill buffer for this operation
14029949e86Sstevel 		 */
14129949e86Sstevel 		base_va = vmem_alloc(heap_arena, PAGESIZE, VM_SLEEP);
14229949e86Sstevel 		fill_buf = kmem_zalloc(ADD_PAGESIZE, KM_SLEEP);
14329949e86Sstevel 		linesize = cpunodes[CPU->cpu_id].ecache_linesize;
14529949e86Sstevel 		/*
14629949e86Sstevel 		 * zero fill the memory -- indirectly initializes the ECC
14729949e86Sstevel 		 */
14829949e86Sstevel 		kpreempt_disable();
14929949e86Sstevel 		for (current_pa = base_pa; current_pa < limit_pa;
15029949e86Sstevel 		    current_pa += ADD_PAGESIZE) {
15229949e86Sstevel 			/* map current pa */
15329949e86Sstevel 			ac_mapin(current_pa, base_va);
15529949e86Sstevel 			/* fill the target page */
15629949e86Sstevel 			ac_blkcopy(fill_buf, base_va,
15729949e86Sstevel 				ADD_PAGESIZE/linesize, linesize);
15929949e86Sstevel 			/* tear down translation */
16029949e86Sstevel 			ac_unmap(base_va);
16129949e86Sstevel 		}
16229949e86Sstevel 		kpreempt_enable();
16429949e86Sstevel 		/*
16529949e86Sstevel 		 * clean up temporary resources
16629949e86Sstevel 		 */
16729949e86Sstevel 		kmem_free(fill_buf, ADD_PAGESIZE);
16829949e86Sstevel 		vmem_free(heap_arena, base_va, PAGESIZE);
16929949e86Sstevel 	}
17129949e86Sstevel 	/*
17229949e86Sstevel 	 * give the memory to Solaris
17329949e86Sstevel 	 */
17429949e86Sstevel 	errs = kphysm_add_memory_dynamic(base_pa >> PAGESHIFT,
17529949e86Sstevel 	    bank_size >> PAGESHIFT);
17729949e86Sstevel 	if (errs != KPHYSM_OK) {
17829949e86Sstevel 		AC_ERR_SET(pkt, ac_kpm_err_cvt(errs));
17929949e86Sstevel 		return (EINVAL);
18029949e86Sstevel 	}
18229949e86Sstevel 	/*
18329949e86Sstevel 	 * Add the board to the cage growth list.
18429949e86Sstevel 	 */
185*85f58038Sdp 	errs = kcage_range_add(btop(base_pa), btop(bank_size), KCAGE_DOWN);
18629949e86Sstevel 	/* TODO: deal with error return. */
18729949e86Sstevel 	if (errs != 0)
18829949e86Sstevel 		cmn_err(CE_NOTE, "ac_add_bank(): board %d, bank %d, "
18929949e86Sstevel 		    "kcage_range_add() returned %d",
19029949e86Sstevel 		    add->sc.board, pkt->bank, errs);
19229949e86Sstevel 	return (0);
19329949e86Sstevel }
19529949e86Sstevel int
ac_add_memory(ac_cfga_pkt_t * pkt)19629949e86Sstevel ac_add_memory(ac_cfga_pkt_t *pkt)
19729949e86Sstevel {
19829949e86Sstevel 	struct bd_list *board;
19929949e86Sstevel 	struct ac_mem_info *mem_info;
20029949e86Sstevel 	int force = pkt->cmd_cfga.force;
20129949e86Sstevel 	int retval;
20329949e86Sstevel 	board = fhc_bdlist_lock(pkt->softsp->board);
20429949e86Sstevel 	if (board == NULL || board->ac_softsp == NULL) {
20529949e86Sstevel 		fhc_bdlist_unlock();
20629949e86Sstevel 		AC_ERR_SET(pkt, AC_ERR_BD);
20729949e86Sstevel 		return (EINVAL);
20829949e86Sstevel 	}
20929949e86Sstevel 	ASSERT(pkt->softsp == board->ac_softsp);
21129949e86Sstevel 	/* verify the board is of the correct type */
21229949e86Sstevel 	switch (board->sc.type) {
21329949e86Sstevel 	case CPU_BOARD:
21429949e86Sstevel 	case MEM_BOARD:
21529949e86Sstevel 		break;
21629949e86Sstevel 	default:
21729949e86Sstevel 		fhc_bdlist_unlock();
21829949e86Sstevel 		AC_ERR_SET(pkt, AC_ERR_BD_TYPE);
21929949e86Sstevel 		return (EINVAL);
22029949e86Sstevel 	}
22229949e86Sstevel 	/* verify the memory condition is acceptable */
22329949e86Sstevel 	mem_info = &pkt->softsp->bank[pkt->bank];
22429949e86Sstevel 	if (!MEM_BOARD_VISIBLE(board) || mem_info->busy ||
22529949e86Sstevel 	    fhc_bd_busy(pkt->softsp->board) ||
22629949e86Sstevel 	    mem_info->rstate != SYSC_CFGA_RSTATE_CONNECTED ||
22729949e86Sstevel 	    mem_info->ostate != SYSC_CFGA_OSTATE_UNCONFIGURED ||
22829949e86Sstevel 	    (!force && mem_info->condition != SYSC_CFGA_COND_OK)) {
22929949e86Sstevel 		fhc_bdlist_unlock();
23029949e86Sstevel 		AC_ERR_SET(pkt, AC_ERR_BD_STATE);
23129949e86Sstevel 		return (EINVAL);
23229949e86Sstevel 	}
23429949e86Sstevel 	/*
23529949e86Sstevel 	 * at this point, we have an available bank to add.
23629949e86Sstevel 	 * mark it busy and initiate the add function.
23729949e86Sstevel 	 */
23829949e86Sstevel 	mem_info->busy = TRUE;
23929949e86Sstevel 	fhc_bdlist_unlock();
24129949e86Sstevel 	retval = ac_add_bank(board, pkt);
24329949e86Sstevel 	/*
24429949e86Sstevel 	 * We made it!  Update the status and get out of here.
24529949e86Sstevel 	 */
24629949e86Sstevel 	(void) fhc_bdlist_lock(-1);
24729949e86Sstevel 	mem_info->busy = FALSE;
24829949e86Sstevel 	if (retval == 0) {
24929949e86Sstevel 		mem_info->ostate = SYSC_CFGA_OSTATE_CONFIGURED;
25029949e86Sstevel 		mem_info->status_change = ddi_get_time();
25129949e86Sstevel 	}
25329949e86Sstevel 	fhc_bdlist_unlock();
25529949e86Sstevel 	if (retval != 0) {
25629949e86Sstevel 		return (retval);
25729949e86Sstevel 	}
25829949e86Sstevel 	return (DDI_SUCCESS);
25929949e86Sstevel }