OSDN Git Service

Solaris: probe improvement
authorHenry Zhao <henry.zhao@oracle.com>
Fri, 11 Jan 2013 01:53:09 +0000 (17:53 -0800)
committerAlan Coopersmith <alan.coopersmith@oracle.com>
Sun, 10 Mar 2013 06:02:40 +0000 (22:02 -0800)
Remove pcitool dependency in probing phase. Use the data
collected from devinfo tree instead in creating pci file
system.

Signed-off-by: Henry Zhao <henzhao@glory3.(none)>
Signed-off-by: Alan Coopersmith <alan.coopersmith@oracle.com>
src/solx_devfs.c

index 108fd1f..433969f 100644 (file)
@@ -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
 #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;
        }