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