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