Example #1
0
/*
 * function to read the PCI Bus, Device, and function numbers for the
 * device instance.
 *
 * dev - handle to device private data
 */
int
oce_get_bdf(struct oce_dev *dev)
{
	pci_regspec_t *pci_rp;
	uint32_t length;
	int rc;

	/* Get "reg" property */
	rc = ddi_prop_lookup_int_array(DDI_DEV_T_ANY, dev->dip,
	    0, "reg", (int **)&pci_rp, (uint_t *)&length);

	if ((rc != DDI_SUCCESS) ||
	    (length < (sizeof (pci_regspec_t) / sizeof (int)))) {
		oce_log(dev, CE_WARN, MOD_CONFIG,
		    "Failed to read \"reg\" property, Status = 0x%x", rc);
		return (rc);
	}

	dev->pci_bus = PCI_REG_BUS_G(pci_rp->pci_phys_hi);
	dev->pci_device = PCI_REG_DEV_G(pci_rp->pci_phys_hi);
	dev->pci_function = PCI_REG_FUNC_G(pci_rp->pci_phys_hi);

	oce_log(dev, CE_NOTE, MOD_CONFIG,
	    "\"reg\" property num=%d, Bus=%d, Device=%d, Function=%d",
	    length, dev->pci_bus, dev->pci_device, dev->pci_function);

	/* Free the memory allocated by ddi_prop_lookup_int_array() */
	ddi_prop_free(pci_rp);
	return (rc);
}
static int
gfxp_pci_get_bsf(dev_info_t *dip, uint8_t *bus, uint8_t *dev, uint8_t *func)
{
	pci_regspec_t   *pci_rp;
	uint32_t	length;
	int	rc;

	/* get "reg" property */
	rc = ddi_prop_lookup_int_array(DDI_DEV_T_ANY, dip,
		DDI_PROP_DONTPASS, "reg", (int **)&pci_rp,
		(uint_t *)&length);
	if ((rc != DDI_SUCCESS) || (length <
			(sizeof (pci_regspec_t) / sizeof (int)))) {
		return (DDI_FAILURE);
	}

	*bus = PCI_REG_BUS_G(pci_rp->pci_phys_hi);
	*dev = PCI_REG_DEV_G(pci_rp->pci_phys_hi);
	*func = PCI_REG_FUNC_G(pci_rp->pci_phys_hi);

	/*
	 * free the memory allocated by ddi_prop_lookup_int_array().
	 */
	ddi_prop_free(pci_rp);

	return (DDI_SUCCESS);
}
static int
find_target_node(di_node_t node, void *arg)
{
    int *regbuf = NULL;
    int len = 0;
    uint32_t busno, funcno, devno;
    i_devnode_t *devnode = (i_devnode_t *)arg;

    /*
     * Test the property functions, only for testing
     */
    /*
    void *prop = DI_PROP_NIL;

    (void) fprintf(stderr, "start of node 0x%x\n", node->nodeid);
    while ((prop = di_prop_hw_next(node, prop)) != DI_PROP_NIL) {
	int i;
	(void) fprintf(stderr, "name=%s: ", di_prop_name(prop));
	len = 0;
	if (!strcmp(di_prop_name(prop), "reg")) {
	    len = di_prop_ints(prop, &regbuf);
	}
	for (i = 0; i < len; i++) {
	    fprintf(stderr, "0x%0x.", regbuf[i]);
	}
	fprintf(stderr, "\n");
    }
    (void) fprintf(stderr, "end of node 0x%x\n", node->nodeid);
    */

    len = di_prop_lookup_ints(DDI_DEV_T_ANY, node, "reg", &regbuf);

#ifdef __sparc
    if ((len <= 0) && di_phdl)
	len = di_prom_prop_lookup_ints(di_phdl, node, "reg", &regbuf);
#endif

    if (len <= 0) {
#ifdef DEBUG
	fprintf(stderr, "error = %x\n", errno);
	fprintf(stderr, "can not find assigned-address\n");
#endif
	return (DI_WALK_CONTINUE);
    }

    busno = PCI_REG_BUS_G(regbuf[0]);
    devno = PCI_REG_DEV_G(regbuf[0]);
    funcno = PCI_REG_FUNC_G(regbuf[0]);

    if ((busno == devnode->bus) &&
	(devno == devnode->dev) &&
	(funcno == devnode->func)) {
	devnode->node = node;

	return (DI_WALK_TERMINATE);
    }

    return (DI_WALK_CONTINUE);
}
int
get_bdf(dev_info_t *dip, int *bus, int *device, int *func)
{
	pci_regspec_t *pci_rp;
	int len;

	if (ddi_prop_lookup_int_array(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS,
	    "reg", (int **)&pci_rp, (uint_t *)&len) != DDI_SUCCESS)
		return (-1);

	if (len < (sizeof (pci_regspec_t) / sizeof (int))) {
		ddi_prop_free(pci_rp);
		return (-1);
	}
	if (bus != NULL)
		*bus = (int)PCI_REG_BUS_G(pci_rp->pci_phys_hi);
	if (device != NULL)
		*device = (int)PCI_REG_DEV_G(pci_rp->pci_phys_hi);
	if (func != NULL)
		*func = (int)PCI_REG_FUNC_G(pci_rp->pci_phys_hi);
	ddi_prop_free(pci_rp);
	return (0);
}
Example #5
0
static int
sunos_add_devices(di_devlink_t link, void *arg)
{
	struct devlink_cbarg	*largs = (struct devlink_cbarg *)arg;
	struct node_args	*nargs;
	di_node_t		myself, pnode;
	uint64_t		session_id = 0;
	uint16_t		bdf = 0;
	struct libusb_device	*dev;
	sunos_dev_priv_t	*devpriv;
	const char		*path, *newpath;
	int			 n, i;
	int			*addr_prop;
	uint8_t			bus_number = 0;

	nargs = (struct node_args *)largs->nargs;
	myself = largs->myself;
	if (nargs->last_ugenpath) {
		/* the same node's links */
		return (DI_WALK_CONTINUE);
	}

	/*
	 * Construct session ID.
	 * session ID = ...parent hub addr|hub addr|dev addr.
	 */
	pnode = myself;
	i = 0;
	while (pnode != DI_NODE_NIL) {
		if (di_prop_exists(DDI_DEV_T_ANY, pnode, "root-hub") == 1) {
			/* walk to root */
			uint32_t *regbuf = NULL;
			uint32_t reg;

			n = di_prop_lookup_ints(DDI_DEV_T_ANY, pnode, "reg",
			    (int **)&regbuf);
			reg = regbuf[0];
			bdf = (PCI_REG_BUS_G(reg) << 8) |
			    (PCI_REG_DEV_G(reg) << 3) | PCI_REG_FUNC_G(reg);
			session_id |= (bdf << i * 8);

			/* same as 'unit-address' property */
			bus_number =
			    (PCI_REG_DEV_G(reg) << 3) | PCI_REG_FUNC_G(reg);

			usbi_dbg("device bus address=%s:%x",
			    di_bus_addr(pnode), bus_number);

			break;
		}

		/* usb_addr */
		n = di_prop_lookup_ints(DDI_DEV_T_ANY, pnode,
		    "assigned-address", &addr_prop);
		if ((n != 1) || (addr_prop[0] == 0)) {
			usbi_dbg("cannot get valid usb_addr");

			return (DI_WALK_CONTINUE);
		}

		session_id |= ((addr_prop[0] & 0xff) << i * 8);
		if (++i > 7)
			break;

		pnode = di_parent_node(pnode);
	}

	path = di_devlink_path(link);
	dev = usbi_get_device_by_session_id(nargs->ctx, session_id);
	if (dev == NULL) {
		dev = usbi_alloc_device(nargs->ctx, session_id);
		if (dev == NULL) {
			usbi_dbg("can't alloc device");

			return (DI_WALK_TERMINATE);
		}
		devpriv = (sunos_dev_priv_t *)dev->os_priv;
		if ((newpath = strrchr(path, '/')) == NULL) {
			libusb_unref_device(dev);

			return (DI_WALK_TERMINATE);
		}
		devpriv->ugenpath = strndup(path, strlen(path) -
		    strlen(newpath));
		dev->bus_number = bus_number;

		if (sunos_fill_in_dev_info(myself, dev) != LIBUSB_SUCCESS) {
			libusb_unref_device(dev);

			return (DI_WALK_TERMINATE);
		}
		if (usbi_sanitize_device(dev) < 0) {
			libusb_unref_device(dev);
			usbi_dbg("sanatize failed: ");
			return (DI_WALK_TERMINATE);
		}
	} else {
		usbi_dbg("Dev %s exists", path);
	}

	devpriv = (sunos_dev_priv_t *)dev->os_priv;
	if (nargs->last_ugenpath == NULL) {
		/* first device */
		nargs->last_ugenpath = devpriv->ugenpath;

		if (discovered_devs_append(*(nargs->discdevs), dev) == NULL) {
			usbi_dbg("cannot append device");
		}

		/*
		 * we alloc and hence ref this dev. We don't need to ref it
		 * hereafter. Front end or app should take care of their ref.
		 */
		libusb_unref_device(dev);
	}

	usbi_dbg("Device %s %s id=0x%llx, devcount:%d, bdf=%x",
	    devpriv->ugenpath, path, (uint64_t)session_id,
	    (*nargs->discdevs)->len, bdf);

	return (DI_WALK_CONTINUE);
}
Example #6
0
/*
 * This function is called from di_walk_minor() when any PROBE is processed
 */
static int
probe_nexus_node(di_node_t di_node, di_minor_t minor, void *arg)
{
    probe_info_t *pinfo = (probe_info_t *)arg;
    char *nexus_name, *nexus_dev_path;
    nexus_t *nexus;
    int fd;
    char nexus_path[MAXPATHLEN];

    di_prop_t prop;
    char *strings;
    int *ints;
    int numval;
    int pci_node = 0;
    int first_bus = 0, last_bus = PCI_REG_BUS_G(PCI_REG_BUS_M);
    int domain = 0;
    di_node_t rnode =  DI_NODE_NIL;
#ifdef __sparc
    int bus_range_found = 0;
    int device_type_found = 0;
    di_prom_prop_t prom_prop;
#endif


#ifdef DEBUG
    nexus_name = di_devfs_minor_path(minor);
    fprintf(stderr, "-- device name: %s\n", nexus_name);
    di_devfs_path_free(nexus_name);
#endif

    for (prop = di_prop_next(di_node, NULL); prop != NULL;
	 prop = di_prop_next(di_node, prop)) {

	const char *prop_name = di_prop_name(prop);

#ifdef DEBUG
	fprintf(stderr, "   property: %s\n", prop_name);
#endif

	if (strcmp(prop_name, "device_type") == 0) {
	    numval = di_prop_strings(prop, &strings);
	    if (numval == 1) {
		if (strncmp(strings, "pci", 3) != 0)
		    /* not a PCI node, bail */
		    return (DI_WALK_CONTINUE);
		else {
		    pci_node = 1;
#ifdef __sparc
		    device_type_found =  1;
#endif
		}
	    }
	}
	else if (strcmp(prop_name, "class-code") == 0) {
	    /* not a root bus node, bail */
	    return (DI_WALK_CONTINUE);
	}
	else if (strcmp(prop_name, "bus-range") == 0) {
	    numval = di_prop_ints(prop, &ints);
	    if (numval == 2) {
		first_bus = ints[0];
		last_bus = ints[1];
#ifdef __sparc
		bus_range_found = 1;
#endif
	    }
	}
#ifdef __sparc
	domain = nexus_count;
#else
	else if (strcmp(prop_name, "pciseg") == 0) {
	    numval = di_prop_ints(prop, &ints);
	    if (numval == 1) {
		domain = ints[0];
	    }
	}
#endif
    }
Example #7
0
static int
probe_device_node(di_node_t node, void *arg)
{
    int *retbuf = NULL;
    int len = 0, i;
    struct pci_device	*pci_base;
    probe_info_t *pinfo = ((probe_args_t *)arg)->pinfo;
    nexus_t *nexus = ((probe_args_t *)arg)->nexus;
    property_info_t property_list[] = {
        { "class-code", 0 },
        { "device-id", 0 },
        { "vendor-id", 0 },
        { "revision-id", 0},
        { "subsystem-vendor-id", 0},
        { "subsystem-id", 0},
    };
#define NUM_PROPERTIES		sizeof(property_list)/sizeof(property_info_t)

    len = di_prop_lookup_ints(DDI_DEV_T_ANY, node, "reg", &retbuf);

#ifdef __sparc
    if ((len <= 0) && di_phdl)
	len = di_prom_prop_lookup_ints(di_phdl, node, "reg", &retbuf);
#endif

    /* Exclude usb devices */
    if (len < 5) {
	return DI_WALK_CONTINUE;
    }

    pci_base = &pinfo->devices[pinfo->num_devices].base;

    pci_base->domain = nexus->domain;
    pci_base->bus = PCI_REG_BUS_G(retbuf[0]);
    pci_base->dev = PCI_REG_DEV_G(retbuf[0]);
    pci_base->func  = PCI_REG_FUNC_G(retbuf[0]);

    /* Get property values */
    for (i = 0; i < NUM_PROPERTIES; i++) {
	len = di_prop_lookup_ints(DDI_DEV_T_ANY, node,
		property_list[i].name, &retbuf);
#ifdef __sparc
	if ((len <= 0) && di_phdl)
	    len = di_prom_prop_lookup_ints(di_phdl, node,
		property_list[i].name, &retbuf);
#endif

	if (len > 0)
	    property_list[i].value = retbuf[0];
	else {
	    /* a device must have property "class-code", "device-id", "vendor-id" */
	    if (i < 3)
		return DI_WALK_CONTINUE;
#ifdef DEBUG
	    fprintf(stderr, "cannot get property \"%s\" for nexus = %s :\n",
		property_list[i].name, nexus->path);
	    fprintf(stderr, "	domain = %x, busno = %x, devno = %x, funcno = %x\n",
		pci_base->domain, pci_base->bus, pci_base->dev, pci_base->func);
#endif
	}
    }

    if ((property_list[1].value == 0) && (property_list[2].value == 0))
	return DI_WALK_CONTINUE;

    pci_base->device_class = property_list[0].value;
    pci_base->device_id = property_list[1].value;
    pci_base->vendor_id = property_list[2].value;
    pci_base->revision = property_list[3].value;
    pci_base->subvendor_id = property_list[4].value;
    pci_base->subdevice_id = property_list[5].value;

#ifdef DEBUG
    fprintf(stderr,
	    "nexus = %s, domain = %x, busno = %x, devno = %x, funcno = %x\n",
	    nexus->path, pci_base->domain, pci_base->bus, pci_base->dev, pci_base->func);
#endif

    pinfo->num_devices++;
    if (pinfo->num_devices == pinfo->num_allocated_elems) {
	struct pci_device_private *new_devs;
	size_t new_num_elems = pinfo->num_allocated_elems * 2;

	new_devs = realloc(pinfo->devices,
	new_num_elems * sizeof (struct pci_device_private));
	if (new_devs == NULL) {
	    (void) fprintf(stderr,
	           "Error allocating memory for PCI devices:"
		   " %s\n discarding additional devices\n",
		   strerror(errno));
	    ((probe_args_t *)arg)->ret = 1;
	    return (DI_WALK_TERMINATE);
	}
	(void) memset(&new_devs[pinfo->num_devices], 0,
		pinfo->num_allocated_elems *
		sizeof (struct pci_device_private));
	pinfo->num_allocated_elems = new_num_elems;
	pinfo->devices = new_devs;
    }

    return (DI_WALK_CONTINUE);
}
/*
 * This function is called from di_walk_minor() when any PROBE is processed
 */
static int
probe_nexus_node(di_node_t di_node, di_minor_t minor, void *arg)
{
    probe_info_t *pinfo = (probe_info_t *)arg;
    char *nexus_name, *nexus_dev_path;
    nexus_t *nexus;
    int fd;
    char nexus_path[MAXPATHLEN];

    di_prop_t prop;
    char *strings;
    int *ints;
    int numval;
    int pci_node = 0;
    int first_bus = 0, last_bus = PCI_REG_BUS_G(PCI_REG_BUS_M);
    int domain = 0;
#ifdef __sparc
    int bus_range_found = 0;
    int device_type_found = 0;
    di_prom_prop_t prom_prop;
#endif


#ifdef DEBUG
    nexus_name = di_devfs_minor_path(minor);
    fprintf(stderr, "-- device name: %s\n", nexus_name);
#endif

    for (prop = di_prop_next(di_node, NULL); prop != NULL;
	 prop = di_prop_next(di_node, prop)) {

	const char *prop_name = di_prop_name(prop);

#ifdef DEBUG
	fprintf(stderr, "   property: %s\n", prop_name);
#endif

	if (strcmp(prop_name, "device_type") == 0) {
	    numval = di_prop_strings(prop, &strings);
	    if (numval == 1) {
		if (strncmp(strings, "pci", 3) != 0)
		    /* not a PCI node, bail */
		    return (DI_WALK_CONTINUE);
		else {
		    pci_node = 1;
#ifdef __sparc
		    device_type_found =  1;
#endif
		}
	    }
	}
	else if (strcmp(prop_name, "class-code") == 0) {
	    /* not a root bus node, bail */
	    return (DI_WALK_CONTINUE);
	}
	else if (strcmp(prop_name, "bus-range") == 0) {
	    numval = di_prop_ints(prop, &ints);
	    if (numval == 2) {
		first_bus = ints[0];
		last_bus = ints[1];
#ifdef __sparc
		bus_range_found = 1;
#endif
	    }
	}
	else if (strcmp(prop_name, "pciseg") == 0) {
	    numval = di_prop_ints(prop, &ints);
	    if (numval == 1) {
		domain = ints[0];
	    }
	}
    }

#ifdef __sparc
    if ((!device_type_found) && di_phdl) {
	numval = di_prom_prop_lookup_strings(di_phdl, di_node,
	    "device_type", &strings);
	if (numval == 1) {
	    if (strncmp(strings, "pci", 3) != 0)
		return (DI_WALK_CONTINUE);
	    else
		pci_node = 1;
	}
    }

    if ((!bus_range_found) && di_phdl) {
	numval = di_prom_prop_lookup_ints(di_phdl, di_node,
	    "bus-range", &ints);
	if (numval == 2) {
	    first_bus = ints[0];
	    last_bus = ints[1];
	}
    }
#endif

    if (pci_node != 1)
	return (DI_WALK_CONTINUE);

    /* we have a PCI root bus node. */
    nexus = calloc(1, sizeof(nexus_t));
    if (nexus == NULL) {
	(void) fprintf(stderr, "Error allocating memory for nexus: %s\n",
		       strerror(errno));
	return (DI_WALK_TERMINATE);
    }
    nexus->first_bus = first_bus;
    nexus->last_bus = last_bus;
    nexus->domain = domain;

#ifdef __sparc
    if ((nexus->devlist = calloc(INITIAL_NUM_DEVICES,
			sizeof (struct pci_device *))) == NULL) {
	(void) fprintf(stderr, "Error allocating memory for nexus devlist: %s\n",
                       strerror(errno));
	free (nexus);
	return (DI_WALK_TERMINATE);
    }
    nexus->num_allocated_elems = INITIAL_NUM_DEVICES;
    nexus->num_devices = 0;
#endif

    nexus_name = di_devfs_minor_path(minor);
    if (nexus_name == NULL) {
	(void) fprintf(stderr, "Error getting nexus path: %s\n",
		       strerror(errno));
	free(nexus);
	return (DI_WALK_CONTINUE);
    }

    snprintf(nexus_path, sizeof(nexus_path), "/devices%s", nexus_name);
    di_devfs_path_free(nexus_name);

#ifdef DEBUG
    fprintf(stderr, "nexus = %s, bus-range = %d - %d\n",
	    nexus_path, first_bus, last_bus);
#endif

    if ((fd = open(nexus_path, O_RDWR | O_CLOEXEC)) >= 0) {
	nexus->fd = fd;
	nexus->path = strdup(nexus_path);
	nexus_dev_path = di_devfs_path(di_node);
	nexus->dev_path = strdup(nexus_dev_path);
	di_devfs_path_free(nexus_dev_path);
	if ((do_probe(nexus, pinfo) != 0) && (errno != ENXIO)) {
	    (void) fprintf(stderr, "Error probing node %s: %s\n",
			   nexus_path, strerror(errno));
	    (void) close(fd);
	    free(nexus->path);
	    free(nexus->dev_path);
	    free(nexus);
	} else {
	    nexus->next = nexus_list;
	    nexus_list = nexus;
	}
    } else {
	(void) fprintf(stderr, "Error opening %s: %s\n",
		       nexus_path, strerror(errno));
	free(nexus);
    }

    return DI_WALK_CONTINUE;
}
Example #9
0
static int
pci_bus_map(dev_info_t *dip, dev_info_t *rdip, ddi_map_req_t *mp,
	off_t offset, off_t len, caddr_t *vaddrp)
{
	struct regspec reg;
	ddi_map_req_t mr;
	ddi_acc_hdl_t *hp;
	pci_regspec_t pci_reg;
	pci_regspec_t *pci_rp;
	int 	rnumber;
	int	length;
	pci_acc_cfblk_t *cfp;
	int	space;


	mr = *mp; /* Get private copy of request */
	mp = &mr;

	/*
	 * check for register number
	 */
	switch (mp->map_type) {
	case DDI_MT_REGSPEC:
		pci_reg = *(pci_regspec_t *)(mp->map_obj.rp);
		pci_rp = &pci_reg;
		if (pci_common_get_reg_prop(rdip, pci_rp) != DDI_SUCCESS)
			return (DDI_FAILURE);
		break;
	case DDI_MT_RNUMBER:
		rnumber = mp->map_obj.rnumber;
		/*
		 * get ALL "reg" properties for dip, select the one of
		 * of interest. In x86, "assigned-addresses" property
		 * is identical to the "reg" property, so there is no
		 * need to cross check the two to determine the physical
		 * address of the registers.
		 * This routine still performs some validity checks to
		 * make sure that everything is okay.
		 */
		if (ddi_prop_lookup_int_array(DDI_DEV_T_ANY, rdip,
		    DDI_PROP_DONTPASS, "reg", (int **)&pci_rp,
		    (uint_t *)&length) != DDI_PROP_SUCCESS)
			return (DDI_FAILURE);

		/*
		 * validate the register number.
		 */
		length /= (sizeof (pci_regspec_t) / sizeof (int));
		if (rnumber >= length) {
			ddi_prop_free(pci_rp);
			return (DDI_FAILURE);
		}

		/*
		 * copy the required entry.
		 */
		pci_reg = pci_rp[rnumber];

		/*
		 * free the memory allocated by ddi_prop_lookup_int_array
		 */
		ddi_prop_free(pci_rp);

		pci_rp = &pci_reg;
		if (pci_common_get_reg_prop(rdip, pci_rp) != DDI_SUCCESS)
			return (DDI_FAILURE);
		mp->map_type = DDI_MT_REGSPEC;
		break;
	default:
		return (DDI_ME_INVAL);
	}

	space = pci_rp->pci_phys_hi & PCI_REG_ADDR_M;

	/*
	 * check for unmap and unlock of address space
	 */
	if ((mp->map_op == DDI_MO_UNMAP) || (mp->map_op == DDI_MO_UNLOCK)) {
		/*
		 * Adjust offset and length
		 * A non-zero length means override the one in the regspec.
		 */
		pci_rp->pci_phys_low += (uint_t)offset;
		if (len != 0)
			pci_rp->pci_size_low = len;

		switch (space) {
		case PCI_ADDR_CONFIG:
			/* No work required on unmap of Config space */
			return (DDI_SUCCESS);

		case PCI_ADDR_IO:
			reg.regspec_bustype = 1;
			break;

		case PCI_ADDR_MEM64:
			/*
			 * MEM64 requires special treatment on map, to check
			 * that the device is below 4G.  On unmap, however,
			 * we can assume that everything is OK... the map
			 * must have succeeded.
			 */
			/* FALLTHROUGH */
		case PCI_ADDR_MEM32:
			reg.regspec_bustype = 0;
			break;

		default:
			return (DDI_FAILURE);
		}
		reg.regspec_addr = pci_rp->pci_phys_low;
		reg.regspec_size = pci_rp->pci_size_low;

		mp->map_obj.rp = &reg;
		return (ddi_map(dip, mp, (off_t)0, (off_t)0, vaddrp));

	}

	/* check for user mapping request - not legal for Config */
	if (mp->map_op == DDI_MO_MAP_HANDLE && space == PCI_ADDR_CONFIG) {
		return (DDI_FAILURE);
	}

	/*
	 * check for config space
	 * On x86, CONFIG is not mapped via MMU and there is
	 * no endian-ness issues. Set the attr field in the handle to
	 * indicate that the common routines to call the nexus driver.
	 */
	if (space == PCI_ADDR_CONFIG) {
		/* Can't map config space without a handle */
		hp = (ddi_acc_hdl_t *)mp->map_handlep;
		if (hp == NULL)
			return (DDI_FAILURE);

		/* record the device address for future reference */
		cfp = (pci_acc_cfblk_t *)&hp->ah_bus_private;
		cfp->c_busnum = PCI_REG_BUS_G(pci_rp->pci_phys_hi);
		cfp->c_devnum = PCI_REG_DEV_G(pci_rp->pci_phys_hi);
		cfp->c_funcnum = PCI_REG_FUNC_G(pci_rp->pci_phys_hi);

		*vaddrp = (caddr_t)offset;
		return (pci_fm_acc_setup(hp, offset, len));
	}

	/*
	 * range check
	 */
	if ((offset >= pci_rp->pci_size_low) ||
	    (len > pci_rp->pci_size_low) ||
	    (offset + len > pci_rp->pci_size_low)) {
		return (DDI_FAILURE);
	}

	/*
	 * Adjust offset and length
	 * A non-zero length means override the one in the regspec.
	 */
	pci_rp->pci_phys_low += (uint_t)offset;
	if (len != 0)
		pci_rp->pci_size_low = len;

	/*
	 * convert the pci regsec into the generic regspec used by the
	 * parent root nexus driver.
	 */
	switch (space) {
	case PCI_ADDR_IO:
		reg.regspec_bustype = 1;
		break;
	case PCI_ADDR_MEM64:
		/*
		 * We can't handle 64-bit devices that are mapped above
		 * 4G or that are larger than 4G.
		 */
		if (pci_rp->pci_phys_mid != 0 ||
		    pci_rp->pci_size_hi != 0)
			return (DDI_FAILURE);
		/*
		 * Other than that, we can treat them as 32-bit mappings
		 */
		/* FALLTHROUGH */
	case PCI_ADDR_MEM32:
		reg.regspec_bustype = 0;
		break;
	default:
		return (DDI_FAILURE);
	}
	reg.regspec_addr = pci_rp->pci_phys_low;
	reg.regspec_size = pci_rp->pci_size_low;

	mp->map_obj.rp = &reg;
	return (ddi_map(dip, mp, (off_t)0, (off_t)0, vaddrp));
}