ac_add.c revision 1341:6d7c4f090a72
1/*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
7 *
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
12 *
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 *
19 * CDDL HEADER END
20 */
21
22/*
23 * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
24 * Use is subject to license terms.
25 */
26
27#pragma ident	"%Z%%M%	%I%	%E% SMI"
28
29#include <sys/types.h>
30#include <sys/systm.h>
31#include <sys/ddi.h>
32#include <sys/sunddi.h>
33#include <sys/ddi_impldefs.h>
34#include <sys/obpdefs.h>
35#include <sys/errno.h>
36#include <sys/kmem.h>
37#include <sys/vmem.h>
38#include <sys/debug.h>
39#include <sys/sysmacros.h>
40#include <sys/machsystm.h>
41#include <sys/machparam.h>
42#include <sys/modctl.h>
43#include <sys/fhc.h>
44#include <sys/ac.h>
45#include <sys/vm.h>
46#include <sys/cpu_module.h>
47#include <vm/seg_kmem.h>
48#include <vm/hat_sfmmu.h>
49#include <sys/mem_config.h>
50#include <sys/mem_cage.h>
51
52/*
53 * Default to always clean memory on add to reduce chance
54 * of uncorrectable errors.
55 */
56int ac_add_clean = 1;
57
58#define	ADD_PAGESIZE	MMU_PAGESIZE
59
60ac_err_t
61ac_kpm_err_cvt(int err)
62{
63	switch (err) {
64	case KPHYSM_ESPAN:
65		return (AC_ERR_KPM_SPAN);
66	case KPHYSM_EFAULT:
67		return (AC_ERR_KPM_FAULT);
68	case KPHYSM_ERESOURCE:
69		return (AC_ERR_KPM_RESOURCE);
70	case KPHYSM_ENOTSUP:
71		return (AC_ERR_KPM_NOTSUP);
72	case KPHYSM_ENOHANDLES:
73		return (AC_ERR_KPM_NOHANDLES);
74	case KPHYSM_ENONRELOC:
75		return (AC_ERR_KPM_NONRELOC);
76	case KPHYSM_EHANDLE:
77		return (AC_ERR_KPM_HANDLE);
78	case KPHYSM_EBUSY:
79		return (AC_ERR_KPM_BUSY);
80	case KPHYSM_ENOTVIABLE:
81		return (AC_ERR_KPM_NOTVIABLE);
82	case KPHYSM_ESEQUENCE:
83		return (AC_ERR_KPM_SEQUENCE);
84	case KPHYSM_ENOWORK:
85		return (AC_ERR_KPM_NOWORK);
86	case KPHYSM_ECANCELLED:
87		return (AC_ERR_KPM_CANCELLED);
88	case KPHYSM_ENOTFINISHED:
89		return (AC_ERR_KPM_NOTFINISHED);
90	case KPHYSM_ENOTRUNNING:
91		return (AC_ERR_KPM_NOTRUNNING);
92	case KPHYSM_EREFUSED:
93		return (AC_ERR_KPM_REFUSED);
94	case KPHYSM_EDUP:
95		return (AC_ERR_KPM_DUP);
96	default:
97		return (AC_ERR_DEFAULT);
98	}
99}
100
101static int
102ac_add_bank(struct bd_list *add, ac_cfga_pkt_t *pkt)
103{
104	uint64_t		decode;
105	uint64_t		base_pa;
106	uint64_t		limit_pa;
107	uint64_t		current_pa;
108	int			errs;
109	uint64_t		bank_size;
110	struct ac_mem_info	*mem_info;
111	struct ac_soft_state	*asp = pkt->softsp;
112	uint_t			ilv;
113
114	/*
115	 * Cannot add interleaved banks at the moment.
116	 */
117	ilv = (pkt->bank == Bank0) ?
118	    INTLV0(*asp->ac_memctl) : INTLV1(*asp->ac_memctl);
119	if (ilv != 1) {
120		AC_ERR_SET(pkt, AC_ERR_MEM_DEINTLV);
121		return (EINVAL);
122	}
123	/*
124	 * Determine the physical location of the selected bank
125	 */
126	decode = (pkt->bank == Bank0) ?
127	    *asp->ac_memdecode0 : *asp->ac_memdecode1;
128	base_pa = GRP_REALBASE(decode);
129	bank_size = GRP_UK2SPAN(decode);
130	limit_pa = base_pa + bank_size;
131
132	mem_info = &asp->bank[pkt->bank];
133	if (ac_add_clean || mem_info->condition != SYSC_CFGA_COND_OK) {
134		caddr_t			base_va;
135		caddr_t			fill_buf;
136		int			linesize;
137
138		/*
139		 * We need a page_va and a fill buffer for this operation
140		 */
141		base_va = vmem_alloc(heap_arena, PAGESIZE, VM_SLEEP);
142		fill_buf = kmem_zalloc(ADD_PAGESIZE, KM_SLEEP);
143		linesize = cpunodes[CPU->cpu_id].ecache_linesize;
144
145		/*
146		 * zero fill the memory -- indirectly initializes the ECC
147		 */
148		kpreempt_disable();
149		for (current_pa = base_pa; current_pa < limit_pa;
150		    current_pa += ADD_PAGESIZE) {
151
152			/* map current pa */
153			ac_mapin(current_pa, base_va);
154
155			/* fill the target page */
156			ac_blkcopy(fill_buf, base_va,
157				ADD_PAGESIZE/linesize, linesize);
158
159			/* tear down translation */
160			ac_unmap(base_va);
161		}
162		kpreempt_enable();
163
164		/*
165		 * clean up temporary resources
166		 */
167		kmem_free(fill_buf, ADD_PAGESIZE);
168		vmem_free(heap_arena, base_va, PAGESIZE);
169	}
170
171	/*
172	 * give the memory to Solaris
173	 */
174	errs = kphysm_add_memory_dynamic(base_pa >> PAGESHIFT,
175	    bank_size >> PAGESHIFT);
176
177	if (errs != KPHYSM_OK) {
178		AC_ERR_SET(pkt, ac_kpm_err_cvt(errs));
179		return (EINVAL);
180	}
181
182	/*
183	 * Add the board to the cage growth list.
184	 */
185	kcage_range_lock();
186	errs = kcage_range_add(base_pa >> PAGESHIFT, bank_size >> PAGESHIFT, 1);
187	/* TODO: deal with error return. */
188	if (errs != 0)
189		cmn_err(CE_NOTE, "ac_add_bank(): board %d, bank %d, "
190		    "kcage_range_add() returned %d",
191		    add->sc.board, pkt->bank, errs);
192	kcage_range_unlock();
193
194	return (0);
195}
196
197int
198ac_add_memory(ac_cfga_pkt_t *pkt)
199{
200	struct bd_list *board;
201	struct ac_mem_info *mem_info;
202	int force = pkt->cmd_cfga.force;
203	int retval;
204
205	board = fhc_bdlist_lock(pkt->softsp->board);
206	if (board == NULL || board->ac_softsp == NULL) {
207		fhc_bdlist_unlock();
208		AC_ERR_SET(pkt, AC_ERR_BD);
209		return (EINVAL);
210	}
211	ASSERT(pkt->softsp == board->ac_softsp);
212
213	/* verify the board is of the correct type */
214	switch (board->sc.type) {
215	case CPU_BOARD:
216	case MEM_BOARD:
217		break;
218	default:
219		fhc_bdlist_unlock();
220		AC_ERR_SET(pkt, AC_ERR_BD_TYPE);
221		return (EINVAL);
222	}
223
224	/* verify the memory condition is acceptable */
225	mem_info = &pkt->softsp->bank[pkt->bank];
226	if (!MEM_BOARD_VISIBLE(board) || mem_info->busy ||
227	    fhc_bd_busy(pkt->softsp->board) ||
228	    mem_info->rstate != SYSC_CFGA_RSTATE_CONNECTED ||
229	    mem_info->ostate != SYSC_CFGA_OSTATE_UNCONFIGURED ||
230	    (!force && mem_info->condition != SYSC_CFGA_COND_OK)) {
231		fhc_bdlist_unlock();
232		AC_ERR_SET(pkt, AC_ERR_BD_STATE);
233		return (EINVAL);
234	}
235
236	/*
237	 * at this point, we have an available bank to add.
238	 * mark it busy and initiate the add function.
239	 */
240	mem_info->busy = TRUE;
241	fhc_bdlist_unlock();
242
243	retval = ac_add_bank(board, pkt);
244
245	/*
246	 * We made it!  Update the status and get out of here.
247	 */
248	(void) fhc_bdlist_lock(-1);
249	mem_info->busy = FALSE;
250	if (retval == 0) {
251		mem_info->ostate = SYSC_CFGA_OSTATE_CONFIGURED;
252		mem_info->status_change = ddi_get_time();
253	}
254
255	fhc_bdlist_unlock();
256
257	if (retval != 0) {
258		return (retval);
259	}
260	return (DDI_SUCCESS);
261}
262