OSDN Git Service

linux: Fix a crash in populate_devices
[android-x86/external-libpciaccess.git] / src / solx_devfs.c
index 99ee1ac..d47a846 100644 (file)
@@ -1,5 +1,6 @@
 /*
  * (C) Copyright IBM Corporation 2006
+ * Copyright (c) 2007, 2009, 2011, Oracle and/or its affiliates.
  * All Rights Reserved.
  *
  * Permission is hereby granted, free of charge, to any person obtaining a
  * DEALINGS IN THE SOFTWARE.
  */
 /*
- * Copyright 2007, 2009 Sun Microsystems, Inc.  All rights reserved.
- *
- * Permission is hereby granted, free of charge, to any person obtaining a
- * copy of this software and associated documentation files (the
- * "Software"), to deal in the Software without restriction, including
- * without limitation the rights to use, copy, modify, merge, publish,
- * distribute, and/or sell copies of the Software, and to permit persons
- * to whom the Software is furnished to do so, provided that the above
- * copyright notice(s) and this permission notice appear in all copies of
- * the Software and that both the above copyright notice(s) and this
- * permission notice appear in supporting documentation.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
- * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
- * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
- * OF THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
- * HOLDERS INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL
- * INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING
- * FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
- * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
- * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- *
- * Except as contained in this notice, the name of a copyright holder
- * shall not be used in advertising or otherwise to promote the sale, use
- * or other dealings in this Software without prior written authorization
- * of the copyright holder.
- */
-/*
  * Solaris devfs interfaces
  */
 
@@ -70,7 +43,7 @@
 
 /* #define DEBUG */
 
-#define        MAX_DEVICES     256
+#define        INITIAL_NUM_DEVICES     256
 #define        CELL_NUMS_1275  (sizeof(pci_regspec_t) / sizeof(uint_t))
 
 typedef union {
@@ -89,10 +62,18 @@ typedef struct nexus {
     int fd;
     int first_bus;
     int last_bus;
-    const char *path;          /* for errors/debugging; fd is all we need */
+    int domain;
+    char *path;                        /* for errors/debugging; fd is all we need */
+    char *dev_path;
     struct nexus *next;
 } nexus_t;
 
+typedef struct probe_info {
+    volatile size_t num_allocated_elems;
+    volatile size_t num_devices;
+    struct pci_device_private * volatile devices;
+} probe_info_t;
+
 static nexus_t *nexus_list = NULL;
 static int xsvc_fd = -1;
 
@@ -145,10 +126,9 @@ static int pci_device_solx_devfs_write( struct pci_device * dev,
     const void * data, pciaddr_t offset, pciaddr_t size,
     pciaddr_t * bytes_written );
 
-static int probe_dev(nexus_t *nexus, pcitool_reg_t *prg_p,
-                    struct pci_system *pci_sys);
+static int probe_dev(nexus_t *nexus, pcitool_reg_t *prg_p, probe_info_t *pinfo);
 
-static int do_probe(nexus_t *nexus, struct pci_system *pci_sys);
+static int do_probe(nexus_t *nexus, probe_info_t *pinfo);
 
 static int probe_nexus_node(di_node_t di_node, di_minor_t minor, void *arg);
 
@@ -174,12 +154,13 @@ static const struct pci_system_methods solx_devfs_methods = {
 };
 
 static nexus_t *
-find_nexus_for_bus( int bus )
+find_nexus_for_bus( int domain, int bus )
 {
     nexus_t *nexus;
 
     for (nexus = nexus_list ; nexus != NULL ; nexus = nexus->next) {
-       if ((bus >= nexus->first_bus) && (bus <= nexus->last_bus)) {
+       if ((domain == nexus->domain) &&
+           (bus >= nexus->first_bus) && (bus <= nexus->last_bus)) {
            return nexus;
        }
     }
@@ -213,6 +194,7 @@ pci_system_solx_devfs_destroy( void )
        next = nexus->next;
        close(nexus->fd);
        free(nexus->path);
+       free(nexus->dev_path);
        free(nexus);
     }
     nexus_list = NULL;
@@ -232,48 +214,41 @@ pci_system_solx_devfs_create( void )
 {
     int err = 0;
     di_node_t di_node;
-
+    probe_info_t pinfo;
+    struct pci_device_private *devices;
 
     if (nexus_list != NULL) {
        return 0;
     }
 
-    /*
-     * Only allow MAX_DEVICES exists
-     * I will fix it later to get
-     * the total devices first
-     */
-    if ((pci_sys = calloc(1, sizeof (struct pci_system))) != NULL) {
-       pci_sys->methods = &solx_devfs_methods;
-
-       if ((pci_sys->devices =
-            calloc(MAX_DEVICES, sizeof (struct pci_device_private)))
-           != NULL) {
-
-           if ((di_node = di_init("/", DINFOCPYALL)) == DI_NODE_NIL) {
-               err = errno;
-               (void) fprintf(stderr, "di_init() failed: %s\n",
-                              strerror(errno));
-           } else {
-               (void) di_walk_minor(di_node, DDI_NT_REGACC, 0, pci_sys,
-                                    probe_nexus_node);
-               di_fini(di_node);
-           }
-       }
-       else {
-           err = errno;
-       }
-    } else {
+    if ((di_node = di_init("/", DINFOCPYALL)) == DI_NODE_NIL) {
        err = errno;
+       (void) fprintf(stderr, "di_init() failed: %s\n",
+                      strerror(errno));
+       return (err);
     }
 
-    if (err != 0) {
-       if (pci_sys != NULL) {
-           free(pci_sys->devices);
-           free(pci_sys);
-           pci_sys = NULL;
-       }
+    if ((devices = calloc(INITIAL_NUM_DEVICES,
+                       sizeof (struct pci_device_private))) == NULL) {
+       err = errno;
+       di_fini(di_node);
+       return (err);
+    }
+
+    pinfo.num_allocated_elems = INITIAL_NUM_DEVICES;
+    pinfo.num_devices = 0;
+    pinfo.devices = devices;
+    (void) di_walk_minor(di_node, DDI_NT_REGACC, 0, &pinfo, probe_nexus_node);
+    di_fini(di_node);
+
+    if ((pci_sys = calloc(1, sizeof (struct pci_system))) == NULL) {
+       err = errno;
+       free(devices);
+       return (err);
     }
+    pci_sys->methods = &solx_devfs_methods;
+    pci_sys->devices = pinfo.devices;
+    pci_sys->num_devices = pinfo.num_devices;
 
     return (err);
 }
@@ -316,7 +291,7 @@ get_config_header(int fd, uint8_t bus_no, uint8_t dev_no, uint8_t func_no,
  * Probe device's functions.  Modifies many fields in the prg_p.
  */
 static int
-probe_dev(nexus_t *nexus, pcitool_reg_t *prg_p, struct pci_system *pci_sys)
+probe_dev(nexus_t *nexus, pcitool_reg_t *prg_p, probe_info_t *pinfo)
 {
     pci_conf_hdr_t     config_hdr;
     boolean_t          multi_function_device;
@@ -439,12 +414,9 @@ probe_dev(nexus_t *nexus, pcitool_reg_t *prg_p, struct pci_system *pci_sys)
             * function number.
             */
 
-           pci_base = &pci_sys->devices[pci_sys->num_devices].base;
+           pci_base = &pinfo->devices[pinfo->num_devices].base;
 
-           /*
-            * Domain is peer bus??
-            */
-           pci_base->domain = 0;
+           pci_base->domain = nexus->domain;
            pci_base->bus = prg_p->bus_no;
            pci_base->dev = prg_p->dev_no;
            pci_base->func = func;
@@ -464,7 +436,7 @@ probe_dev(nexus_t *nexus, pcitool_reg_t *prg_p, struct pci_system *pci_sys)
            pci_base->subvendor_id      = GET_CONFIG_VAL_16(PCI_CONF_SUBVENID);
            pci_base->subdevice_id      = GET_CONFIG_VAL_16(PCI_CONF_SUBSYSID);
 
-           pci_sys->devices[pci_sys->num_devices].header_type
+           pinfo->devices[pinfo->num_devices].header_type
                                        = GET_CONFIG_VAL_8(PCI_CONF_HEADER);
 
 #ifdef DEBUG
@@ -473,15 +445,26 @@ probe_dev(nexus_t *nexus, pcitool_reg_t *prg_p, struct pci_system *pci_sys)
                    nexus->path, prg_p->bus_no, prg_p->dev_no, func);
 #endif
 
-           if (pci_sys->num_devices < (MAX_DEVICES - 1)) {
-               pci_sys->num_devices++;
-           } else {
-               (void) fprintf(stderr,
-                              "Maximum number of PCI devices found,"
-                              " discarding additional devices\n");
+           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,
+                                  "Maximum number of PCI devices found,"
+                                  " discarding additional devices\n");
+                   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
@@ -503,18 +486,19 @@ probe_dev(nexus_t *nexus, pcitool_reg_t *prg_p, struct pci_system *pci_sys)
 static int
 probe_nexus_node(di_node_t di_node, di_minor_t minor, void *arg)
 {
-    struct pci_system *pci_sys = (struct pci_system *) arg;
-    const char *nexus_name;
+    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;
-    const char *strings;
+    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 DEBUG
     nexus_name = di_devfs_minor_path(minor);
@@ -549,6 +533,12 @@ probe_nexus_node(di_node_t di_node, di_minor_t minor, void *arg)
                last_bus = ints[1];
            }
        }
+       else if (strcmp(prop_name, "pciseg") == 0) {
+           numval = di_prop_ints(prop, &ints);
+           if (numval == 1) {
+               domain = ints[0];
+           }
+       }
     }
 
 #ifdef __x86  /* sparc pci nodes don't have the device_type set */
@@ -565,6 +555,7 @@ probe_nexus_node(di_node_t di_node, di_minor_t minor, void *arg)
     }
     nexus->first_bus = first_bus;
     nexus->last_bus = last_bus;
+    nexus->domain = domain;
 
     nexus_name = di_devfs_minor_path(minor);
     if (nexus_name == NULL) {
@@ -576,7 +567,6 @@ probe_nexus_node(di_node_t di_node, di_minor_t minor, void *arg)
 
     snprintf(nexus_path, sizeof(nexus_path), "/devices%s", nexus_name);
     di_devfs_path_free(nexus_name);
-    nexus->path = strdup(nexus_path);
 
 #ifdef DEBUG
     fprintf(stderr, "nexus = %s, bus-range = %d - %d\n",
@@ -585,11 +575,16 @@ probe_nexus_node(di_node_t di_node, di_minor_t minor, void *arg)
 
     if ((fd = open(nexus_path, O_RDWR)) >= 0) {
        nexus->fd = fd;
-       if ((do_probe(nexus, pci_sys) != 0) && (errno != ENXIO)) {
+       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;
@@ -598,7 +593,6 @@ probe_nexus_node(di_node_t di_node, di_minor_t minor, void *arg)
     } else {
        (void) fprintf(stderr, "Error opening %s: %s\n",
                       nexus_path, strerror(errno));
-       free(nexus->path);
        free(nexus);
     }
 
@@ -614,7 +608,7 @@ probe_nexus_node(di_node_t di_node, di_minor_t minor, void *arg)
  * input_args contains commandline options as specified by the user.
  */
 static int
-do_probe(nexus_t *nexus, struct pci_system *pci_sys)
+do_probe(nexus_t *nexus, probe_info_t *pinfo)
 {
     pcitool_reg_t prg;
     uint32_t bus;
@@ -648,7 +642,7 @@ do_probe(nexus_t *nexus, struct pci_system *pci_sys)
 
        for (dev = first_dev; ((dev <= last_dev) && (rval == 0)); dev++) {
            prg.dev_no = dev;
-           rval = probe_dev(nexus, &prg, pci_sys);
+           rval = probe_dev(nexus, &prg, pinfo);
        }
 
        /*
@@ -669,18 +663,17 @@ find_target_node(di_node_t node, void *arg)
     int *regbuf = NULL;
     int len = 0;
     uint32_t busno, funcno, devno;
-    i_devnode_t *devnode;
-    void *prop = DI_PROP_NIL;
-    int i;
-
-    devnode = (i_devnode_t *)arg;
+    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")) {
@@ -727,20 +720,24 @@ pci_device_solx_devfs_probe( struct pci_device * dev )
 {
     uint8_t  config[256];
     int err;
-    di_node_t rnode;
-    i_devnode_t args;
+    di_node_t rnode = DI_NODE_NIL;
+    i_devnode_t args = { 0, 0, 0, DI_NODE_NIL };
     int *regbuf;
     pci_regspec_t *reg;
     int i;
     pciaddr_t bytes;
     int len = 0;
+    uint ent = 0;
 
     err = pci_device_solx_devfs_read( dev, config, 0, 256, & bytes );
-    args.node = DI_NODE_NIL;
 
     if ( bytes >= 64 ) {
        struct pci_device_private *priv =
            (struct pci_device_private *) dev;
+    nexus_t *nexus;
+
+    if ( (nexus = find_nexus_for_bus(dev->domain, dev->bus)) == NULL )
+       return ENODEV;
 
        dev->vendor_id = (uint16_t)config[0] + ((uint16_t)config[1] << 8);
        dev->device_id = (uint16_t)config[2] + ((uint16_t)config[3] << 8);
@@ -762,7 +759,7 @@ pci_device_solx_devfs_probe( struct pci_device * dev )
         * starting to find if it is MEM/MEM64/IO
         * using libdevinfo
         */
-       if ((rnode = di_init("/", DINFOCPYALL)) == DI_NODE_NIL) {
+       if ((rnode = di_init(nexus->dev_path, DINFOCPYALL)) == DI_NODE_NIL) {
            err = errno;
            (void) fprintf(stderr, "di_init failed: %s\n", strerror(errno));
        } else {
@@ -771,7 +768,6 @@ pci_device_solx_devfs_probe( struct pci_device * dev )
            args.func = dev->func;
            (void) di_walk_node(rnode, DI_WALK_CLDFIRST,
                                (void *)&args, find_target_node);
-           di_fini(rnode);
        }
     }
     if (args.node != DI_NODE_NIL) {
@@ -786,7 +782,7 @@ pci_device_solx_devfs_probe( struct pci_device * dev )
     }
 
     if (len <= 0)
-       return (err);
+       goto cleanup;
 
 
     /*
@@ -815,13 +811,21 @@ pci_device_solx_devfs_probe( struct pci_device * dev )
     }
 
     /*
-     * solaris has its own BAR index. To be sure that
-     * Xorg has the same BAR number as solaris. ????
+     * Solaris has its own BAR index.
+     * Linux give two region slot for 64 bit address.
      */
     for (i = 0; i < len; i = i + CELL_NUMS_1275) {
-       int ent = i/CELL_NUMS_1275;
 
        reg = (pci_regspec_t *)&regbuf[i];
+       ent = reg->pci_phys_hi & 0xff;
+       /*
+        * G35 broken in BAR0
+        */
+       ent = (ent - PCI_CONF_BASE0) >> 2;
+       if (ent >= 6) {
+           fprintf(stderr, "error ent = %d\n", ent);
+           break;
+       }
 
        /*
         * non relocatable resource is excluded
@@ -835,6 +839,16 @@ pci_device_solx_devfs_probe( struct pci_device * dev )
            dev->regions[ent].is_prefetchable = 1;
        }
 
+
+       /*
+        * We split the shift count 32 into two 16 to
+        * avoid the complaining of the compiler
+        */
+       dev->regions[ent].base_addr = reg->pci_phys_low +
+           ((reg->pci_phys_mid << 16) << 16);
+       dev->regions[ent].size = reg->pci_size_low +
+           ((reg->pci_size_hi << 16) << 16);
+
        switch (reg->pci_phys_hi & PCI_REG_ADDR_M) {
            case PCI_ADDR_IO:
                dev->regions[ent].is_IO = 1;
@@ -843,19 +857,17 @@ pci_device_solx_devfs_probe( struct pci_device * dev )
                break;
            case PCI_ADDR_MEM64:
                dev->regions[ent].is_64 = 1;
+               /*
+                * Skip one slot for 64 bit address
+                */
                break;
        }
-
-       /*
-        * We split the shift count 32 into two 16 to
-        * avoid the complaining of the compiler
-        */
-       dev->regions[ent].base_addr = reg->pci_phys_low +
-           ((reg->pci_phys_mid << 16) << 16);
-       dev->regions[ent].size = reg->pci_size_low +
-           ((reg->pci_size_hi << 16) << 16);
     }
 
+  cleanup:
+    if (rnode != DI_NODE_NIL) {
+       di_fini(rnode);
+    }
     return (err);
 }
 
@@ -894,7 +906,7 @@ pci_device_solx_devfs_read( struct pci_device * dev, void * data,
     pcitool_reg_t cfg_prg;
     int err = 0;
     int i = 0;
-    nexus_t *nexus = find_nexus_for_bus(dev->bus);
+    nexus_t *nexus = find_nexus_for_bus(dev->domain, dev->bus);
 
     *bytes_read = 0;
 
@@ -946,7 +958,7 @@ pci_device_solx_devfs_write( struct pci_device * dev, const void * data,
     pcitool_reg_t cfg_prg;
     int err = 0;
     int cmd;
-    nexus_t *nexus = find_nexus_for_bus(dev->bus);
+    nexus_t *nexus = find_nexus_for_bus(dev->domain, dev->bus);
 
     if ( bytes_written != NULL ) {
        *bytes_written = 0;