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 * Copyright 2007 Sun Microsystems, Inc. All rights reserved. 23 * Use is subject to license terms. 24 */ 25 26 #pragma ident "%Z%%M% %I% %E% SMI" 27 28 #include <sys/sysmacros.h> 29 #include <sys/machsystm.h> 30 #include <sys/cpuvar.h> 31 #include <sys/ddi_implfuncs.h> 32 #include <px_csr.h> 33 #include <px_regs.h> 34 #include <px_obj.h> 35 #include <sys/pci_tools.h> 36 #include <px_tools_var.h> 37 #include <px_asm_4u.h> 38 #include <px_lib4u.h> 39 #include <px_tools_ext.h> 40 41 /* 42 * Delay needed to have a safe environment envelop any error which could 43 * surface. The larger the number of bridges and switches, the larger the 44 * number needed here. 45 * 46 * The way it works is as follows: 47 * 48 * An access is done which causes an error. Fire errors are handled with 49 * ontrap protection and usually come in first. Fabric errors can come in 50 * later. 51 * 52 * px_phys_peek_4u() disables interrupts. Interrupts are reenabled at the end 53 * of that function if no errors have been caught by the trap handler, or by 54 * peek_fault() which executes when a fire error occurs. 55 * 56 * Fabric error messages get put on an event queue but are not processed until 57 * interrupts are reenabled. 58 * 59 * The delay gives time for the fabric errors to be processed by FMA before 60 * changing the fm error flag back to DDI_FM_ERR_UNEXPECTED. If this isn't 61 * done, then the fabric error which should be safe can panic the system. 62 * 63 * Note: this is a workaround until a better solution is found. While this 64 * number is high, given enough bridges and switches in the device path, this 65 * workaround can break. Also, other PIL 15 interrupts besides the ones we are 66 * enveloping could delay processing of the interrupt we are trying to protect. 67 */ 68 69 /* 70 * Set delay to 10 ms 71 */ 72 int pxtool_delay_usec = 10000; 73 74 /* Number of inos per root complex. */ 75 int pxtool_num_inos = INTERRUPT_MAPPING_ENTRIES; 76 77 /* Mechanism for getting offsets of smaller datatypes aligned in 64 bit long */ 78 typedef union { 79 uint64_t u64; 80 uint32_t u32; 81 uint16_t u16; 82 uint8_t u8; 83 } peek_poke_value_t; 84 85 /* 86 * Safe C wrapper around assy language routine px_phys_peek_4u 87 * 88 * Type is TRUE for big endian, FALSE for little endian. 89 * Size is 1, 2, 4 or 8 bytes. 90 * paddr is the physical address in IO space to access read. 91 * value_p is where the value is returned. 92 */ 93 static int 94 pxtool_safe_phys_peek(px_t *px_p, boolean_t type, size_t size, uint64_t paddr, 95 uint64_t *value_p) 96 { 97 px_pec_t *pec_p = px_p->px_pec_p; 98 pxu_t *pxu_p = (pxu_t *)px_p->px_plat_p; 99 on_trap_data_t otd; 100 peek_poke_value_t peek_value; 101 int err = DDI_SUCCESS; 102 103 mutex_enter(&pec_p->pec_pokefault_mutex); 104 pec_p->pec_safeacc_type = DDI_FM_ERR_PEEK; 105 106 pxu_p->pcitool_addr = (caddr_t)(paddr & px_paddr_mask); 107 108 /* 109 * Set up trap handling to make the access safe. 110 * 111 * on_trap works like setjmp. 112 * Set it up to not panic on data access error, 113 * but to call peek_fault instead. 114 * Call px_phys_peek_4u after trap handling is setup. 115 * When on_trap returns FALSE, it has been setup. 116 * When it returns TRUE, an it has caught an error. 117 */ 118 if (!on_trap(&otd, OT_DATA_ACCESS)) { 119 otd.ot_trampoline = (uintptr_t)&peek_fault; 120 err = px_phys_peek_4u(size, paddr, &peek_value.u64, type); 121 } else 122 err = DDI_FAILURE; 123 124 no_trap(); 125 126 /* 127 * Workaround: delay taking down safe access env. 128 * For more info, see comments where pxtool_delay_usec is declared. 129 */ 130 if ((err == DDI_FAILURE) && (pxtool_delay_usec > 0)) 131 delay(drv_usectohz(pxtool_delay_usec)); 132 133 pec_p->pec_safeacc_type = DDI_FM_ERR_UNEXPECTED; 134 pxu_p->pcitool_addr = NULL; 135 mutex_exit(&pec_p->pec_pokefault_mutex); 136 137 if (err != DDI_FAILURE) { 138 switch (size) { 139 case 8: 140 *value_p = peek_value.u64; 141 break; 142 case 4: 143 *value_p = (uint64_t)peek_value.u32; 144 break; 145 case 2: 146 *value_p = (uint64_t)peek_value.u16; 147 break; 148 case 1: 149 *value_p = (uint64_t)peek_value.u8; 150 break; 151 default: 152 err = DDI_FAILURE; 153 } 154 } 155 156 return (err); 157 } 158 159 /* 160 * Safe C wrapper around assy language routine px_phys_poke_4u 161 * 162 * Type is TRUE for big endian, FALSE for little endian. 163 * Size is 1,2,4 or 8 bytes. 164 * paddr is the physical address in IO space to access read. 165 * value contains the value to be written. 166 */ 167 static int 168 pxtool_safe_phys_poke(px_t *px_p, boolean_t type, size_t size, uint64_t paddr, 169 uint64_t value) 170 { 171 on_trap_data_t otd; 172 pxu_t *pxu_p = (pxu_t *)px_p->px_plat_p; 173 px_pec_t *pec_p = px_p->px_pec_p; 174 peek_poke_value_t poke_value; 175 int err = DDI_SUCCESS; 176 177 switch (size) { 178 case 8: 179 poke_value.u64 = value; 180 break; 181 case 4: 182 poke_value.u32 = (uint32_t)value; 183 break; 184 case 2: 185 poke_value.u16 = (uint16_t)value; 186 break; 187 case 1: 188 poke_value.u8 = (uint8_t)value; 189 break; 190 default: 191 return (DDI_FAILURE); 192 } 193 194 mutex_enter(&pec_p->pec_pokefault_mutex); 195 pec_p->pec_ontrap_data = &otd; 196 pec_p->pec_safeacc_type = DDI_FM_ERR_POKE; 197 pxu_p->pcitool_addr = (caddr_t)(paddr & px_paddr_mask); 198 199 /* 200 * on_trap works like setjmp. 201 * Set it up to not panic on data access error, 202 * but to call poke_fault instead. 203 * Call px_phys_poke_4u after trap handling is setup. 204 * When on_trap returns FALSE, it has been setup. 205 * When it returns TRUE, an it has caught an error. 206 */ 207 if (!on_trap(&otd, OT_DATA_ACCESS)) { 208 209 otd.ot_trampoline = (uintptr_t)&poke_fault; 210 err = px_phys_poke_4u(size, paddr, &poke_value.u64, type); 211 } else 212 err = DDI_FAILURE; 213 214 px_lib_clr_errs(px_p, 0, paddr); 215 216 if (otd.ot_trap & OT_DATA_ACCESS) 217 err = DDI_FAILURE; 218 219 /* Take down protected environment. */ 220 no_trap(); 221 pec_p->pec_ontrap_data = NULL; 222 223 /* 224 * Workaround: delay taking down safe access env. 225 * For more info, see comments where pxtool_delay_usec is declared. 226 */ 227 if (pxtool_delay_usec > 0) 228 delay(drv_usectohz(pxtool_delay_usec)); 229 230 pec_p->pec_safeacc_type = DDI_FM_ERR_UNEXPECTED; 231 pxu_p->pcitool_addr = NULL; 232 mutex_exit(&pec_p->pec_pokefault_mutex); 233 234 return (err); 235 } 236 237 238 /* 239 * Wrapper around pxtool_safe_phys_peek/poke. 240 * 241 * Validates arguments and calls pxtool_safe_phys_peek/poke appropriately. 242 * 243 * Dip is of the nexus, 244 * phys_addr is the address to write in physical space. 245 * pcitool_status returns more detailed status in addition to a more generic 246 * errno-style function return value. 247 * other args are self-explanatory. 248 * 249 * This function assumes that offset, bdf, and acc_attr are current in 250 * prg_p. It also assumes that prg_p->phys_addr is the final phys addr, 251 * including offset. 252 * This function modifies prg_p status and data. 253 */ 254 /*ARGSUSED*/ 255 static int 256 pxtool_access(px_t *px_p, pcitool_reg_t *prg_p, uint64_t *data_p, 257 boolean_t is_write) 258 { 259 dev_info_t *dip = px_p->px_dip; 260 uint64_t phys_addr = prg_p->phys_addr; 261 boolean_t endian = PCITOOL_ACC_IS_BIG_ENDIAN(prg_p->acc_attr); 262 size_t size = PCITOOL_ACC_ATTR_SIZE(prg_p->acc_attr); 263 int rval = SUCCESS; 264 265 /* Alignment checking. Assumes base address is 8-byte aligned. */ 266 if (!IS_P2ALIGNED(phys_addr, size)) { 267 DBG(DBG_TOOLS, dip, "not aligned.\n"); 268 prg_p->status = PCITOOL_NOT_ALIGNED; 269 270 rval = EINVAL; 271 272 } else if (is_write) { /* Made it through checks. Do the access. */ 273 274 DBG(DBG_PHYS_ACC, dip, 275 "%d byte %s pxtool_safe_phys_poke at addr 0x%" PRIx64 "\n", 276 size, (endian ? "BE" : "LE"), phys_addr); 277 278 if (pxtool_safe_phys_poke(px_p, endian, size, phys_addr, 279 *data_p) != DDI_SUCCESS) { 280 DBG(DBG_PHYS_ACC, dip, 281 "%d byte %s pxtool_safe_phys_poke at addr " 282 "0x%" PRIx64 " failed\n", 283 size, (endian ? "BE" : "LE"), phys_addr); 284 prg_p->status = PCITOOL_INVALID_ADDRESS; 285 286 rval = EFAULT; 287 } 288 289 } else { /* Read */ 290 291 DBG(DBG_PHYS_ACC, dip, 292 "%d byte %s pxtool_safe_phys_peek at addr 0x%" PRIx64 "\n", 293 size, (endian ? "BE" : "LE"), phys_addr); 294 295 if (pxtool_safe_phys_peek(px_p, endian, size, phys_addr, 296 data_p) != DDI_SUCCESS) { 297 DBG(DBG_PHYS_ACC, dip, 298 "%d byte %s pxtool_safe_phys_peek at addr " 299 "0x%" PRIx64 " failed\n", 300 size, (endian ? "BE" : "LE"), phys_addr); 301 prg_p->status = PCITOOL_INVALID_ADDRESS; 302 303 rval = EFAULT; 304 } 305 } 306 return (rval); 307 } 308 309 310 int 311 pxtool_pcicfg_access(px_t *px_p, pcitool_reg_t *prg_p, 312 uint64_t *data_p, boolean_t is_write) 313 { 314 return (pxtool_access(px_p, prg_p, data_p, is_write)); 315 } 316 317 int 318 pxtool_pciiomem_access(px_t *px_p, pcitool_reg_t *prg_p, 319 uint64_t *data_p, boolean_t is_write) 320 { 321 return (pxtool_access(px_p, prg_p, data_p, is_write)); 322 } 323 324 int 325 pxtool_dev_reg_ops_platchk(dev_info_t *dip, pcitool_reg_t *prg_p) 326 { 327 /* 328 * Guard against checking a root nexus which is empty. 329 * On some systems this will result in a Fatal Reset. 330 */ 331 if (ddi_get_child(dip) == NULL) { 332 DBG(DBG_TOOLS, dip, 333 "pxtool_dev_reg_ops set/get reg: nexus has no devs!\n"); 334 prg_p->status = PCITOOL_IO_ERROR; 335 return (ENXIO); 336 } 337 338 return (SUCCESS); 339 } 340 341 /* 342 * Perform register accesses on the nexus device itself. 343 */ 344 int 345 pxtool_bus_reg_ops(dev_info_t *dip, void *arg, int cmd, int mode) 346 { 347 pcitool_reg_t prg; 348 uint64_t base_addr; 349 uint32_t reglen; 350 px_t *px_p = DIP_TO_STATE(dip); 351 px_nexus_regspec_t *px_rp = NULL; 352 uint32_t numbanks = 0; 353 boolean_t write_flag = B_FALSE; 354 uint32_t rval = 0; 355 356 if (cmd == PCITOOL_NEXUS_SET_REG) 357 write_flag = B_TRUE; 358 359 DBG(DBG_TOOLS, dip, "pxtool_bus_reg_ops set/get reg\n"); 360 361 /* Read data from userland. */ 362 if (ddi_copyin(arg, &prg, sizeof (pcitool_reg_t), mode) != 363 DDI_SUCCESS) { 364 DBG(DBG_TOOLS, dip, "Error reading arguments\n"); 365 return (EFAULT); 366 } 367 368 /* Read reg property which contains starting addr and size of banks. */ 369 if (ddi_prop_lookup_int_array(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS, 370 "reg", (int **)&px_rp, ®len) == DDI_SUCCESS) { 371 if (((reglen * sizeof (int)) % 372 sizeof (px_nexus_regspec_t)) != 0) { 373 DBG(DBG_TOOLS, dip, "reg prop not well-formed"); 374 prg.status = PCITOOL_REGPROP_NOTWELLFORMED; 375 rval = EIO; 376 goto done; 377 } 378 } 379 380 numbanks = (reglen * sizeof (int)) / sizeof (px_nexus_regspec_t); 381 382 /* Bounds check the bank number. */ 383 if (prg.barnum >= numbanks) { 384 prg.status = PCITOOL_OUT_OF_RANGE; 385 rval = EINVAL; 386 goto done; 387 } 388 389 base_addr = px_rp[prg.barnum].phys_addr; 390 prg.phys_addr = base_addr + prg.offset; 391 392 DBG(DBG_TOOLS, dip, "pxtool_bus_reg_ops: nexus: base:0x%" PRIx64 ", " 393 "offset:0x%" PRIx64 ", addr:0x%" PRIx64 ", max_offset:" 394 "0x%" PRIx64 "\n", 395 base_addr, prg.offset, prg.phys_addr, px_rp[prg.barnum].size); 396 397 if (prg.offset >= px_rp[prg.barnum].size) { 398 prg.status = PCITOOL_OUT_OF_RANGE; 399 rval = EINVAL; 400 goto done; 401 } 402 403 /* Access device. prg.status is modified. */ 404 rval = pxtool_access(px_p, &prg, &prg.data, write_flag); 405 406 done: 407 if (px_rp != NULL) 408 ddi_prop_free(px_rp); 409 410 prg.drvr_version = PCITOOL_VERSION; 411 if (ddi_copyout(&prg, arg, sizeof (pcitool_reg_t), 412 mode) != DDI_SUCCESS) { 413 DBG(DBG_TOOLS, dip, "Copyout failed.\n"); 414 return (EFAULT); 415 } 416 417 return (rval); 418 } 419