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