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