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 2007 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 */ 56 int ac_add_clean = 1; 57 58 #define ADD_PAGESIZE MMU_PAGESIZE 59 60 ac_err_t 61 ac_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 101 static int 102 ac_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 errs = kcage_range_add(btop(base_pa), btop(bank_size), KCAGE_DOWN); 186 /* TODO: deal with error return. */ 187 if (errs != 0) 188 cmn_err(CE_NOTE, "ac_add_bank(): board %d, bank %d, " 189 "kcage_range_add() returned %d", 190 add->sc.board, pkt->bank, errs); 191 192 return (0); 193 } 194 195 int 196 ac_add_memory(ac_cfga_pkt_t *pkt) 197 { 198 struct bd_list *board; 199 struct ac_mem_info *mem_info; 200 int force = pkt->cmd_cfga.force; 201 int retval; 202 203 board = fhc_bdlist_lock(pkt->softsp->board); 204 if (board == NULL || board->ac_softsp == NULL) { 205 fhc_bdlist_unlock(); 206 AC_ERR_SET(pkt, AC_ERR_BD); 207 return (EINVAL); 208 } 209 ASSERT(pkt->softsp == board->ac_softsp); 210 211 /* verify the board is of the correct type */ 212 switch (board->sc.type) { 213 case CPU_BOARD: 214 case MEM_BOARD: 215 break; 216 default: 217 fhc_bdlist_unlock(); 218 AC_ERR_SET(pkt, AC_ERR_BD_TYPE); 219 return (EINVAL); 220 } 221 222 /* verify the memory condition is acceptable */ 223 mem_info = &pkt->softsp->bank[pkt->bank]; 224 if (!MEM_BOARD_VISIBLE(board) || mem_info->busy || 225 fhc_bd_busy(pkt->softsp->board) || 226 mem_info->rstate != SYSC_CFGA_RSTATE_CONNECTED || 227 mem_info->ostate != SYSC_CFGA_OSTATE_UNCONFIGURED || 228 (!force && mem_info->condition != SYSC_CFGA_COND_OK)) { 229 fhc_bdlist_unlock(); 230 AC_ERR_SET(pkt, AC_ERR_BD_STATE); 231 return (EINVAL); 232 } 233 234 /* 235 * at this point, we have an available bank to add. 236 * mark it busy and initiate the add function. 237 */ 238 mem_info->busy = TRUE; 239 fhc_bdlist_unlock(); 240 241 retval = ac_add_bank(board, pkt); 242 243 /* 244 * We made it! Update the status and get out of here. 245 */ 246 (void) fhc_bdlist_lock(-1); 247 mem_info->busy = FALSE; 248 if (retval == 0) { 249 mem_info->ostate = SYSC_CFGA_OSTATE_CONFIGURED; 250 mem_info->status_change = ddi_get_time(); 251 } 252 253 fhc_bdlist_unlock(); 254 255 if (retval != 0) { 256 return (retval); 257 } 258 return (DDI_SUCCESS); 259 } 260