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