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 2006 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 int pxtool_delay_ticks = 1; 69 70 /* Number of inos per root complex. */ 71 int pxtool_num_inos = INTERRUPT_MAPPING_ENTRIES; 72 73 /* Mechanism for getting offsets of smaller datatypes aligned in 64 bit long */ 74 typedef union { 75 uint64_t u64; 76 uint32_t u32; 77 uint16_t u16; 78 uint8_t u8; 79 } peek_poke_value_t; 80 81 /* 82 * Safe C wrapper around assy language routine px_phys_peek_4u 83 * 84 * Type is TRUE for big endian, FALSE for little endian. 85 * Size is 1, 2, 4 or 8 bytes. 86 * paddr is the physical address in IO space to access read. 87 * value_p is where the value is returned. 88 */ 89 static int 90 pxtool_safe_phys_peek(px_t *px_p, boolean_t type, size_t size, uint64_t paddr, 91 uint64_t *value_p) 92 { 93 px_pec_t *pec_p = px_p->px_pec_p; 94 pxu_t *pxu_p = (pxu_t *)px_p->px_plat_p; 95 on_trap_data_t otd; 96 peek_poke_value_t peek_value; 97 int err = DDI_SUCCESS; 98 99 mutex_enter(&pec_p->pec_pokefault_mutex); 100 pec_p->pec_safeacc_type = DDI_FM_ERR_PEEK; 101 102 pxu_p->pcitool_addr = (caddr_t)(paddr & px_paddr_mask); 103 104 /* 105 * Set up trap handling to make the access safe. 106 * 107 * on_trap works like setjmp. 108 * Set it up to not panic on data access error, 109 * but to call peek_fault instead. 110 * Call px_phys_peek_4u after trap handling is setup. 111 * When on_trap returns FALSE, it has been setup. 112 * When it returns TRUE, an it has caught an error. 113 */ 114 if (!on_trap(&otd, OT_DATA_ACCESS)) { 115 otd.ot_trampoline = (uintptr_t)&peek_fault; 116 err = px_phys_peek_4u(size, paddr, &peek_value.u64, type); 117 } else 118 err = DDI_FAILURE; 119 120 no_trap(); 121 122 /* 123 * Workaround: delay taking down safe access env. 124 * For more info, see comments where pxtool_delay_ticks is declared. 125 */ 126 if (pxtool_delay_ticks > 0) 127 delay(pxtool_delay_ticks); 128 129 pec_p->pec_safeacc_type = DDI_FM_ERR_UNEXPECTED; 130 pxu_p->pcitool_addr = NULL; 131 mutex_exit(&pec_p->pec_pokefault_mutex); 132 133 if (err != DDI_FAILURE) { 134 switch (size) { 135 case 8: 136 *value_p = peek_value.u64; 137 break; 138 case 4: 139 *value_p = (uint64_t)peek_value.u32; 140 break; 141 case 2: 142 *value_p = (uint64_t)peek_value.u16; 143 break; 144 case 1: 145 *value_p = (uint64_t)peek_value.u8; 146 break; 147 default: 148 err = DDI_FAILURE; 149 } 150 } 151 152 return (err); 153 } 154 155 /* 156 * Safe C wrapper around assy language routine px_phys_poke_4u 157 * 158 * Type is TRUE for big endian, FALSE for little endian. 159 * Size is 1,2,4 or 8 bytes. 160 * paddr is the physical address in IO space to access read. 161 * value contains the value to be written. 162 */ 163 static int 164 pxtool_safe_phys_poke(px_t *px_p, boolean_t type, size_t size, uint64_t paddr, 165 uint64_t value) 166 { 167 on_trap_data_t otd; 168 pxu_t *pxu_p = (pxu_t *)px_p->px_plat_p; 169 px_pec_t *pec_p = px_p->px_pec_p; 170 peek_poke_value_t poke_value; 171 int err = DDI_SUCCESS; 172 173 switch (size) { 174 case 8: 175 poke_value.u64 = value; 176 break; 177 case 4: 178 poke_value.u32 = (uint32_t)value; 179 break; 180 case 2: 181 poke_value.u16 = (uint16_t)value; 182 break; 183 case 1: 184 poke_value.u8 = (uint8_t)value; 185 break; 186 default: 187 return (DDI_FAILURE); 188 } 189 190 mutex_enter(&pec_p->pec_pokefault_mutex); 191 pec_p->pec_ontrap_data = &otd; 192 pec_p->pec_safeacc_type = DDI_FM_ERR_POKE; 193 pxu_p->pcitool_addr = (caddr_t)(paddr & px_paddr_mask); 194 195 /* 196 * on_trap works like setjmp. 197 * Set it up to not panic on data access error, 198 * but to call poke_fault instead. 199 * Call px_phys_poke_4u after trap handling is setup. 200 * When on_trap returns FALSE, it has been setup. 201 * When it returns TRUE, an it has caught an error. 202 */ 203 if (!on_trap(&otd, OT_DATA_ACCESS)) { 204 205 otd.ot_trampoline = (uintptr_t)&poke_fault; 206 err = px_phys_poke_4u(size, paddr, &poke_value.u64, type); 207 } else 208 err = DDI_FAILURE; 209 210 px_lib_clr_errs(px_p, 0, paddr); 211 212 if (otd.ot_trap & OT_DATA_ACCESS) 213 err = DDI_FAILURE; 214 215 /* Take down protected environment. */ 216 no_trap(); 217 pec_p->pec_ontrap_data = NULL; 218 219 /* 220 * Workaround: delay taking down safe access env. 221 * For more info, see comments where pxtool_delay_ticks is declared. 222 */ 223 if (pxtool_delay_ticks > 0) 224 delay(pxtool_delay_ticks); 225 226 pec_p->pec_safeacc_type = DDI_FM_ERR_UNEXPECTED; 227 pxu_p->pcitool_addr = NULL; 228 mutex_exit(&pec_p->pec_pokefault_mutex); 229 230 return (err); 231 } 232 233 234 /* 235 * Wrapper around pxtool_safe_phys_peek/poke. 236 * 237 * Validates arguments and calls pxtool_safe_phys_peek/poke appropriately. 238 * 239 * Dip is of the nexus, 240 * phys_addr is the address to write in physical space. 241 * pcitool_status returns more detailed status in addition to a more generic 242 * errno-style function return value. 243 * other args are self-explanatory. 244 * 245 * This function assumes that offset, bdf, and acc_attr are current in 246 * prg_p. It also assumes that prg_p->phys_addr is the final phys addr, 247 * including offset. 248 * This function modifies prg_p status and data. 249 */ 250 /*ARGSUSED*/ 251 static int 252 pxtool_access(px_t *px_p, pcitool_reg_t *prg_p, uint64_t *data_p, 253 boolean_t is_write) 254 { 255 dev_info_t *dip = px_p->px_dip; 256 uint64_t phys_addr = prg_p->phys_addr; 257 boolean_t endian = PCITOOL_ACC_IS_BIG_ENDIAN(prg_p->acc_attr); 258 size_t size = PCITOOL_ACC_ATTR_SIZE(prg_p->acc_attr); 259 int rval = SUCCESS; 260 261 /* Alignment checking. Assumes base address is 8-byte aligned. */ 262 if (!IS_P2ALIGNED(phys_addr, size)) { 263 DBG(DBG_TOOLS, dip, "not aligned.\n"); 264 prg_p->status = PCITOOL_NOT_ALIGNED; 265 266 rval = EINVAL; 267 268 } else if (is_write) { /* Made it through checks. Do the access. */ 269 270 DBG(DBG_PHYS_ACC, dip, 271 "%d byte %s pxtool_safe_phys_poke at addr 0x%" PRIx64 "\n", 272 size, (endian ? "BE" : "LE"), phys_addr); 273 274 if (pxtool_safe_phys_poke(px_p, endian, size, phys_addr, 275 *data_p) != DDI_SUCCESS) { 276 DBG(DBG_PHYS_ACC, dip, 277 "%d byte %s pxtool_safe_phys_poke at addr " 278 "0x%" PRIx64 " failed\n", 279 size, (endian ? "BE" : "LE"), phys_addr); 280 prg_p->status = PCITOOL_INVALID_ADDRESS; 281 282 rval = EFAULT; 283 } 284 285 } else { /* Read */ 286 287 DBG(DBG_PHYS_ACC, dip, 288 "%d byte %s pxtool_safe_phys_peek at addr 0x%" PRIx64 "\n", 289 size, (endian ? "BE" : "LE"), phys_addr); 290 291 if (pxtool_safe_phys_peek(px_p, endian, size, phys_addr, 292 data_p) != DDI_SUCCESS) { 293 DBG(DBG_PHYS_ACC, dip, 294 "%d byte %s pxtool_safe_phys_peek at addr " 295 "0x%" PRIx64 " failed\n", 296 size, (endian ? "BE" : "LE"), phys_addr); 297 prg_p->status = PCITOOL_INVALID_ADDRESS; 298 299 rval = EFAULT; 300 } 301 } 302 return (rval); 303 } 304 305 306 int 307 pxtool_pcicfg_access(px_t *px_p, pcitool_reg_t *prg_p, 308 uint64_t *data_p, boolean_t is_write) 309 { 310 return (pxtool_access(px_p, prg_p, data_p, is_write)); 311 } 312 313 int 314 pxtool_pciiomem_access(px_t *px_p, pcitool_reg_t *prg_p, 315 uint64_t *data_p, boolean_t is_write) 316 { 317 return (pxtool_access(px_p, prg_p, data_p, is_write)); 318 } 319 320 int 321 pxtool_dev_reg_ops_platchk(dev_info_t *dip, pcitool_reg_t *prg_p) 322 { 323 int devi_nodeid = ddi_get_nodeid(dip); 324 325 /* 326 * Guard against checking a root nexus which is empty. 327 * On some systems this will result in a Fatal Reset. 328 */ 329 if ((int)prom_childnode((pnode_t)devi_nodeid) == OBP_NONODE) { 330 DBG(DBG_TOOLS, dip, 331 "pxtool_dev_reg_ops set/get reg: nexus has no devs!\n"); 332 prg_p->status = PCITOOL_IO_ERROR; 333 return (ENXIO); 334 } 335 336 return (SUCCESS); 337 } 338 339 /* 340 * Perform register accesses on the nexus device itself. 341 */ 342 int 343 pxtool_bus_reg_ops(dev_info_t *dip, void *arg, int cmd, int mode) 344 { 345 pcitool_reg_t prg; 346 uint64_t base_addr; 347 uint32_t reglen; 348 px_t *px_p = DIP_TO_STATE(dip); 349 px_nexus_regspec_t *px_rp = NULL; 350 uint32_t numbanks = 0; 351 boolean_t write_flag = B_FALSE; 352 uint32_t rval = 0; 353 354 if (cmd == PCITOOL_NEXUS_SET_REG) 355 write_flag = B_TRUE; 356 357 DBG(DBG_TOOLS, dip, "pxtool_bus_reg_ops set/get reg\n"); 358 359 /* Read data from userland. */ 360 if (ddi_copyin(arg, &prg, sizeof (pcitool_reg_t), mode) != 361 DDI_SUCCESS) { 362 DBG(DBG_TOOLS, dip, "Error reading arguments\n"); 363 return (EFAULT); 364 } 365 366 /* Read reg property which contains starting addr and size of banks. */ 367 if (ddi_prop_lookup_int_array(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS, 368 "reg", (int **)&px_rp, ®len) == DDI_SUCCESS) { 369 if (((reglen * sizeof (int)) % 370 sizeof (px_nexus_regspec_t)) != 0) { 371 DBG(DBG_TOOLS, dip, "reg prop not well-formed"); 372 prg.status = PCITOOL_REGPROP_NOTWELLFORMED; 373 rval = EIO; 374 goto done; 375 } 376 } 377 378 numbanks = (reglen * sizeof (int)) / sizeof (px_nexus_regspec_t); 379 380 /* Bounds check the bank number. */ 381 if (prg.barnum >= numbanks) { 382 prg.status = PCITOOL_OUT_OF_RANGE; 383 rval = EINVAL; 384 goto done; 385 } 386 387 base_addr = px_rp[prg.barnum].phys_addr; 388 prg.phys_addr = base_addr + prg.offset; 389 390 DBG(DBG_TOOLS, dip, "pxtool_bus_reg_ops: nexus: base:0x%" PRIx64 ", " 391 "offset:0x%" PRIx64 ", addr:0x%" PRIx64 ", max_offset:" 392 "0x%" PRIx64 "\n", 393 base_addr, prg.offset, prg.phys_addr, px_rp[prg.barnum].size); 394 395 if (prg.offset >= px_rp[prg.barnum].size) { 396 prg.status = PCITOOL_OUT_OF_RANGE; 397 rval = EINVAL; 398 goto done; 399 } 400 401 /* Access device. prg.status is modified. */ 402 rval = pxtool_access(px_p, &prg, &prg.data, write_flag); 403 404 done: 405 if (px_rp != NULL) 406 ddi_prop_free(px_rp); 407 408 if (ddi_copyout(&prg, arg, sizeof (pcitool_reg_t), 409 mode) != DDI_SUCCESS) { 410 DBG(DBG_TOOLS, dip, "Copyout failed.\n"); 411 return (EFAULT); 412 } 413 414 return (rval); 415 } 416