Solaris: Use bus-range properties to limit busses scanned on each node

Based on code provided by Dan.Mick@sun.com
diff --git a/src/solx_devfs.c b/src/solx_devfs.c
index e184841..99ee1ac 100644
--- a/src/solx_devfs.c
+++ b/src/solx_devfs.c
@@ -22,7 +22,7 @@
  * DEALINGS IN THE SOFTWARE.
  */
 /*
- * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
+ * 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
@@ -68,6 +68,8 @@
 #include "pciaccess.h"
 #include "pciaccess_private.h"
 
+/* #define DEBUG */
+
 #define	MAX_DEVICES	256
 #define	CELL_NUMS_1275	(sizeof(pci_regspec_t) / sizeof(uint_t))
 
@@ -85,12 +87,13 @@
 
 typedef struct nexus {
     int fd;
-    int domain;
+    int first_bus;
+    int last_bus;
+    const char *path;		/* for errors/debugging; fd is all we need */
     struct nexus *next;
 } nexus_t;
 
 static nexus_t *nexus_list = NULL;
-static int num_domains = 0;
 static int xsvc_fd = -1;
 
 /*
@@ -127,9 +130,6 @@
 # define U45_SB_CLASS_RID	0x06040000
 #endif
 
-#define	DEBUGON	0
-
-
 static int pci_device_solx_devfs_map_range(struct pci_device *dev,
     struct pci_device_mapping *map);
 
@@ -174,12 +174,12 @@
 };
 
 static nexus_t *
-find_nexus_for_domain( int domain )
+find_nexus_for_bus( int bus )
 {
     nexus_t *nexus;
 
     for (nexus = nexus_list ; nexus != NULL ; nexus = nexus->next) {
-	if (nexus->domain == domain) {
+	if ((bus >= nexus->first_bus) && (bus <= nexus->last_bus)) {
 	    return nexus;
 	}
     }
@@ -212,6 +212,7 @@
     for (nexus = nexus_list ; nexus != NULL ; nexus = next) {
 	next = nexus->next;
 	close(nexus->fd);
+	free(nexus->path);
 	free(nexus);
     }
     nexus_list = NULL;
@@ -443,7 +444,7 @@
 	    /*
 	     * Domain is peer bus??
 	     */
-	    pci_base->domain = nexus->domain;
+	    pci_base->domain = 0;
 	    pci_base->bus = prg_p->bus_no;
 	    pci_base->dev = prg_p->dev_no;
 	    pci_base->func = func;
@@ -466,12 +467,20 @@
 	    pci_sys->devices[pci_sys->num_devices].header_type
 					= GET_CONFIG_VAL_8(PCI_CONF_HEADER);
 
-#if DEBUGON
-	    fprintf(stderr, "busno = %x, devno = %x, funcno = %x\n",
-		    prg_p->bus_no, prg_p->dev_no, func);
+#ifdef DEBUG
+	    fprintf(stderr,
+		    "nexus = %s, busno = %x, devno = %x, funcno = %x\n",
+		    nexus->path, prg_p->bus_no, prg_p->dev_no, func);
 #endif
 
-	    pci_sys->num_devices++;
+	    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");
+	    }
+
 
 	    /*
 	     * Accommodate devices which state their
@@ -495,17 +504,67 @@
 probe_nexus_node(di_node_t di_node, di_minor_t minor, void *arg)
 {
     struct pci_system *pci_sys = (struct pci_system *) arg;
-    char *nexus_name;
+    const char *nexus_name;
     nexus_t *nexus;
     int fd;
     char nexus_path[MAXPATHLEN];
 
+    di_prop_t prop;
+    const char *strings;
+    int *ints;
+    int numval;
+    int pci_node = 0;
+    int first_bus = 0, last_bus = PCI_REG_BUS_G(PCI_REG_BUS_M);
+
+#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 || strncmp(strings, "pci", 3) != 0) {
+		/* not a PCI node, bail */
+		return (DI_WALK_CONTINUE);
+	    }
+	    pci_node = 1;
+	}
+	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 __x86  /* sparc pci nodes don't have the device_type set */
+    if (pci_node != 1)
+	return (DI_WALK_CONTINUE);
+#endif
+
+    /* 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;
+	return (DI_WALK_TERMINATE);
     }
+    nexus->first_bus = first_bus;
+    nexus->last_bus = last_bus;
 
     nexus_name = di_devfs_minor_path(minor);
     if (nexus_name == NULL) {
@@ -517,14 +576,20 @@
 
     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",
+	    nexus_path, first_bus, last_bus);
+#endif
 
     if ((fd = open(nexus_path, O_RDWR)) >= 0) {
 	nexus->fd = fd;
-	nexus->domain = num_domains++;
 	if ((do_probe(nexus, pci_sys) != 0) && (errno != ENXIO)) {
 	    (void) fprintf(stderr, "Error probing node %s: %s\n",
 			   nexus_path, strerror(errno));
 	    (void) close(fd);
+	    free(nexus->path);
 	    free(nexus);
 	} else {
 	    nexus->next = nexus_list;
@@ -533,6 +598,7 @@
     } else {
 	(void) fprintf(stderr, "Error opening %s: %s\n",
 		       nexus_path, strerror(errno));
+	free(nexus->path);
 	free(nexus);
     }
 
@@ -553,9 +619,9 @@
     pcitool_reg_t prg;
     uint32_t bus;
     uint8_t dev;
-    uint32_t last_bus = PCI_REG_BUS_M >> PCI_REG_BUS_SHIFT;
+    uint32_t last_bus = nexus->last_bus;
     uint8_t last_dev = PCI_REG_DEV_M >> PCI_REG_DEV_SHIFT;
-    uint8_t first_bus = 0;
+    uint8_t first_bus = nexus->first_bus;
     uint8_t first_dev = 0;
     int rval = 0;
 
@@ -593,9 +659,6 @@
 	    rval = 0;
 	}
     }
-    if (pci_sys->num_devices > MAX_DEVICES) {
-	(void) fprintf(stderr, "pci devices reach maximum number\n");
-    }
 
     return (rval);
 }
@@ -634,7 +697,7 @@
     len = di_prop_lookup_ints(DDI_DEV_T_ANY, node, "reg", &regbuf);
 
     if (len <= 0) {
-#if DEBUGON
+#ifdef DEBUG
 	fprintf(stderr, "error = %x\n", errno);
 	fprintf(stderr, "can not find assigned-address\n");
 #endif
@@ -808,7 +871,7 @@
 	.size = dev->rom_size,
 	.flags = 0
     };
-    
+
     err = pci_device_solx_devfs_map_range(dev, &prom);
     if (err == 0) {
 	(void) bcopy(prom.memory, buffer, dev->rom_size);
@@ -831,7 +894,7 @@
     pcitool_reg_t cfg_prg;
     int err = 0;
     int i = 0;
-    nexus_t *nexus = find_nexus_for_domain(dev->domain);
+    nexus_t *nexus = find_nexus_for_bus(dev->bus);
 
     *bytes_read = 0;
 
@@ -852,7 +915,8 @@
 	cfg_prg.offset = offset + i;
 
 	if ((err = ioctl(nexus->fd, PCITOOL_DEVICE_GET_REG, &cfg_prg)) != 0) {
-	    fprintf(stderr, "read bdf<%x,%x,%x,%llx> config space failure\n",
+	    fprintf(stderr, "read bdf<%s,%x,%x,%x,%llx> config space failure\n",
+		    nexus->path,
 		    cfg_prg.bus_no,
 		    cfg_prg.dev_no,
 		    cfg_prg.func_no,
@@ -882,7 +946,7 @@
     pcitool_reg_t cfg_prg;
     int err = 0;
     int cmd;
-    nexus_t *nexus = find_nexus_for_domain(dev->domain);
+    nexus_t *nexus = find_nexus_for_bus(dev->bus);
 
     if ( bytes_written != NULL ) {
 	*bytes_written = 0;