/*ARGSUSED*/ static int pxtool_access(px_t *px_p, pcitool_reg_t *prg_p, uint64_t *data_p, boolean_t is_write) { dev_info_t *dip = px_p->px_dip; uint64_t phys_addr = prg_p->phys_addr; boolean_t endian = PCITOOL_ACC_IS_BIG_ENDIAN(prg_p->acc_attr); size_t size = PCITOOL_ACC_ATTR_SIZE(prg_p->acc_attr); int rval = SUCCESS; /* Alignment checking. Assumes base address is 8-byte aligned. */ if (!IS_P2ALIGNED(phys_addr, size)) { DBG(DBG_TOOLS, dip, "not aligned.\n"); prg_p->status = PCITOOL_NOT_ALIGNED; rval = EINVAL; } else if (is_write) { /* Made it through checks. Do the access. */ DBG(DBG_PHYS_ACC, dip, "%d byte %s pxtool_safe_phys_poke at addr 0x%" PRIx64 "\n", size, (endian ? "BE" : "LE"), phys_addr); if (pxtool_safe_phys_poke(px_p, endian, size, phys_addr, *data_p) != DDI_SUCCESS) { DBG(DBG_PHYS_ACC, dip, "%d byte %s pxtool_safe_phys_poke at addr " "0x%" PRIx64 " failed\n", size, (endian ? "BE" : "LE"), phys_addr); prg_p->status = PCITOOL_INVALID_ADDRESS; rval = EFAULT; } } else { /* Read */ DBG(DBG_PHYS_ACC, dip, "%d byte %s pxtool_safe_phys_peek at addr 0x%" PRIx64 "\n", size, (endian ? "BE" : "LE"), phys_addr); if (pxtool_safe_phys_peek(px_p, endian, size, phys_addr, data_p) != DDI_SUCCESS) { DBG(DBG_PHYS_ACC, dip, "%d byte %s pxtool_safe_phys_peek at addr " "0x%" PRIx64 " failed\n", size, (endian ? "BE" : "LE"), phys_addr); prg_p->status = PCITOOL_INVALID_ADDRESS; rval = EFAULT; } } return (rval); }
/* * Perform register accesses on the nexus device itself. */ int pxtool_bus_reg_ops(dev_info_t *dip, void *arg, int cmd, int mode) { pcitool_reg_t prg; size_t size; px_t *px_p = DIP_TO_STATE(dip); boolean_t is_write = B_FALSE; uint32_t rval = 0; if (cmd == PCITOOL_NEXUS_SET_REG) is_write = B_TRUE; DBG(DBG_TOOLS, dip, "pxtool_bus_reg_ops set/get reg\n"); /* Read data from userland. */ if (ddi_copyin(arg, &prg, sizeof (pcitool_reg_t), mode) != DDI_SUCCESS) { DBG(DBG_TOOLS, dip, "Error reading arguments\n"); return (EFAULT); } size = PCITOOL_ACC_ATTR_SIZE(prg.acc_attr); DBG(DBG_TOOLS, dip, "raw bus:0x%x, dev:0x%x, func:0x%x\n", prg.bus_no, prg.dev_no, prg.func_no); DBG(DBG_TOOLS, dip, "barnum:0x%x, offset:0x%" PRIx64 ", acc:0x%x\n", prg.barnum, prg.offset, prg.acc_attr); DBG(DBG_TOOLS, dip, "data:0x%" PRIx64 ", phys_addr:0x%" PRIx64 "\n", prg.data, prg.phys_addr); /* * If bank num == ff, base phys addr passed in from userland. * * Normal bank specification is invalid, as there is no OBP property to * back it up. */ if (prg.barnum != PCITOOL_BASE) { prg.status = PCITOOL_OUT_OF_RANGE; rval = EINVAL; goto done; } /* Allow only size of 8-bytes. */ if (size != sizeof (uint64_t)) { prg.status = PCITOOL_INVALID_SIZE; rval = EINVAL; goto done; } /* Alignment checking. */ if (!IS_P2ALIGNED(prg.offset, size)) { DBG(DBG_TOOLS, dip, "not aligned.\n"); prg.status = PCITOOL_NOT_ALIGNED; rval = EINVAL; goto done; } prg.phys_addr += prg.offset; /* * Only the hypervisor can access nexus registers. As a result, there * can be no error recovery in the OS. If there is an error, the * system will go down, but with a trap type 7f. The OS cannot * intervene with this kind of trap. */ /* Access device. prg.status is modified. */ rval = pxtool_phys_access(px_p, prg.phys_addr, &prg.data, PCITOOL_ACC_IS_BIG_ENDIAN(prg.acc_attr), is_write); done: prg.drvr_version = PCITOOL_VERSION; if (ddi_copyout(&prg, arg, sizeof (pcitool_reg_t), mode) != DDI_SUCCESS) { DBG(DBG_TOOLS, dip, "Copyout failed.\n"); return (EFAULT); } return (rval); }
/* * This function is for PCI IO space and memory space access. * It assumes that offset, bdf, acc_attr are current in prg_p. * It assumes that prg_p->phys_addr is the final phys addr (including offset). * This function modifies prg_p status and data. */ int pxtool_pciiomem_access(px_t *px_p, pcitool_reg_t *prg_p, uint64_t *data_p, boolean_t is_write) { on_trap_data_t otd; uint32_t io_stat = 0; dev_info_t *dip = px_p->px_dip; px_pec_t *pec_p = px_p->px_pec_p; size_t size = PCITOOL_ACC_ATTR_SIZE(prg_p->acc_attr); int rval = 0; /* Alignment checking. */ if (!IS_P2ALIGNED(prg_p->offset, size)) { DBG(DBG_TOOLS, dip, "not aligned.\n"); prg_p->status = PCITOOL_NOT_ALIGNED; return (EINVAL); } mutex_enter(&pec_p->pec_pokefault_mutex); pec_p->pec_ontrap_data = &otd; if (is_write) { pci_device_t bdf = PX_GET_BDF(prg_p); if (PCITOOL_ACC_IS_BIG_ENDIAN(prg_p->acc_attr)) *data_p = pxtool_swap_endian(*data_p, size); pec_p->pec_safeacc_type = DDI_FM_ERR_POKE; if (!on_trap(&otd, OT_DATA_ACCESS)) { otd.ot_trampoline = (uintptr_t)&poke_fault; rval = hvio_poke(px_p->px_dev_hdl, prg_p->phys_addr, size, *data_p, bdf, &io_stat); } else rval = H_EIO; if (otd.ot_trap & OT_DATA_ACCESS) rval = H_EIO; DBG(DBG_TOOLS, dip, "iomem:phys_addr:0x%" PRIx64 ", bdf:0x%x, " "rval:%d, io_stat:%d\n", prg_p->phys_addr, bdf, rval, io_stat); } else { *data_p = 0; pec_p->pec_safeacc_type = DDI_FM_ERR_PEEK; if (!on_trap(&otd, OT_DATA_ACCESS)) { otd.ot_trampoline = (uintptr_t)&peek_fault; rval = hvio_peek(px_p->px_dev_hdl, prg_p->phys_addr, size, &io_stat, data_p); } else rval = H_EIO; DBG(DBG_TOOLS, dip, "iomem:phys_addr:0x%" PRIx64 ", " "size:0x%" PRIx64 ", hdl:0x%" PRIx64 ", " "rval:%d, io_stat:%d\n", prg_p->phys_addr, size, px_p->px_dev_hdl, rval, io_stat); DBG(DBG_TOOLS, dip, "read data:0x%" PRIx64 "\n", *data_p); if (PCITOOL_ACC_IS_BIG_ENDIAN(prg_p->acc_attr)) *data_p = pxtool_swap_endian(*data_p, size); } /* * Workaround: delay taking down safe access env. * For more info, see comment where pxtool_iomem_delay_usec is declared. */ if (pxtool_iomem_delay_usec > 0) delay(drv_usectohz(pxtool_iomem_delay_usec)); no_trap(); pec_p->pec_ontrap_data = NULL; pec_p->pec_safeacc_type = DDI_FM_ERR_UNEXPECTED; mutex_exit(&pec_p->pec_pokefault_mutex); if (rval != SUCCESS) { prg_p->status = PCITOOL_INVALID_ADDRESS; rval = EINVAL; } else if (io_stat != SUCCESS) { prg_p->status = PCITOOL_IO_ERROR; rval = EIO; } else prg_p->status = PCITOOL_SUCCESS; return (rval); }
int pxtool_pcicfg_access(px_t *px_p, pcitool_reg_t *prg_p, uint64_t *data_p, boolean_t is_write) { pci_cfg_data_t data; on_trap_data_t otd; dev_info_t *dip = px_p->px_dip; px_pec_t *pec_p = px_p->px_pec_p; size_t size = PCITOOL_ACC_ATTR_SIZE(prg_p->acc_attr); int rval = 0; pci_cfgacc_req_t req; if ((size <= 0) || (size > 8)) { DBG(DBG_TOOLS, dip, "not supported size.\n"); prg_p->status = PCITOOL_INVALID_SIZE; return (ENOTSUP); } /* Alignment checking. */ if (!IS_P2ALIGNED(prg_p->offset, size)) { DBG(DBG_TOOLS, dip, "not aligned.\n"); prg_p->status = PCITOOL_NOT_ALIGNED; return (EINVAL); } mutex_enter(&pec_p->pec_pokefault_mutex); pec_p->pec_ontrap_data = &otd; req.rcdip = dip; req.bdf = PCI_GETBDF(prg_p->bus_no, prg_p->dev_no, prg_p->func_no); req.offset = prg_p->offset; req.size = size; req.write = is_write; if (is_write) { if (PCITOOL_ACC_IS_BIG_ENDIAN(prg_p->acc_attr)) data.qw = pxtool_swap_endian(*data_p, size); else data.qw = *data_p; switch (size) { case sizeof (uint8_t): data.b = (uint8_t)data.qw; break; case sizeof (uint16_t): data.w = (uint16_t)data.qw; break; case sizeof (uint32_t): data.dw = (uint32_t)data.qw; break; case sizeof (uint64_t): break; } DBG(DBG_TOOLS, dip, "put: bdf:%d,%d,%d, off:0x%"PRIx64", size:" "0x%"PRIx64", data:0x%"PRIx64"\n", prg_p->bus_no, prg_p->dev_no, prg_p->func_no, prg_p->offset, size, data.qw); pec_p->pec_safeacc_type = DDI_FM_ERR_POKE; if (!on_trap(&otd, OT_DATA_ACCESS)) { otd.ot_trampoline = (uintptr_t)&poke_fault; VAL64(&req) = data.qw; pci_cfgacc_acc(&req); } else rval = H_EIO; if (otd.ot_trap & OT_DATA_ACCESS) rval = H_EIO; } else { data.qw = 0; pec_p->pec_safeacc_type = DDI_FM_ERR_PEEK; if (!on_trap(&otd, OT_DATA_ACCESS)) { otd.ot_trampoline = (uintptr_t)&peek_fault; pci_cfgacc_acc(&req); data.qw = VAL64(&req); } else rval = H_EIO; switch (size) { case sizeof (uint8_t): data.qw = (uint64_t)data.b; break; case sizeof (uint16_t): data.qw = (uint64_t)data.w; break; case sizeof (uint32_t): data.qw = (uint64_t)data.dw; break; case sizeof (uint64_t): break; } DBG(DBG_TOOLS, dip, "get: bdf:%d,%d,%d, off:0x%"PRIx64", size:" "0x%"PRIx64", data:0x%"PRIx64"\n", prg_p->bus_no, prg_p->dev_no, prg_p->func_no, prg_p->offset, size, data.qw); *data_p = data.qw; if (PCITOOL_ACC_IS_BIG_ENDIAN(prg_p->acc_attr)) *data_p = pxtool_swap_endian(*data_p, size); } /* * Workaround: delay taking down safe access env. * For more info, see comments where pxtool_cfg_delay_usec is declared. */ if (pxtool_cfg_delay_usec > 0) drv_usecwait(pxtool_cfg_delay_usec); no_trap(); pec_p->pec_ontrap_data = NULL; pec_p->pec_safeacc_type = DDI_FM_ERR_UNEXPECTED; mutex_exit(&pec_p->pec_pokefault_mutex); if (rval != SUCCESS) { prg_p->status = PCITOOL_INVALID_ADDRESS; rval = EINVAL; } else prg_p->status = PCITOOL_SUCCESS; return (rval); }
/* * Solaris version: read the VGA ROM data */ static int pci_device_solx_devfs_read_rom( struct pci_device * dev, void * buffer ) { int err; struct pci_device_mapping prom = { .base = 0xC0000, .size = dev->rom_size, .flags = 0 }; err = pci_device_solx_devfs_map_range(dev, &prom); if (err == 0) { (void) bcopy(prom.memory, buffer, dev->rom_size); if (munmap(prom.memory, dev->rom_size) == -1) { err = errno; } } return err; } /* * solaris version: Read the configurations space of the devices */ static int pci_device_solx_devfs_read( struct pci_device * dev, void * data, pciaddr_t offset, pciaddr_t size, pciaddr_t * bytes_read ) { pcitool_reg_t cfg_prg; int err = 0; int i = 0; nexus_t *nexus; #ifdef __sparc nexus = find_nexus_for_dev(dev); #else nexus = find_nexus_for_bus(dev->domain, dev->bus); #endif *bytes_read = 0; if ( nexus == NULL ) { return ENODEV; } cfg_prg.offset = offset; cfg_prg.acc_attr = PCITOOL_ACC_ATTR_SIZE_1 + NATIVE_ENDIAN; cfg_prg.bus_no = dev->bus; cfg_prg.dev_no = dev->dev; cfg_prg.func_no = dev->func; cfg_prg.barnum = 0; cfg_prg.user_version = PCITOOL_USER_VERSION; for (i = 0; i < size; i += PCITOOL_ACC_ATTR_SIZE(PCITOOL_ACC_ATTR_SIZE_1)) { cfg_prg.offset = offset + i; if ((err = ioctl(nexus->fd, PCITOOL_DEVICE_GET_REG, &cfg_prg)) != 0) { fprintf(stderr, "read bdf<%s,%x,%x,%x,%llx> config space failure\n", nexus->path, cfg_prg.bus_no, cfg_prg.dev_no, cfg_prg.func_no, cfg_prg.offset); fprintf(stderr, "Failure cause = %x\n", err); break; } ((uint8_t *)data)[i] = (uint8_t)cfg_prg.data; /* * DWORDS Offset or bytes Offset ?? */ } *bytes_read = i; return (err); }