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