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 <sys/hypervisor_api.h> 34 #include <px_obj.h> 35 #include <sys/pci_tools.h> 36 #include <px_tools_var.h> 37 #include "px_asm_4v.h" 38 #include "px_lib4v.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 * Note: this is a workaround until a better solution is found. While this 47 * number is high, given enough bridges and switches in the device path, this 48 * workaround can break. Also, other PIL 15 interrupts besides the ones we are 49 * enveloping could delay processing of the interrupt we are trying to protect. 50 */ 51 int pxtool_cfg_delay_usec = 2500; 52 int pxtool_iomem_delay_usec = 25000; 53 54 /* Currently there is no way of getting this info from hypervisor. */ 55 #define INTERRUPT_MAPPING_ENTRIES 64 56 57 /* Number of inos per root complex. */ 58 int pxtool_num_inos = INTERRUPT_MAPPING_ENTRIES; 59 60 static int 61 pxtool_phys_access(px_t *px_p, uintptr_t dev_addr, 62 uint64_t *data_p, boolean_t is_big_endian, boolean_t is_write) 63 { 64 void *func; 65 uint64_t rfunc; 66 uint64_t pfunc; 67 uint64_t rdata_addr; 68 uint64_t pdata_addr; 69 int rval; 70 dev_info_t *dip = px_p->px_dip; 71 72 DBG(DBG_TOOLS, dip, 73 "pxtool_phys_access: dev_addr:0x%" PRIx64 "\n", dev_addr); 74 DBG(DBG_TOOLS, dip, " data_addr:0x%" PRIx64 ", is_write:%s\n", 75 data_p, (is_write ? "yes" : "no")); 76 77 /*LINTED*/ 78 func = (is_write) ? (void *)px_phys_poke_4v : (void *)px_phys_peek_4v; 79 80 if ((rfunc = va_to_pa(func)) == (uint64_t)-1) { 81 DBG(DBG_TOOLS, dip, "Error getting real addr for function\n"); 82 return (EIO); 83 } 84 85 if ((pfunc = hv_ra2pa(rfunc)) == -1) { 86 DBG(DBG_TOOLS, dip, "Error getting phys addr for function\n"); 87 return (EIO); 88 } 89 90 if ((rdata_addr = va_to_pa((void *)data_p)) == (uint64_t)-1) { 91 DBG(DBG_TOOLS, dip, "Error getting real addr for data_p\n"); 92 return (EIO); 93 } 94 95 if ((pdata_addr = hv_ra2pa(rdata_addr)) == -1) { 96 DBG(DBG_TOOLS, dip, "Error getting phys addr for data ptr\n"); 97 return (EIO); 98 } 99 100 rval = hv_hpriv((void *)pfunc, dev_addr, pdata_addr, is_big_endian); 101 switch (rval) { 102 case H_ENOACCESS: /* Returned by non-debug hypervisor. */ 103 rval = ENOTSUP; 104 break; 105 case H_EOK: 106 rval = SUCCESS; 107 break; 108 default: 109 rval = EIO; 110 break; 111 } 112 113 return (rval); 114 } 115 116 /* Swap endianness. */ 117 static uint64_t 118 pxtool_swap_endian(uint64_t data, int size) 119 { 120 typedef union { 121 uint64_t data64; 122 uint8_t data8[8]; 123 } data_split_t; 124 125 data_split_t orig_data; 126 data_split_t returned_data; 127 int i; 128 129 orig_data.data64 = data; 130 returned_data.data64 = 0; 131 132 for (i = 0; i < size; i++) { 133 returned_data.data8[7 - i] = orig_data.data8[8 - size + i]; 134 } 135 136 return (returned_data.data64); 137 } 138 139 /* 140 * This function is for PCI config space access. 141 * It assumes that offset, bdf, acc_attr are valid in prg_p. 142 * It assumes that prg_p->phys_addr is the final phys addr (including offset). 143 * This function modifies prg_p status and data. 144 */ 145 146 /*ARGSUSED*/ 147 int 148 pxtool_pcicfg_access(px_t *px_p, pcitool_reg_t *prg_p, 149 uint64_t max_addr, uint64_t *data_p, boolean_t is_write) 150 { 151 pci_cfg_data_t data; 152 on_trap_data_t otd; 153 dev_info_t *dip = px_p->px_dip; 154 px_pec_t *pec_p = px_p->px_pec_p; 155 pci_device_t bdf = PX_GET_BDF(prg_p); 156 size_t size = PCITOOL_ACC_ATTR_SIZE(prg_p->acc_attr); 157 int rval = 0; 158 159 /* Alignment checking. */ 160 if (!IS_P2ALIGNED(prg_p->offset, size)) { 161 DBG(DBG_TOOLS, dip, "not aligned.\n"); 162 prg_p->status = PCITOOL_NOT_ALIGNED; 163 return (EINVAL); 164 } 165 166 mutex_enter(&pec_p->pec_pokefault_mutex); 167 pec_p->pec_ontrap_data = &otd; 168 169 if (is_write) { 170 171 if (PCITOOL_ACC_IS_BIG_ENDIAN(prg_p->acc_attr)) 172 data.qw = pxtool_swap_endian(*data_p, size); 173 else 174 data.qw = *data_p; 175 176 switch (size) { 177 case sizeof (uint8_t): 178 data.b = (uint8_t)data.qw; 179 break; 180 case sizeof (uint16_t): 181 data.w = (uint16_t)data.qw; 182 break; 183 case sizeof (uint32_t): 184 data.dw = (uint32_t)data.qw; 185 break; 186 case sizeof (uint64_t): 187 break; 188 } 189 190 DBG(DBG_TOOLS, dip, "put: bdf:0x%x, off:0x%" PRIx64 ", size:" 191 "0x%" PRIx64 ", data:0x%" PRIx64 "\n", 192 bdf, prg_p->offset, size, data.qw); 193 194 pec_p->pec_safeacc_type = DDI_FM_ERR_POKE; 195 196 if (!on_trap(&otd, OT_DATA_ACCESS)) { 197 otd.ot_trampoline = (uintptr_t)&poke_fault; 198 rval = hvio_config_put(px_p->px_dev_hdl, bdf, 199 prg_p->offset, size, data); 200 } else 201 rval = H_EIO; 202 203 if (otd.ot_trap & OT_DATA_ACCESS) 204 rval = H_EIO; 205 206 } else { 207 208 data.qw = 0; 209 210 pec_p->pec_safeacc_type = DDI_FM_ERR_PEEK; 211 212 if (!on_trap(&otd, OT_DATA_ACCESS)) { 213 otd.ot_trampoline = (uintptr_t)&peek_fault; 214 rval = hvio_config_get(px_p->px_dev_hdl, bdf, 215 prg_p->offset, size, &data); 216 } else 217 rval = H_EIO; 218 219 DBG(DBG_TOOLS, dip, "get: bdf:0x%x, off:0x%" PRIx64 ", size:" 220 "0x%" PRIx64 ", data:0x%" PRIx64 "\n", 221 bdf, prg_p->offset, size, data.qw); 222 223 switch (size) { 224 case sizeof (uint8_t): 225 *data_p = data.b; 226 break; 227 case sizeof (uint16_t): 228 *data_p = data.w; 229 break; 230 case sizeof (uint32_t): 231 *data_p = data.dw; 232 break; 233 case sizeof (uint64_t): 234 *data_p = data.qw; 235 break; 236 default: 237 DBG(DBG_TOOLS, dip, 238 "bad size:0x%" PRIx64 "\n", size); 239 break; 240 } 241 if (PCITOOL_ACC_IS_BIG_ENDIAN(prg_p->acc_attr)) 242 *data_p = pxtool_swap_endian(*data_p, size); 243 } 244 245 /* 246 * Workaround: delay taking down safe access env. 247 * For more info, see comments where pxtool_cfg_delay_usec is declared. 248 */ 249 if (pxtool_cfg_delay_usec > 0) 250 drv_usecwait(pxtool_cfg_delay_usec); 251 252 no_trap(); 253 pec_p->pec_ontrap_data = NULL; 254 pec_p->pec_safeacc_type = DDI_FM_ERR_UNEXPECTED; 255 mutex_exit(&pec_p->pec_pokefault_mutex); 256 257 if (rval != SUCCESS) { 258 prg_p->status = PCITOOL_INVALID_ADDRESS; 259 rval = EINVAL; 260 } else 261 prg_p->status = PCITOOL_SUCCESS; 262 263 return (rval); 264 } 265 266 267 /* 268 * This function is for PCI IO space and memory space access. 269 * It assumes that offset, bdf, acc_attr are current in prg_p. 270 * It assumes that prg_p->phys_addr is the final phys addr (including offset). 271 * This function modifies prg_p status and data. 272 */ 273 int 274 pxtool_pciiomem_access(px_t *px_p, pcitool_reg_t *prg_p, uint64_t max_addr, 275 uint64_t *data_p, boolean_t is_write) 276 { 277 on_trap_data_t otd; 278 uint32_t io_stat = 0; 279 dev_info_t *dip = px_p->px_dip; 280 px_pec_t *pec_p = px_p->px_pec_p; 281 size_t size = PCITOOL_ACC_ATTR_SIZE(prg_p->acc_attr); 282 int rval = 0; 283 284 /* Alignment checking. */ 285 if (!IS_P2ALIGNED(prg_p->offset, size)) { 286 DBG(DBG_TOOLS, dip, "not aligned.\n"); 287 prg_p->status = PCITOOL_NOT_ALIGNED; 288 return (EINVAL); 289 } 290 291 /* Upper bounds checking. */ 292 if (prg_p->phys_addr > max_addr) { 293 DBG(DBG_TOOLS, dip, "Phys addr 0x%" PRIx64 " out of " 294 "range (max 0x%" PRIx64 ").\n", prg_p->phys_addr, max_addr); 295 prg_p->status = PCITOOL_INVALID_ADDRESS; 296 return (EINVAL); 297 } 298 299 mutex_enter(&pec_p->pec_pokefault_mutex); 300 pec_p->pec_ontrap_data = &otd; 301 302 if (is_write) { 303 pci_device_t bdf = PX_GET_BDF(prg_p); 304 305 if (PCITOOL_ACC_IS_BIG_ENDIAN(prg_p->acc_attr)) 306 *data_p = pxtool_swap_endian(*data_p, size); 307 308 pec_p->pec_safeacc_type = DDI_FM_ERR_POKE; 309 310 if (!on_trap(&otd, OT_DATA_ACCESS)) { 311 otd.ot_trampoline = (uintptr_t)&poke_fault; 312 rval = hvio_poke(px_p->px_dev_hdl, prg_p->phys_addr, 313 size, *data_p, bdf, &io_stat); 314 } else 315 rval = H_EIO; 316 317 if (otd.ot_trap & OT_DATA_ACCESS) 318 rval = H_EIO; 319 320 DBG(DBG_TOOLS, dip, "iomem:phys_addr:0x%" PRIx64 ", bdf:0x%x, " 321 "rval:%d, io_stat:%d\n", prg_p->phys_addr, bdf, 322 rval, io_stat); 323 } else { 324 325 *data_p = 0; 326 327 pec_p->pec_safeacc_type = DDI_FM_ERR_PEEK; 328 329 if (!on_trap(&otd, OT_DATA_ACCESS)) { 330 otd.ot_trampoline = (uintptr_t)&peek_fault; 331 rval = hvio_peek(px_p->px_dev_hdl, prg_p->phys_addr, 332 size, &io_stat, data_p); 333 } else 334 rval = H_EIO; 335 336 DBG(DBG_TOOLS, dip, "iomem:phys_addr:0x%" PRIx64 ", " 337 "size:0x%" PRIx64 ", hdl:0x%" PRIx64 ", " 338 "rval:%d, io_stat:%d\n", prg_p->phys_addr, 339 size, px_p->px_dev_hdl, rval, io_stat); 340 DBG(DBG_TOOLS, dip, "read data:0x%" PRIx64 "\n", *data_p); 341 342 if (PCITOOL_ACC_IS_BIG_ENDIAN(prg_p->acc_attr)) 343 *data_p = pxtool_swap_endian(*data_p, size); 344 } 345 346 /* 347 * Workaround: delay taking down safe access env. 348 * For more info, see comment where pxtool_iomem_delay_usec is declared. 349 */ 350 if (pxtool_iomem_delay_usec > 0) 351 delay(drv_usectohz(pxtool_iomem_delay_usec)); 352 353 no_trap(); 354 pec_p->pec_ontrap_data = NULL; 355 pec_p->pec_safeacc_type = DDI_FM_ERR_UNEXPECTED; 356 mutex_exit(&pec_p->pec_pokefault_mutex); 357 358 if (rval != SUCCESS) { 359 prg_p->status = PCITOOL_INVALID_ADDRESS; 360 rval = EINVAL; 361 } else if (io_stat != SUCCESS) { 362 prg_p->status = PCITOOL_IO_ERROR; 363 rval = EIO; 364 } else 365 prg_p->status = PCITOOL_SUCCESS; 366 367 return (rval); 368 } 369 370 371 /*ARGSUSED*/ 372 int 373 pxtool_dev_reg_ops_platchk(dev_info_t *dip, pcitool_reg_t *prg_p) 374 { 375 return (SUCCESS); 376 } 377 378 379 /* 380 * Perform register accesses on the nexus device itself. 381 */ 382 int 383 pxtool_bus_reg_ops(dev_info_t *dip, void *arg, int cmd, int mode) 384 { 385 386 pcitool_reg_t prg; 387 size_t size; 388 px_t *px_p = DIP_TO_STATE(dip); 389 boolean_t is_write = B_FALSE; 390 uint32_t rval = 0; 391 392 if (cmd == PCITOOL_NEXUS_SET_REG) 393 is_write = B_TRUE; 394 395 DBG(DBG_TOOLS, dip, "pxtool_bus_reg_ops set/get reg\n"); 396 397 /* Read data from userland. */ 398 if (ddi_copyin(arg, &prg, sizeof (pcitool_reg_t), 399 mode) != DDI_SUCCESS) { 400 DBG(DBG_TOOLS, dip, "Error reading arguments\n"); 401 return (EFAULT); 402 } 403 404 size = PCITOOL_ACC_ATTR_SIZE(prg.acc_attr); 405 406 DBG(DBG_TOOLS, dip, "raw bus:0x%x, dev:0x%x, func:0x%x\n", 407 prg.bus_no, prg.dev_no, prg.func_no); 408 DBG(DBG_TOOLS, dip, "barnum:0x%x, offset:0x%" PRIx64 ", acc:0x%x\n", 409 prg.barnum, prg.offset, prg.acc_attr); 410 DBG(DBG_TOOLS, dip, "data:0x%" PRIx64 ", phys_addr:0x%" PRIx64 "\n", 411 prg.data, prg.phys_addr); 412 413 /* 414 * If bank num == ff, base phys addr passed in from userland. 415 * 416 * Normal bank specification is invalid, as there is no OBP property to 417 * back it up. 418 */ 419 if (prg.barnum != PCITOOL_BASE) { 420 prg.status = PCITOOL_OUT_OF_RANGE; 421 rval = EINVAL; 422 goto done; 423 } 424 425 /* Allow only size of 8-bytes. */ 426 if (size != sizeof (uint64_t)) { 427 prg.status = PCITOOL_INVALID_SIZE; 428 rval = EINVAL; 429 goto done; 430 } 431 432 /* Alignment checking. */ 433 if (!IS_P2ALIGNED(prg.offset, size)) { 434 DBG(DBG_TOOLS, dip, "not aligned.\n"); 435 prg.status = PCITOOL_NOT_ALIGNED; 436 rval = EINVAL; 437 goto done; 438 } 439 440 prg.phys_addr += prg.offset; 441 442 /* XXX do some kind of checking here? */ 443 444 /* Access device. prg.status is modified. */ 445 rval = pxtool_phys_access(px_p, prg.phys_addr, &prg.data, 446 PCITOOL_ACC_IS_BIG_ENDIAN(prg.acc_attr), is_write); 447 done: 448 if (ddi_copyout(&prg, arg, sizeof (pcitool_reg_t), 449 mode) != DDI_SUCCESS) { 450 DBG(DBG_TOOLS, dip, "Copyout failed.\n"); 451 return (EFAULT); 452 } 453 454 return (rval); 455 } 456