summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAlan Coopersmith <alan.coopersmith@sun.com>2008-10-16 16:55:27 -0700
committerAlan Coopersmith <alan.coopersmith@sun.com>2008-10-16 16:55:27 -0700
commitde97e7e4c63146032c0badb9e0f0b1899dc8debf (patch)
tree599c0afd6032a0ab1db3c3bdec5af89027308785
parent110cdac97ca1bca3ec811ce0a71b2b24c1f80525 (diff)
Fix bus probing on Solaris/SPARC
-rw-r--r--src/solx_devfs.c258
1 files changed, 187 insertions, 71 deletions
diff --git a/src/solx_devfs.c b/src/solx_devfs.c
index e82e5b7..9321bd2 100644
--- a/src/solx_devfs.c
+++ b/src/solx_devfs.c
@@ -53,8 +53,6 @@
* Solaris devfs interfaces
*/
-#define _GNU_SOURCE
-
#include <stdlib.h>
#include <strings.h>
#include <stdio.h>
@@ -73,7 +71,6 @@
#include "pciaccess.h"
#include "pciaccess_private.h"
-#define PCI_NEXUS_1 "/devices/pci@0,0:reg"
#define MAX_DEVICES 256
#define CELL_NUMS_1275 (sizeof(pci_regspec_t)/sizeof(uint_t))
typedef union {
@@ -86,9 +83,16 @@ typedef struct i_devnode {
uint8_t dev;
uint8_t func;
di_node_t node;
-}i_devnode_t;
+} i_devnode_t;
+
+typedef struct nexus {
+ int fd;
+ int domain;
+ struct nexus *next;
+} nexus_t;
-static int root_fd = -1;
+static nexus_t *nexus_list = NULL;
+static int num_domains = 0;
static int xsvc_fd = -1;
/*
* Read config space in native processor endianness. Endian-neutral
@@ -143,10 +147,13 @@ static int pci_device_solx_devfs_write( struct pci_device * dev,
pciaddr_t * bytes_written );
static int
-probe_dev(int fd, pcitool_reg_t *prg_p, struct pci_system *pci_sys);
+probe_dev(nexus_t *nexus, pcitool_reg_t *prg_p, struct pci_system *pci_sys);
static int
-do_probe(int fd, struct pci_system *pci_sys);
+do_probe(nexus_t *nexus, struct pci_system *pci_sys);
+
+static int
+probe_nexus_node(di_node_t di_node, di_minor_t minor, void *arg);
static void
pci_system_solx_devfs_destroy( void );
@@ -175,6 +182,39 @@ static const struct pci_system_methods solx_devfs_methods = {
.fill_capabilities = pci_fill_capabilities_generic
};
+static nexus_t *
+find_nexus_for_domain( int domain )
+{
+ nexus_t *nexus;
+
+ for (nexus = nexus_list ; nexus != NULL ; nexus = nexus->next) {
+ if (nexus->domain == domain) {
+ return nexus;
+ }
+ }
+ return NULL;
+}
+
+static uint32_t
+get_config_hdr_value(pci_conf_hdr_t *config_hdr_p, uint16_t offset,
+ uint8_t size)
+{
+ uint32_t value = 0;
+
+ while (size-- > 0) {
+ value = (value << 8) + config_hdr_p->bytes[offset + size];
+ }
+
+ return value;
+}
+
+#define GET_CONFIG_VAL_8(offset) \
+ (config_hdr.bytes[offset])
+#define GET_CONFIG_VAL_16(offset) \
+ (uint16_t)get_config_hdr_value(&config_hdr, offset, 2)
+#define GET_CONFIG_VAL_32(offset) \
+ (uint32_t)get_config_hdr_value(&config_hdr, offset, 4)
+
/*
* Release all the resources
* Solaris version
@@ -187,17 +227,21 @@ pci_system_solx_devfs_destroy( void )
* will be freed in pci_system_init
* It is more reasonable to free them here
*/
- if (root_fd >= 0) {
- close(root_fd);
- root_fd = -1;
+ nexus_t *nexus, *next;
+ for (nexus = nexus_list ; nexus != NULL ; nexus = next) {
+ next = nexus->next;
+ close(nexus->fd);
+ free(nexus);
}
-
+ nexus_list = NULL;
+
if (xsvc_fd >= 0) {
close(xsvc_fd);
xsvc_fd = -1;
}
}
+
/*
* Attempt to access PCI subsystem using Solaris's devfs interface.
* Solaris version
@@ -206,45 +250,50 @@ int
pci_system_solx_devfs_create( void )
{
int err = 0;
+ di_node_t di_node;
- if (root_fd >= 0)
- return (err);
- /* If the PCI nexus device "/devices/pci@0,0:reg" exists,
- * then the PCI subsystem can be accessed using
- * this interface.
- */
- if ((root_fd = open(PCI_NEXUS_1, O_RDWR)) == -1) {
- (void) fprintf(stderr,
- "Could not open nexus node %s: %s\n",
- PCI_NEXUS_1, strerror(errno));
-
- err = errno;
+ if (nexus_list != NULL) {
+ return 0;
+ }
- return (err);
- } else {
- /*
- * 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) {
- (void) do_probe(root_fd, pci_sys);
- }
- else {
+ /*
+ * 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;
- free(pci_sys);
- pci_sys = NULL;
+ (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 {
+ }
+ else {
err = errno;
}
-
+ } else {
+ err = errno;
}
+
+ if (err != 0) {
+ if (pci_sys != NULL) {
+ free(pci_sys->devices);
+ free(pci_sys);
+ pci_sys = NULL;
+ }
+ }
return (err);
}
@@ -288,7 +337,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(int fd, pcitool_reg_t *prg_p, struct pci_system *pci_sys)
+probe_dev(nexus_t *nexus, pcitool_reg_t *prg_p, struct pci_system *pci_sys)
{
pci_conf_hdr_t config_hdr;
boolean_t multi_function_device;
@@ -296,7 +345,7 @@ probe_dev(int fd, pcitool_reg_t *prg_p, struct pci_system *pci_sys)
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
@@ -334,7 +383,7 @@ probe_dev(int fd, pcitool_reg_t *prg_p, struct pci_system *pci_sys)
prg_p->offset = 0;
prg_p->data = 0;
prg_p->user_version = PCITOOL_USER_VERSION;
- if (((rval = ioctl(fd, PCITOOL_DEVICE_GET_REG, prg_p)) != 0) ||
+ if (((rval = ioctl(nexus->fd, PCITOOL_DEVICE_GET_REG, prg_p)) != 0) ||
(prg_p->data == 0xffffffff)) {
/*
@@ -389,7 +438,7 @@ probe_dev(int fd, pcitool_reg_t *prg_p, struct pci_system *pci_sys)
config_hdr.dwords[0] = (uint32_t)prg_p->data;
/* Get the rest of the PCI header. */
- if ((rval = get_config_header(fd, prg_p->bus_no,
+ if ((rval = get_config_header(nexus->fd, prg_p->bus_no,
prg_p->dev_no, prg_p->func_no, &config_hdr)) !=
0) {
break;
@@ -410,32 +459,36 @@ probe_dev(int fd, pcitool_reg_t *prg_p, struct pci_system *pci_sys)
* function number.
*/
+ pci_base =
+ &pci_sys->devices[pci_sys->num_devices].base;
+
/*
* Domain is peer bus??
*/
- pci_sys->devices[pci_sys->num_devices].base.domain = 0;
- pci_sys->devices[pci_sys->num_devices].base.bus =
- prg_p->bus_no;
- pci_sys->devices[pci_sys->num_devices].base.dev =
- prg_p->dev_no;
- pci_sys->devices[pci_sys->num_devices].base.func = func;
+ 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;
*/
- pci_sys->devices[pci_sys->num_devices].base.device_class =
- config_hdr.dwords[2]>>8;
- pci_sys->devices[pci_sys->num_devices].base.revision =
- (uint8_t)(config_hdr.dwords[2] & 0xff);
- pci_sys->devices[pci_sys->num_devices].base.vendor_id =
- (uint16_t)(config_hdr.dwords[0] & 0xffff);
- pci_sys->devices[pci_sys->num_devices].base.device_id =
- (uint16_t)((config_hdr.dwords[0]>>16) & 0xffff);
- pci_sys->devices[pci_sys->num_devices].base.subvendor_id =
- (uint16_t)(config_hdr.dwords[11] & 0xffff);
- pci_sys->devices[pci_sys->num_devices].base.subdevice_id =
- (uint16_t)((config_hdr.dwords[11]>>16) & 0xffff);
+ 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->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_sys->devices[pci_sys->num_devices].header_type =
- (uint8_t)(((config_hdr.dwords[3])&0xff0000)>>16);
+ 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);
@@ -459,6 +512,59 @@ probe_dev(int fd, pcitool_reg_t *prg_p, struct pci_system *pci_sys)
return (rval);
}
+/*
+ * 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)
+{
+ struct pci_system *pci_sys = (struct pci_system *) arg;
+ char *nexus_name;
+ nexus_t *nexus;
+ int fd;
+ char nexus_path[MAXPATHLEN];
+
+ 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_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);
+
+ 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);
+ } else {
+ nexus->next = nexus_list;
+ nexus_list = nexus;
+ }
+ } else {
+ (void) fprintf(stderr, "Error opening %s: %s\n",
+ nexus_path, strerror(errno));
+ free(nexus);
+ }
+ di_devfs_path_free(nexus_name);
+
+ return DI_WALK_CONTINUE;
+}
+
+
/*
* Solaris version
* Probe a given nexus config space for devices.
@@ -467,7 +573,7 @@ probe_dev(int fd, pcitool_reg_t *prg_p, struct pci_system *pci_sys)
* input_args contains commandline options as specified by the user.
*/
static int
-do_probe(int fd, struct pci_system *pci_sys)
+do_probe(nexus_t *nexus, struct pci_system *pci_sys)
{
pcitool_reg_t prg;
uint32_t bus;
@@ -501,7 +607,7 @@ do_probe(int fd, struct pci_system *pci_sys)
for (dev = first_dev;
((dev <= last_dev) && (rval == 0)); dev++) {
prg.dev_no = dev;
- rval = probe_dev(fd, &prg, pci_sys);
+ rval = probe_dev(nexus, &prg, pci_sys);
}
/*
@@ -765,6 +871,13 @@ 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_domain(dev->domain);
+
+ *bytes_read = 0;
+
+ if ( nexus == NULL ) {
+ return ENODEV;
+ }
cfg_prg.offset = offset;
cfg_prg.acc_attr = PCITOOL_ACC_ATTR_SIZE_1 + NATIVE_ENDIAN;
@@ -773,12 +886,11 @@ pci_device_solx_devfs_read( struct pci_device * dev, void * data,
cfg_prg.func_no = dev->func;
cfg_prg.barnum = 0;
cfg_prg.user_version = PCITOOL_USER_VERSION;
- *bytes_read = 0;
for (i = 0; i < size; i = i + PCITOOL_ACC_ATTR_SIZE(PCITOOL_ACC_ATTR_SIZE_1)) {
cfg_prg.offset = offset + i;
- if ((err = ioctl(root_fd, PCITOOL_DEVICE_GET_REG,
+ if ((err = ioctl(nexus->fd, PCITOOL_DEVICE_GET_REG,
&cfg_prg)) != 0) {
fprintf(stderr, "read bdf<%x,%x,%x,%llx> config space failure\n",
cfg_prg.bus_no,
@@ -810,12 +922,16 @@ 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_domain(dev->domain);
if ( bytes_written != NULL ) {
*bytes_written = 0;
}
+ if ( nexus == NULL ) {
+ return ENODEV;
+ }
+
cfg_prg.offset = offset;
switch (size) {
case 1:
@@ -849,7 +965,7 @@ pci_device_solx_devfs_write( struct pci_device * dev, const void * data,
*/
cmd = PCITOOL_DEVICE_SET_REG;
- if ((err = ioctl(root_fd, cmd, &cfg_prg)) != 0) {
+ if ((err = ioctl(nexus->fd, cmd, &cfg_prg)) != 0) {
return (err);
}
*bytes_written = size;