From d76fb36d9c28be0f7c43e0ca1e961c30a7781bd4 Mon Sep 17 00:00:00 2001 From: Henry Zhao Date: Thu, 10 Jan 2013 17:53:09 -0800 Subject: [PATCH] Solaris: probe improvement Remove pcitool dependency in probing phase. Use the data collected from devinfo tree instead in creating pci file system. Signed-off-by: Henry Zhao Signed-off-by: Alan Coopersmith --- src/solx_devfs.c | 420 +++++++++++++++++-------------------------------------- 1 file changed, 128 insertions(+), 292 deletions(-) diff --git a/src/solx_devfs.c b/src/solx_devfs.c index 108fd1f..433969f 100644 --- a/src/solx_devfs.c +++ b/src/solx_devfs.c @@ -1,6 +1,6 @@ /* * (C) Copyright IBM Corporation 2006 - * Copyright (c) 2007, 2009, 2011, 2012, Oracle and/or its affiliates. + * Copyright (c) 2007, 2009, 2011, 2012, 2013 Oracle and/or its affiliates. * All Rights Reserved. * * Permission is hereby granted, free of charge, to any person obtaining a @@ -51,11 +51,6 @@ #define INITIAL_NUM_DEVICES 256 #define CELL_NUMS_1275 (sizeof(pci_regspec_t) / sizeof(uint_t)) -typedef union { - uint8_t bytes[16 * sizeof (uint32_t)]; - uint32_t dwords[16]; -} pci_conf_hdr_t; - typedef struct i_devnode { uint8_t bus; uint8_t dev; @@ -79,6 +74,17 @@ typedef struct probe_info { struct pci_device_private * volatile devices; } probe_info_t; +typedef struct probe_args { + probe_info_t *pinfo; + nexus_t *nexus; + int ret; +} probe_args_t; + +typedef struct property_info { + const char *name; + int value; +} property_info_t; + static nexus_t *nexus_list = NULL; #if !defined(__sparc) static int xsvc_fd = -1; @@ -141,15 +147,6 @@ find_nexus_for_bus( int domain, int bus ) return NULL; } -#define GET_CONFIG_VAL_8(offset) (config_hdr.bytes[offset]) -#define GET_CONFIG_VAL_16(offset) \ - (uint16_t) (GET_CONFIG_VAL_8(offset) + (GET_CONFIG_VAL_8(offset+1) << 8)) -#define GET_CONFIG_VAL_32(offset) \ - (uint32_t) (GET_CONFIG_VAL_8(offset) + \ - (GET_CONFIG_VAL_8(offset+1) << 8) + \ - (GET_CONFIG_VAL_8(offset+2) << 16) + \ - (GET_CONFIG_VAL_8(offset+3) << 24)) - /* * Release all the resources * Solaris version @@ -198,297 +195,108 @@ pci_system_solx_devfs_destroy_device( struct pci_device *dev ) #endif -/* - * Retrieve first 16 dwords of device's config header, except for the first - * dword. First 16 dwords are defined by the PCI specification. - */ -static int -get_config_header(int fd, uint8_t bus_no, uint8_t dev_no, uint8_t func_no, - pci_conf_hdr_t *config_hdr_p) -{ - pcitool_reg_t cfg_prg; - int i; - int rval = 0; - - /* Prepare a local pcitool_reg_t so as to not disturb the caller's. */ - cfg_prg.offset = 0; - cfg_prg.acc_attr = PCITOOL_ACC_ATTR_SIZE_4 + NATIVE_ENDIAN; - cfg_prg.bus_no = bus_no; - cfg_prg.dev_no = dev_no; - cfg_prg.func_no = func_no; - cfg_prg.barnum = 0; - cfg_prg.user_version = PCITOOL_USER_VERSION; - - /* Get dwords 1-15 of config space. They must be read as uint32_t. */ - for (i = 1; i < (sizeof (pci_conf_hdr_t) / sizeof (uint32_t)); i++) { - cfg_prg.offset += sizeof (uint32_t); - if ((rval = ioctl(fd, PCITOOL_DEVICE_GET_REG, &cfg_prg)) != 0) { - break; - } - config_hdr_p->dwords[i] = (uint32_t)cfg_prg.data; - } - - return (rval); -} - - -/* - * Probe device's functions. Modifies many fields in the prg_p. - */ static int -probe_dev(nexus_t *nexus, pcitool_reg_t *prg_p, probe_info_t *pinfo) +probe_device_node(di_node_t node, void *arg) { - pci_conf_hdr_t config_hdr; - boolean_t multi_function_device; - int8_t func; - int8_t first_func = 0; - int8_t last_func = PCI_REG_FUNC_M >> PCI_REG_FUNC_SHIFT; - int rval = 0; - struct pci_device * pci_base; - - /* - * Loop through at least func=first_func. Continue looping through - * functions if there are no errors and the device is a multi-function - * device. - * - * (Note, if first_func == 0, header will show whether multifunction - * device and set multi_function_device. If first_func != 0, then we - * will force the loop as the user wants a specific function to be - * checked. - */ - for (func = first_func, multi_function_device = B_FALSE; - ((func <= last_func) && - ((func == first_func) || (multi_function_device))); - func++) { - prg_p->func_no = func; - - /* - * Four things can happen here: - * - * 1) ioctl comes back as EFAULT and prg_p->status is - * PCITOOL_INVALID_ADDRESS. There is no device at this location. - * - * 2) ioctl comes back successful and the data comes back as - * zero. Config space is mapped but no device responded. - * - * 3) ioctl comes back successful and the data comes back as - * non-zero. We've found a device. - * - * 4) Some other error occurs in an ioctl. - */ - - prg_p->status = PCITOOL_SUCCESS; - prg_p->offset = 0; - prg_p->data = 0; - prg_p->user_version = PCITOOL_USER_VERSION; - - errno = 0; - if (((rval = ioctl(nexus->fd, PCITOOL_DEVICE_GET_REG, prg_p)) != 0) || - (prg_p->data == 0xffffffff)) { + 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) - /* - * Accept errno == EINVAL along with status of - * PCITOOL_OUT_OF_RANGE because some systems - * don't implement the full range of config space. - * Leave the loop quietly in this case. - */ - if ((errno == EINVAL) || - (prg_p->status == PCITOOL_OUT_OF_RANGE)) { - break; - } + len = di_prop_lookup_ints(DDI_DEV_T_ANY, node, "reg", &retbuf); - /* - * Exit silently with ENXIO as this means that there are - * no devices under the pci root nexus. - */ - else if ((errno == ENXIO) && - (prg_p->status == PCITOOL_IO_ERROR)) { - break; - } - - /* - * Expect errno == EFAULT along with status of - * PCITOOL_INVALID_ADDRESS because there won't be - * devices at each stop. Quit on any other error. - */ - else if (((errno != EFAULT) || - (prg_p->status != PCITOOL_INVALID_ADDRESS)) && - (prg_p->data != 0xffffffff)) { #ifdef __sparc -/* on sparc, devices can be enumerated discontiguously. Do not quit */ - rval = 0; + if ((len <= 0) && di_phdl) + len = di_prom_prop_lookup_ints(di_phdl, node, "reg", &retbuf); #endif - break; - } - - /* - * If no function at this location, - * just advance to the next function. - */ - else { - rval = 0; - } - - /* - * Data came back as 0. - * Treat as unresponsive device and check next device. - */ - } else if (prg_p->data == 0) { - rval = 0; - break; /* Func loop. */ - /* Found something. */ - } else { - config_hdr.dwords[0] = (uint32_t)prg_p->data; - - /* Get the rest of the PCI header. */ - if ((rval = get_config_header(nexus->fd, prg_p->bus_no, - prg_p->dev_no, prg_p->func_no, - &config_hdr)) != 0) { - break; - } - - /* - * Special case for the type of Southbridge found on - * Ultra-45 and other sun4u fire workstations. - */ - if ((config_hdr.dwords[0] == U45_SB_DEVID_VID) && - (config_hdr.dwords[2] == U45_SB_CLASS_RID)) { - rval = ECANCELED; - break; - } - - /* - * Found one device with bus number, device number and - * function number. - */ - - pci_base = &pinfo->devices[pinfo->num_devices].base; - - pci_base->domain = nexus->domain; - pci_base->bus = prg_p->bus_no; - pci_base->dev = prg_p->dev_no; - pci_base->func = func; - - /* - * for the format of device_class, see struct pci_device; - */ + /* Exclude usb devices */ + if (len < 5) { + return DI_WALK_CONTINUE; + } - pci_base->device_class = - (GET_CONFIG_VAL_8(PCI_CONF_BASCLASS) << 16) | - (GET_CONFIG_VAL_8(PCI_CONF_SUBCLASS) << 8) | - GET_CONFIG_VAL_8(PCI_CONF_PROGCLASS); + pci_base = &pinfo->devices[pinfo->num_devices].base; - pci_base->revision = GET_CONFIG_VAL_8(PCI_CONF_REVID); - pci_base->vendor_id = GET_CONFIG_VAL_16(PCI_CONF_VENID); - pci_base->device_id = GET_CONFIG_VAL_16(PCI_CONF_DEVID); - pci_base->subvendor_id = GET_CONFIG_VAL_16(PCI_CONF_SUBVENID); - pci_base->subdevice_id = GET_CONFIG_VAL_16(PCI_CONF_SUBSYSID); - pci_base->irq = GET_CONFIG_VAL_8(PCI_CONF_ILINE); + 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]); - pinfo->devices[pinfo->num_devices].header_type - = GET_CONFIG_VAL_8(PCI_CONF_HEADER); + /* 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, - "nexus = %s, busno = %x, devno = %x, funcno = %x\n", - nexus->path, prg_p->bus_no, prg_p->dev_no, func); + 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 - - 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)); - return (rval); - } - (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; - } - - /* - * Accommodate devices which state their - * multi-functionality only in their function 0 config - * space. Note multi-functionality throughout probing - * of all of this device's functions. - */ - if (config_hdr.bytes[PCI_CONF_HEADER] & PCI_HEADER_MULTI) { - multi_function_device = B_TRUE; - } } } - return (rval); -} - - -/* - * Solaris version - * Probe a given nexus config space for devices. - * - * fd is the file descriptor of the nexus. - * input_args contains commandline options as specified by the user. - */ -static int -do_probe(nexus_t *nexus, probe_info_t *pinfo) -{ - pcitool_reg_t prg; - uint32_t bus; - uint8_t dev; - uint32_t last_bus = nexus->last_bus; - uint8_t last_dev = PCI_REG_DEV_M >> PCI_REG_DEV_SHIFT; - uint8_t first_bus = nexus->first_bus; - uint8_t first_dev = 0; - int rval = 0; + if ((property_list[1].value == 0) && (property_list[2].value == 0)) + return DI_WALK_CONTINUE; - prg.barnum = 0; /* Config space. */ + 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; - /* Must read in 4-byte quantities. */ - prg.acc_attr = PCITOOL_ACC_ATTR_SIZE_4 + NATIVE_ENDIAN; - - prg.data = 0; - - /* - * Loop through all valid bus / dev / func combinations to check for - * all devices, with the following exceptions: - * - * When nothing is found at function 0 of a bus / dev combination, skip - * the other functions of that bus / dev combination. - * - * When a found device's function 0 is probed and it is determined that - * it is not a multifunction device, skip probing of that device's - * other functions. - */ - for (bus = first_bus; ((bus <= last_bus) && (rval == 0)); bus++) { - prg.bus_no = (uint8_t)bus; +#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 - for (dev = first_dev; ((dev <= last_dev) && (rval == 0)); dev++) { - prg.dev_no = dev; - rval = probe_dev(nexus, &prg, pinfo); - } - - /* - * Ultra-45 southbridge workaround: - * ECANCELED tells to skip to the next bus. - */ - if (rval == ECANCELED) { - rval = 0; + 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 (rval); + return (DI_WALK_CONTINUE); } - /* * This function is called from di_walk_minor() when any PROBE is processed */ @@ -508,6 +316,7 @@ probe_nexus_node(di_node_t di_node, di_minor_t minor, void *arg) 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; @@ -518,6 +327,7 @@ probe_nexus_node(di_node_t di_node, di_minor_t minor, void *arg) #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; @@ -626,28 +436,50 @@ probe_nexus_node(di_node_t di_node, di_minor_t minor, void *arg) #endif if ((fd = open(nexus_path, O_RDWR | O_CLOEXEC)) >= 0) { + probe_args_t args; + 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); + + if ((rnode = di_init(nexus->dev_path, DINFOCPYALL)) == DI_NODE_NIL) { + (void) fprintf(stderr, "di_init failed: %s\n", strerror(errno)); + close(nexus->fd); free(nexus->path); free(nexus->dev_path); free(nexus); - } else { - nexus->next = nexus_list; - nexus_list = nexus; + return (DI_WALK_TERMINATE); + } + + /* Walk through devices under the rnode */ + args.pinfo = pinfo; + args.nexus = nexus; + args.ret = 0; + + (void) di_walk_node(rnode, DI_WALK_CLDFIRST, (void *)&args, probe_device_node); + if (args.ret) { + close(nexus->fd); + free(nexus->path); + free(nexus->dev_path); + free(nexus); + di_fini(rnode); + return (DI_WALK_TERMINATE); } + + nexus->next = nexus_list; + nexus_list = nexus; } else { (void) fprintf(stderr, "Error opening %s: %s\n", nexus_path, strerror(errno)); free(nexus); } + if (rnode != DI_NODE_NIL) { + di_fini(rnode); + } + return DI_WALK_CONTINUE; } @@ -732,6 +564,10 @@ pci_device_solx_devfs_probe( struct pci_device * dev ) if ( (nexus = find_nexus_for_bus(dev->domain, dev->bus)) == NULL ) return ENODEV; + pci_device_cfg_read_u8(dev, &priv->header_type, PCI_CONF_HEADER); + + pci_device_cfg_read_u8(dev, (uint8_t *)&dev->irq, PCI_CONF_ILINE); + /* * starting to find if it is MEM/MEM64/IO * using libdevinfo @@ -915,7 +751,7 @@ pci_device_solx_devfs_map_range(struct pci_device *dev, err = errno; (void) fprintf(stderr, "map rom region =%llx failed: %s\n", - map->base, strerror(errno)); + (unsigned long long) map->base, strerror(errno)); } #ifdef __sparc @@ -995,7 +831,7 @@ pci_device_solx_devfs_read( struct pci_device * dev, void * data, cfg_prg.bus_no, cfg_prg.dev_no, cfg_prg.func_no, - cfg_prg.offset); + (unsigned long long) cfg_prg.offset); fprintf(stderr, "Failure cause = %x\n", err); break; } -- 2.7.4