/* * Save the configuration registers for cdip as a property * so that it persists after detach/uninitchild. */ int pci_save_config_regs(dev_info_t *dip) { ddi_acc_handle_t confhdl; pci_config_header_state_t *chsp; pci_cap_save_desc_t *pci_cap_descp; int ret; uint32_t i, ncaps, nwords; uint32_t *regbuf, *p; uint8_t *maskbuf; size_t maskbufsz, regbufsz, capbufsz; ddi_acc_hdl_t *hp; off_t offset = 0; uint8_t cap_ptr, cap_id; int pcie = 0; PMD(PMD_SX, ("pci_save_config_regs %s:%d\n", ddi_driver_name(dip), ddi_get_instance(dip))) if (pci_config_setup(dip, &confhdl) != DDI_SUCCESS) { cmn_err(CE_WARN, "%s%d can't get config handle", ddi_driver_name(dip), ddi_get_instance(dip)); return (DDI_FAILURE); } /* * Determine if it is a pci express device. If it is, save entire * 4k config space treating it as a array of 32 bit integers. * If it is not, do it in a usual PCI way. */ cap_ptr = pci_config_get8(confhdl, PCI_BCNF_CAP_PTR); /* * Walk the capabilities searching for pci express capability */ while (cap_ptr != PCI_CAP_NEXT_PTR_NULL) { cap_id = pci_config_get8(confhdl, cap_ptr + PCI_CAP_ID); if (cap_id == PCI_CAP_ID_PCI_E) { pcie = 1; break; } cap_ptr = pci_config_get8(confhdl, cap_ptr + PCI_CAP_NEXT_PTR); } if (pcie) { /* PCI express device. Can have data in all 4k space */ regbuf = (uint32_t *)kmem_zalloc((size_t)PCIE_CONF_HDR_SIZE, KM_SLEEP); p = regbuf; /* * Allocate space for mask. * mask size is 128 bytes (4096 / 4 / 8 ) */ maskbufsz = (size_t)((PCIE_CONF_HDR_SIZE/ sizeof (uint32_t)) >> INDEX_SHIFT); maskbuf = (uint8_t *)kmem_zalloc(maskbufsz, KM_SLEEP); hp = impl_acc_hdl_get(confhdl); for (i = 0; i < (PCIE_CONF_HDR_SIZE / sizeof (uint32_t)); i++) { if (ddi_peek32(dip, (int32_t *)(hp->ah_addr + offset), (int32_t *)p) == DDI_SUCCESS) { /* it is readable register. set the bit */ maskbuf[i >> INDEX_SHIFT] |= (uint8_t)(1 << (i & BITMASK)); } p++; offset += sizeof (uint32_t); } if ((ret = ndi_prop_update_byte_array(DDI_DEV_T_NONE, dip, SAVED_CONFIG_REGS_MASK, (uchar_t *)maskbuf, maskbufsz)) != DDI_PROP_SUCCESS) { cmn_err(CE_WARN, "couldn't create %s property while" "saving config space for %s@%d\n", SAVED_CONFIG_REGS_MASK, ddi_driver_name(dip), ddi_get_instance(dip)); } else if ((ret = ndi_prop_update_byte_array(DDI_DEV_T_NONE, dip, SAVED_CONFIG_REGS, (uchar_t *)regbuf, (size_t)PCIE_CONF_HDR_SIZE)) != DDI_PROP_SUCCESS) { (void) ddi_prop_remove(DDI_DEV_T_NONE, dip, SAVED_CONFIG_REGS_MASK); cmn_err(CE_WARN, "%s%d can't update prop %s", ddi_driver_name(dip), ddi_get_instance(dip), SAVED_CONFIG_REGS); } kmem_free(maskbuf, (size_t)maskbufsz); kmem_free(regbuf, (size_t)PCIE_CONF_HDR_SIZE); } else {
static int gfc_register_fetch(dev_info_t *ap, fco_handle_t rp, fc_ci_t *cp) { size_t len; caddr_t virt; int error = 0; uint64_t x; uint32_t l; uint16_t w; uint8_t b; char *name = fc_cell2ptr(cp->svc_name); struct fc_resource *ip; if (fc_cell2int(cp->nargs) != 1) return (fc_syntax_error(cp, "nargs must be 1")); if (fc_cell2int(cp->nresults) < 1) return (fc_syntax_error(cp, "nresults must be >= 1")); virt = fc_cell2ptr(fc_arg(cp, 0)); /* * Determine the access width .. we can switch on the 2nd * character of the name which is "rx@", "rl@", "rb@" or "rw@" */ switch (*(name + 1)) { case 'x': len = sizeof (x); break; case 'l': len = sizeof (l); break; case 'w': len = sizeof (w); break; case 'b': len = sizeof (b); break; } /* * Check the alignment ... */ if (((intptr_t)virt & (len - 1)) != 0) return (fc_priv_error(cp, "unaligned access")); /* * Find if this virt is 'within' a request we know about */ fc_lock_resource_list(rp); for (ip = rp->head; ip != NULL; ip = ip->next) { if (ip->type == RT_MAP) { if ((virt >= (caddr_t)ip->fc_map_virt) && ((virt + len) <= ((caddr_t)ip->fc_map_virt + ip->fc_map_len))) break; } else if (ip->type == RT_CONTIGIOUS) { if ((virt >= (caddr_t)ip->fc_contig_virt) && ((virt + len) <= ((caddr_t)ip->fc_contig_virt + ip->fc_contig_len))) break; } } fc_unlock_resource_list(rp); if (ip == NULL) { return (fc_priv_error(cp, "request not within a " "known mapping or contigious adddress")); } switch (len) { case sizeof (x): if (ip->type == RT_MAP) error = ddi_peek64(rp->child, (int64_t *)virt, (int64_t *)&x); else /* RT_CONTIGIOUS */ x = *(int64_t *)virt; break; case sizeof (l): if (ip->type == RT_MAP) error = ddi_peek32(rp->child, (int32_t *)virt, (int32_t *)&l); else /* RT_CONTIGIOUS */ l = *(int32_t *)virt; break; case sizeof (w): if (ip->type == RT_MAP) error = ddi_peek16(rp->child, (int16_t *)virt, (int16_t *)&w); else /* RT_CONTIGIOUS */ w = *(int16_t *)virt; break; case sizeof (b): if (ip->type == RT_MAP) error = ddi_peek8(rp->child, (int8_t *)virt, (int8_t *)&b); else /* RT_CONTIGIOUS */ b = *(int8_t *)virt; break; } if (error) { FC_DEBUG2(1, CE_CONT, "gfc_register_fetch: access error " "accessing virt %p len %d\n", virt, len); return (fc_priv_error(cp, "access error")); } cp->nresults = fc_int2cell(1); switch (len) { case sizeof (x): fc_result(cp, 0) = x; break; case sizeof (l): fc_result(cp, 0) = fc_uint32_t2cell(l); break; case sizeof (w): fc_result(cp, 0) = fc_uint16_t2cell(w); break; case sizeof (b): fc_result(cp, 0) = fc_uint8_t2cell(b); break; } return (fc_success_op(ap, rp, cp)); }