solx_devfs.c revision 49310723
14f5e7dd7Smrg/*
24f5e7dd7Smrg * (C) Copyright IBM Corporation 2006
349310723Smrg * Copyright (c) 2007, 2009, 2011, 2012, 2016 Oracle and/or its affiliates.
44f5e7dd7Smrg * All Rights Reserved.
54f5e7dd7Smrg *
64f5e7dd7Smrg * Permission is hereby granted, free of charge, to any person obtaining a
74f5e7dd7Smrg * copy of this software and associated documentation files (the "Software"),
84f5e7dd7Smrg * to deal in the Software without restriction, including without limitation
94f5e7dd7Smrg * on the rights to use, copy, modify, merge, publish, distribute, sub
104f5e7dd7Smrg * license, and/or sell copies of the Software, and to permit persons to whom
114f5e7dd7Smrg * the Software is furnished to do so, subject to the following conditions:
124f5e7dd7Smrg *
134f5e7dd7Smrg * The above copyright notice and this permission notice (including the next
144f5e7dd7Smrg * paragraph) shall be included in all copies or substantial portions of the
154f5e7dd7Smrg * Software.
164f5e7dd7Smrg *
174f5e7dd7Smrg * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
184f5e7dd7Smrg * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
194f5e7dd7Smrg * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT.  IN NO EVENT SHALL
204f5e7dd7Smrg * IBM AND/OR THEIR SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
214f5e7dd7Smrg * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
224f5e7dd7Smrg * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
234f5e7dd7Smrg * DEALINGS IN THE SOFTWARE.
244f5e7dd7Smrg */
254f5e7dd7Smrg/*
264f5e7dd7Smrg * Solaris devfs interfaces
274f5e7dd7Smrg */
2849310723Smrg#ifdef HAVE_CONFIG_H
2949310723Smrg#include "config.h"
3049310723Smrg#endif
314f5e7dd7Smrg
324f5e7dd7Smrg#include <stdlib.h>
334f5e7dd7Smrg#include <strings.h>
344f5e7dd7Smrg#include <stdio.h>
354f5e7dd7Smrg#include <unistd.h>
364f5e7dd7Smrg#include <sys/types.h>
374f5e7dd7Smrg#include <fcntl.h>
384f5e7dd7Smrg#include <sys/mman.h>
394f5e7dd7Smrg#include <errno.h>
404f5e7dd7Smrg#include <sys/pci.h>
414f5e7dd7Smrg#include <libdevinfo.h>
424f5e7dd7Smrg#include "pci_tools.h"
434f5e7dd7Smrg
4486ea1d58Smrg#ifdef __x86
4586ea1d58Smrg# include <sys/sysi86.h>
4686ea1d58Smrg# include <sys/psw.h>
4786ea1d58Smrg#endif
4886ea1d58Smrg
494f5e7dd7Smrg#include "pciaccess.h"
504f5e7dd7Smrg#include "pciaccess_private.h"
514f5e7dd7Smrg
524f5e7dd7Smrg/* #define DEBUG */
534f5e7dd7Smrg
54cad31331Smrg#define	INITIAL_NUM_DEVICES	256
554f5e7dd7Smrg#define	CELL_NUMS_1275	(sizeof(pci_regspec_t) / sizeof(uint_t))
564f5e7dd7Smrg
574f5e7dd7Smrgtypedef struct i_devnode {
584f5e7dd7Smrg    uint8_t bus;
594f5e7dd7Smrg    uint8_t dev;
604f5e7dd7Smrg    uint8_t func;
614f5e7dd7Smrg    di_node_t node;
624f5e7dd7Smrg} i_devnode_t;
634f5e7dd7Smrg
644f5e7dd7Smrgtypedef struct nexus {
654f5e7dd7Smrg    int first_bus;
664f5e7dd7Smrg    int last_bus;
67cad31331Smrg    int domain;
6849310723Smrg    char *path;			/* for open */
69cad31331Smrg    char *dev_path;
704f5e7dd7Smrg    struct nexus *next;
714f5e7dd7Smrg} nexus_t;
724f5e7dd7Smrg
73cad31331Smrgtypedef struct probe_info {
74cad31331Smrg    volatile size_t num_allocated_elems;
75cad31331Smrg    volatile size_t num_devices;
76cad31331Smrg    struct pci_device_private * volatile devices;
77cad31331Smrg} probe_info_t;
78cad31331Smrg
7986ea1d58Smrgtypedef struct probe_args {
8086ea1d58Smrg    probe_info_t *pinfo;
8186ea1d58Smrg    nexus_t *nexus;
8286ea1d58Smrg    int ret;
8386ea1d58Smrg} probe_args_t;
8486ea1d58Smrg
8586ea1d58Smrgtypedef struct property_info {
8686ea1d58Smrg    const char *name;
8786ea1d58Smrg    int value;
8886ea1d58Smrg} property_info_t;
8986ea1d58Smrg
904f5e7dd7Smrgstatic nexus_t *nexus_list = NULL;
91cad31331Smrg#if !defined(__sparc)
924f5e7dd7Smrgstatic int xsvc_fd = -1;
93cad31331Smrg#endif
94cad31331Smrg
95cad31331Smrg#ifdef __sparc
96cad31331Smrgstatic di_prom_handle_t di_phdl;
9786ea1d58Smrgstatic size_t  nexus_count = 0;
98cad31331Smrg#endif
994f5e7dd7Smrg
1004f5e7dd7Smrg/*
1014f5e7dd7Smrg * Read config space in native processor endianness.  Endian-neutral
1024f5e7dd7Smrg * processing can then take place.  On big endian machines, MSB and LSB
1034f5e7dd7Smrg * of little endian data end up switched if read as little endian.
1044f5e7dd7Smrg * They are in correct order if read as big endian.
1054f5e7dd7Smrg */
1064f5e7dd7Smrg#if defined(__sparc)
1074f5e7dd7Smrg# define NATIVE_ENDIAN	PCITOOL_ACC_ATTR_ENDN_BIG
1084f5e7dd7Smrg#elif defined(__x86)
1094f5e7dd7Smrg# define NATIVE_ENDIAN	PCITOOL_ACC_ATTR_ENDN_LTL
1104f5e7dd7Smrg#else
1114f5e7dd7Smrg# error "ISA is neither __sparc nor __x86"
1124f5e7dd7Smrg#endif
1134f5e7dd7Smrg
114cad31331Smrg#ifdef __sparc
115cad31331Smrg#define MAPPING_DEV_PATH(dev)	 (((struct pci_device_private *) dev)->device_string)
116cad31331Smrg#endif
117cad31331Smrg
1184f5e7dd7Smrgstatic nexus_t *
119cad31331Smrgfind_nexus_for_bus( int domain, int bus )
1204f5e7dd7Smrg{
1214f5e7dd7Smrg    nexus_t *nexus;
1224f5e7dd7Smrg
1234f5e7dd7Smrg    for (nexus = nexus_list ; nexus != NULL ; nexus = nexus->next) {
124cad31331Smrg	if ((domain == nexus->domain) &&
125cad31331Smrg	    (bus >= nexus->first_bus) && (bus <= nexus->last_bus)) {
1264f5e7dd7Smrg	    return nexus;
1274f5e7dd7Smrg	}
1284f5e7dd7Smrg    }
1294f5e7dd7Smrg    return NULL;
1304f5e7dd7Smrg}
1314f5e7dd7Smrg
1324f5e7dd7Smrg/*
1334f5e7dd7Smrg * Release all the resources
1344f5e7dd7Smrg * Solaris version
1354f5e7dd7Smrg */
1364f5e7dd7Smrgstatic void
1374f5e7dd7Smrgpci_system_solx_devfs_destroy( void )
1384f5e7dd7Smrg{
1394f5e7dd7Smrg    /*
1404f5e7dd7Smrg     * The memory allocated for pci_sys & devices in create routines
1414f5e7dd7Smrg     * will be freed in pci_system_cleanup.
1424f5e7dd7Smrg     * Need to free system-specific allocations here.
1434f5e7dd7Smrg     */
1444f5e7dd7Smrg    nexus_t *nexus, *next;
1454f5e7dd7Smrg
1464f5e7dd7Smrg    for (nexus = nexus_list ; nexus != NULL ; nexus = next) {
1474f5e7dd7Smrg	next = nexus->next;
1484f5e7dd7Smrg	free(nexus->path);
149cad31331Smrg	free(nexus->dev_path);
1504f5e7dd7Smrg	free(nexus);
1514f5e7dd7Smrg    }
1524f5e7dd7Smrg    nexus_list = NULL;
1534f5e7dd7Smrg
154cad31331Smrg#ifdef __sparc
155cad31331Smrg    if (di_phdl != DI_PROM_HANDLE_NIL)
156cad31331Smrg	(void) di_prom_fini(di_phdl);
157cad31331Smrg#else
1584f5e7dd7Smrg    if (xsvc_fd >= 0) {
1594f5e7dd7Smrg	close(xsvc_fd);
1604f5e7dd7Smrg	xsvc_fd = -1;
1614f5e7dd7Smrg    }
162cad31331Smrg#endif
1634f5e7dd7Smrg}
1644f5e7dd7Smrg
16586ea1d58Smrg
16686ea1d58Smrg#ifdef __sparc
1674f5e7dd7Smrg/*
16886ea1d58Smrg * Release resources per device
1694f5e7dd7Smrg */
17086ea1d58Smrgstatic void
17186ea1d58Smrgpci_system_solx_devfs_destroy_device( struct pci_device *dev )
1724f5e7dd7Smrg{
17386ea1d58Smrg   if (MAPPING_DEV_PATH(dev))
17486ea1d58Smrg	di_devfs_path_free((char *) MAPPING_DEV_PATH(dev));
1754f5e7dd7Smrg}
17686ea1d58Smrg#endif
1774f5e7dd7Smrg
1784f5e7dd7Smrg
1794f5e7dd7Smrgstatic int
18086ea1d58Smrgprobe_device_node(di_node_t node, void *arg)
1814f5e7dd7Smrg{
18286ea1d58Smrg    int *retbuf = NULL;
18386ea1d58Smrg    int len = 0, i;
18486ea1d58Smrg    struct pci_device	*pci_base;
18586ea1d58Smrg    probe_info_t *pinfo = ((probe_args_t *)arg)->pinfo;
18686ea1d58Smrg    nexus_t *nexus = ((probe_args_t *)arg)->nexus;
18786ea1d58Smrg    property_info_t property_list[] = {
18886ea1d58Smrg        { "class-code", 0 },
18986ea1d58Smrg        { "device-id", 0 },
19086ea1d58Smrg        { "vendor-id", 0 },
19186ea1d58Smrg        { "revision-id", 0},
19286ea1d58Smrg        { "subsystem-vendor-id", 0},
19386ea1d58Smrg        { "subsystem-id", 0},
19486ea1d58Smrg    };
19586ea1d58Smrg#define NUM_PROPERTIES		sizeof(property_list)/sizeof(property_info_t)
1964f5e7dd7Smrg
19786ea1d58Smrg    len = di_prop_lookup_ints(DDI_DEV_T_ANY, node, "reg", &retbuf);
1984f5e7dd7Smrg
199cad31331Smrg#ifdef __sparc
20086ea1d58Smrg    if ((len <= 0) && di_phdl)
20186ea1d58Smrg	len = di_prom_prop_lookup_ints(di_phdl, node, "reg", &retbuf);
202cad31331Smrg#endif
2034f5e7dd7Smrg
20486ea1d58Smrg    /* Exclude usb devices */
20586ea1d58Smrg    if (len < 5) {
20686ea1d58Smrg	return DI_WALK_CONTINUE;
20786ea1d58Smrg    }
2084f5e7dd7Smrg
20986ea1d58Smrg    pci_base = &pinfo->devices[pinfo->num_devices].base;
2104f5e7dd7Smrg
21186ea1d58Smrg    pci_base->domain = nexus->domain;
21286ea1d58Smrg    pci_base->bus = PCI_REG_BUS_G(retbuf[0]);
21386ea1d58Smrg    pci_base->dev = PCI_REG_DEV_G(retbuf[0]);
21486ea1d58Smrg    pci_base->func  = PCI_REG_FUNC_G(retbuf[0]);
2154f5e7dd7Smrg
21686ea1d58Smrg    /* Get property values */
21786ea1d58Smrg    for (i = 0; i < NUM_PROPERTIES; i++) {
21886ea1d58Smrg	len = di_prop_lookup_ints(DDI_DEV_T_ANY, node,
21986ea1d58Smrg		property_list[i].name, &retbuf);
220cad31331Smrg#ifdef __sparc
22186ea1d58Smrg	if ((len <= 0) && di_phdl)
22286ea1d58Smrg	    len = di_prom_prop_lookup_ints(di_phdl, node,
22386ea1d58Smrg		property_list[i].name, &retbuf);
224cad31331Smrg#endif
2254f5e7dd7Smrg
22686ea1d58Smrg	if (len > 0)
22786ea1d58Smrg	    property_list[i].value = retbuf[0];
22886ea1d58Smrg	else {
22986ea1d58Smrg	    /* a device must have property "class-code", "device-id", "vendor-id" */
23086ea1d58Smrg	    if (i < 3)
23186ea1d58Smrg		return DI_WALK_CONTINUE;
23286ea1d58Smrg#ifdef DEBUG
23386ea1d58Smrg	    fprintf(stderr, "cannot get property \"%s\" for nexus = %s :\n",
23486ea1d58Smrg		property_list[i].name, nexus->path);
23586ea1d58Smrg	    fprintf(stderr, "	domain = %x, busno = %x, devno = %x, funcno = %x\n",
23686ea1d58Smrg		pci_base->domain, pci_base->bus, pci_base->dev, pci_base->func);
23786ea1d58Smrg#endif
2384f5e7dd7Smrg	}
2394f5e7dd7Smrg    }
2404f5e7dd7Smrg
24186ea1d58Smrg    if ((property_list[1].value == 0) && (property_list[2].value == 0))
24286ea1d58Smrg	return DI_WALK_CONTINUE;
243cad31331Smrg
24486ea1d58Smrg    pci_base->device_class = property_list[0].value;
24586ea1d58Smrg    pci_base->device_id = property_list[1].value;
24686ea1d58Smrg    pci_base->vendor_id = property_list[2].value;
24786ea1d58Smrg    pci_base->revision = property_list[3].value;
24886ea1d58Smrg    pci_base->subvendor_id = property_list[4].value;
24986ea1d58Smrg    pci_base->subdevice_id = property_list[5].value;
250cad31331Smrg
25186ea1d58Smrg#ifdef DEBUG
25286ea1d58Smrg    fprintf(stderr,
25386ea1d58Smrg	    "nexus = %s, domain = %x, busno = %x, devno = %x, funcno = %x\n",
25486ea1d58Smrg	    nexus->path, pci_base->domain, pci_base->bus, pci_base->dev, pci_base->func);
25586ea1d58Smrg#endif
256cad31331Smrg
25786ea1d58Smrg    pinfo->num_devices++;
25886ea1d58Smrg    if (pinfo->num_devices == pinfo->num_allocated_elems) {
25986ea1d58Smrg	struct pci_device_private *new_devs;
26086ea1d58Smrg	size_t new_num_elems = pinfo->num_allocated_elems * 2;
26186ea1d58Smrg
26286ea1d58Smrg	new_devs = realloc(pinfo->devices,
26386ea1d58Smrg	new_num_elems * sizeof (struct pci_device_private));
26486ea1d58Smrg	if (new_devs == NULL) {
26586ea1d58Smrg	    (void) fprintf(stderr,
26686ea1d58Smrg	           "Error allocating memory for PCI devices:"
26786ea1d58Smrg		   " %s\n discarding additional devices\n",
26886ea1d58Smrg		   strerror(errno));
26986ea1d58Smrg	    ((probe_args_t *)arg)->ret = 1;
27086ea1d58Smrg	    return (DI_WALK_TERMINATE);
271cad31331Smrg	}
27286ea1d58Smrg	(void) memset(&new_devs[pinfo->num_devices], 0,
27386ea1d58Smrg		pinfo->num_allocated_elems *
27486ea1d58Smrg		sizeof (struct pci_device_private));
27586ea1d58Smrg	pinfo->num_allocated_elems = new_num_elems;
27686ea1d58Smrg	pinfo->devices = new_devs;
277cad31331Smrg    }
278cad31331Smrg
27986ea1d58Smrg    return (DI_WALK_CONTINUE);
280cad31331Smrg}
2814f5e7dd7Smrg/*
2824f5e7dd7Smrg * This function is called from di_walk_minor() when any PROBE is processed
2834f5e7dd7Smrg */
2844f5e7dd7Smrgstatic int
2854f5e7dd7Smrgprobe_nexus_node(di_node_t di_node, di_minor_t minor, void *arg)
2864f5e7dd7Smrg{
287cad31331Smrg    probe_info_t *pinfo = (probe_info_t *)arg;
288cad31331Smrg    char *nexus_name, *nexus_dev_path;
2894f5e7dd7Smrg    nexus_t *nexus;
2904f5e7dd7Smrg    int fd;
2914f5e7dd7Smrg    char nexus_path[MAXPATHLEN];
2924f5e7dd7Smrg
2934f5e7dd7Smrg    di_prop_t prop;
2944f5e7dd7Smrg    char *strings;
2954f5e7dd7Smrg    int *ints;
2964f5e7dd7Smrg    int numval;
2974f5e7dd7Smrg    int pci_node = 0;
2984f5e7dd7Smrg    int first_bus = 0, last_bus = PCI_REG_BUS_G(PCI_REG_BUS_M);
299cad31331Smrg    int domain = 0;
300cad31331Smrg#ifdef __sparc
301cad31331Smrg    int bus_range_found = 0;
302cad31331Smrg    int device_type_found = 0;
303cad31331Smrg    di_prom_prop_t prom_prop;
304cad31331Smrg#endif
305cad31331Smrg
3064f5e7dd7Smrg#ifdef DEBUG
3074f5e7dd7Smrg    nexus_name = di_devfs_minor_path(minor);
3084f5e7dd7Smrg    fprintf(stderr, "-- device name: %s\n", nexus_name);
30986ea1d58Smrg    di_devfs_path_free(nexus_name);
3104f5e7dd7Smrg#endif
3114f5e7dd7Smrg
3124f5e7dd7Smrg    for (prop = di_prop_next(di_node, NULL); prop != NULL;
3134f5e7dd7Smrg	 prop = di_prop_next(di_node, prop)) {
3144f5e7dd7Smrg
3154f5e7dd7Smrg	const char *prop_name = di_prop_name(prop);
3164f5e7dd7Smrg
3174f5e7dd7Smrg#ifdef DEBUG
3184f5e7dd7Smrg	fprintf(stderr, "   property: %s\n", prop_name);
3194f5e7dd7Smrg#endif
3204f5e7dd7Smrg
3214f5e7dd7Smrg	if (strcmp(prop_name, "device_type") == 0) {
3224f5e7dd7Smrg	    numval = di_prop_strings(prop, &strings);
323cad31331Smrg	    if (numval == 1) {
324cad31331Smrg		if (strncmp(strings, "pci", 3) != 0)
325cad31331Smrg		    /* not a PCI node, bail */
326cad31331Smrg		    return (DI_WALK_CONTINUE);
327cad31331Smrg		else {
328cad31331Smrg		    pci_node = 1;
329cad31331Smrg#ifdef __sparc
330cad31331Smrg		    device_type_found =  1;
331cad31331Smrg#endif
332cad31331Smrg		}
3334f5e7dd7Smrg	    }
3344f5e7dd7Smrg	}
3354f5e7dd7Smrg	else if (strcmp(prop_name, "class-code") == 0) {
3364f5e7dd7Smrg	    /* not a root bus node, bail */
3374f5e7dd7Smrg	    return (DI_WALK_CONTINUE);
3384f5e7dd7Smrg	}
3394f5e7dd7Smrg	else if (strcmp(prop_name, "bus-range") == 0) {
3404f5e7dd7Smrg	    numval = di_prop_ints(prop, &ints);
3414f5e7dd7Smrg	    if (numval == 2) {
3424f5e7dd7Smrg		first_bus = ints[0];
3434f5e7dd7Smrg		last_bus = ints[1];
344cad31331Smrg#ifdef __sparc
345cad31331Smrg		bus_range_found = 1;
346cad31331Smrg#endif
347cad31331Smrg	    }
348cad31331Smrg	}
34986ea1d58Smrg#ifdef __sparc
35086ea1d58Smrg	domain = nexus_count;
35186ea1d58Smrg#else
352cad31331Smrg	else if (strcmp(prop_name, "pciseg") == 0) {
353cad31331Smrg	    numval = di_prop_ints(prop, &ints);
354cad31331Smrg	    if (numval == 1) {
355cad31331Smrg		domain = ints[0];
3564f5e7dd7Smrg	    }
3574f5e7dd7Smrg	}
35886ea1d58Smrg#endif
3594f5e7dd7Smrg    }
3604f5e7dd7Smrg
361cad31331Smrg#ifdef __sparc
362cad31331Smrg    if ((!device_type_found) && di_phdl) {
363cad31331Smrg	numval = di_prom_prop_lookup_strings(di_phdl, di_node,
364cad31331Smrg	    "device_type", &strings);
365cad31331Smrg	if (numval == 1) {
366cad31331Smrg	    if (strncmp(strings, "pci", 3) != 0)
367cad31331Smrg		return (DI_WALK_CONTINUE);
368cad31331Smrg	    else
369cad31331Smrg		pci_node = 1;
370cad31331Smrg	}
371cad31331Smrg    }
372cad31331Smrg
373cad31331Smrg    if ((!bus_range_found) && di_phdl) {
374cad31331Smrg	numval = di_prom_prop_lookup_ints(di_phdl, di_node,
375cad31331Smrg	    "bus-range", &ints);
376cad31331Smrg	if (numval == 2) {
377cad31331Smrg	    first_bus = ints[0];
378cad31331Smrg	    last_bus = ints[1];
379cad31331Smrg	}
380cad31331Smrg    }
381cad31331Smrg#endif
382cad31331Smrg
3834f5e7dd7Smrg    if (pci_node != 1)
3844f5e7dd7Smrg	return (DI_WALK_CONTINUE);
3854f5e7dd7Smrg
3864f5e7dd7Smrg    /* we have a PCI root bus node. */
3874f5e7dd7Smrg    nexus = calloc(1, sizeof(nexus_t));
3884f5e7dd7Smrg    if (nexus == NULL) {
3894f5e7dd7Smrg	(void) fprintf(stderr, "Error allocating memory for nexus: %s\n",
3904f5e7dd7Smrg		       strerror(errno));
3914f5e7dd7Smrg	return (DI_WALK_TERMINATE);
3924f5e7dd7Smrg    }
3934f5e7dd7Smrg    nexus->first_bus = first_bus;
3944f5e7dd7Smrg    nexus->last_bus = last_bus;
395cad31331Smrg    nexus->domain = domain;
396cad31331Smrg
397cad31331Smrg#ifdef __sparc
39886ea1d58Smrg    nexus_count++;
399cad31331Smrg#endif
4004f5e7dd7Smrg
4014f5e7dd7Smrg    nexus_name = di_devfs_minor_path(minor);
4024f5e7dd7Smrg    if (nexus_name == NULL) {
4034f5e7dd7Smrg	(void) fprintf(stderr, "Error getting nexus path: %s\n",
4044f5e7dd7Smrg		       strerror(errno));
4054f5e7dd7Smrg	free(nexus);
4064f5e7dd7Smrg	return (DI_WALK_CONTINUE);
4074f5e7dd7Smrg    }
4084f5e7dd7Smrg
4094f5e7dd7Smrg    snprintf(nexus_path, sizeof(nexus_path), "/devices%s", nexus_name);
4104f5e7dd7Smrg    di_devfs_path_free(nexus_name);
4114f5e7dd7Smrg
4124f5e7dd7Smrg#ifdef DEBUG
4134f5e7dd7Smrg    fprintf(stderr, "nexus = %s, bus-range = %d - %d\n",
4144f5e7dd7Smrg	    nexus_path, first_bus, last_bus);
4154f5e7dd7Smrg#endif
4164f5e7dd7Smrg
417cad31331Smrg    if ((fd = open(nexus_path, O_RDWR | O_CLOEXEC)) >= 0) {
41886ea1d58Smrg	probe_args_t args;
41986ea1d58Smrg
4204f5e7dd7Smrg	nexus->path = strdup(nexus_path);
421cad31331Smrg	nexus_dev_path = di_devfs_path(di_node);
422cad31331Smrg	nexus->dev_path = strdup(nexus_dev_path);
423cad31331Smrg	di_devfs_path_free(nexus_dev_path);
42486ea1d58Smrg
42586ea1d58Smrg	/* Walk through devices under the rnode */
42686ea1d58Smrg	args.pinfo = pinfo;
42786ea1d58Smrg	args.nexus = nexus;
42886ea1d58Smrg	args.ret = 0;
42986ea1d58Smrg
43049310723Smrg	(void) di_walk_node(di_node, DI_WALK_CLDFIRST, (void *)&args, probe_device_node);
43149310723Smrg
43249310723Smrg	close(fd);
43349310723Smrg
43486ea1d58Smrg	if (args.ret) {
43586ea1d58Smrg	    free(nexus->path);
43686ea1d58Smrg	    free(nexus->dev_path);
43786ea1d58Smrg	    free(nexus);
43886ea1d58Smrg	    return (DI_WALK_TERMINATE);
43986ea1d58Smrg	}
44086ea1d58Smrg
44186ea1d58Smrg	nexus->next = nexus_list;
44286ea1d58Smrg	nexus_list = nexus;
4434f5e7dd7Smrg    } else {
4444f5e7dd7Smrg	(void) fprintf(stderr, "Error opening %s: %s\n",
4454f5e7dd7Smrg		       nexus_path, strerror(errno));
4464f5e7dd7Smrg	free(nexus);
4474f5e7dd7Smrg    }
4484f5e7dd7Smrg
4494f5e7dd7Smrg    return DI_WALK_CONTINUE;
4504f5e7dd7Smrg}
4514f5e7dd7Smrg
4524f5e7dd7Smrgstatic int
4534f5e7dd7Smrgfind_target_node(di_node_t node, void *arg)
4544f5e7dd7Smrg{
4554f5e7dd7Smrg    int *regbuf = NULL;
4564f5e7dd7Smrg    int len = 0;
4574f5e7dd7Smrg    uint32_t busno, funcno, devno;
4584f5e7dd7Smrg    i_devnode_t *devnode = (i_devnode_t *)arg;
4594f5e7dd7Smrg
4604f5e7dd7Smrg    /*
4614f5e7dd7Smrg     * Test the property functions, only for testing
4624f5e7dd7Smrg     */
4634f5e7dd7Smrg    /*
4644f5e7dd7Smrg    void *prop = DI_PROP_NIL;
4654f5e7dd7Smrg
4664f5e7dd7Smrg    (void) fprintf(stderr, "start of node 0x%x\n", node->nodeid);
4674f5e7dd7Smrg    while ((prop = di_prop_hw_next(node, prop)) != DI_PROP_NIL) {
4684f5e7dd7Smrg	int i;
4694f5e7dd7Smrg	(void) fprintf(stderr, "name=%s: ", di_prop_name(prop));
4704f5e7dd7Smrg	len = 0;
4714f5e7dd7Smrg	if (!strcmp(di_prop_name(prop), "reg")) {
4724f5e7dd7Smrg	    len = di_prop_ints(prop, &regbuf);
4734f5e7dd7Smrg	}
4744f5e7dd7Smrg	for (i = 0; i < len; i++) {
4754f5e7dd7Smrg	    fprintf(stderr, "0x%0x.", regbuf[i]);
4764f5e7dd7Smrg	}
4774f5e7dd7Smrg	fprintf(stderr, "\n");
4784f5e7dd7Smrg    }
4794f5e7dd7Smrg    (void) fprintf(stderr, "end of node 0x%x\n", node->nodeid);
4804f5e7dd7Smrg    */
4814f5e7dd7Smrg
4824f5e7dd7Smrg    len = di_prop_lookup_ints(DDI_DEV_T_ANY, node, "reg", &regbuf);
4834f5e7dd7Smrg
484cad31331Smrg#ifdef __sparc
485cad31331Smrg    if ((len <= 0) && di_phdl)
486cad31331Smrg	len = di_prom_prop_lookup_ints(di_phdl, node, "reg", &regbuf);
487cad31331Smrg#endif
488cad31331Smrg
4894f5e7dd7Smrg    if (len <= 0) {
4904f5e7dd7Smrg#ifdef DEBUG
4914f5e7dd7Smrg	fprintf(stderr, "error = %x\n", errno);
4924f5e7dd7Smrg	fprintf(stderr, "can not find assigned-address\n");
4934f5e7dd7Smrg#endif
4944f5e7dd7Smrg	return (DI_WALK_CONTINUE);
4954f5e7dd7Smrg    }
4964f5e7dd7Smrg
4974f5e7dd7Smrg    busno = PCI_REG_BUS_G(regbuf[0]);
4984f5e7dd7Smrg    devno = PCI_REG_DEV_G(regbuf[0]);
4994f5e7dd7Smrg    funcno = PCI_REG_FUNC_G(regbuf[0]);
5004f5e7dd7Smrg
5014f5e7dd7Smrg    if ((busno == devnode->bus) &&
5024f5e7dd7Smrg	(devno == devnode->dev) &&
5034f5e7dd7Smrg	(funcno == devnode->func)) {
5044f5e7dd7Smrg	devnode->node = node;
5054f5e7dd7Smrg
5064f5e7dd7Smrg	return (DI_WALK_TERMINATE);
5074f5e7dd7Smrg    }
5084f5e7dd7Smrg
5094f5e7dd7Smrg    return (DI_WALK_CONTINUE);
5104f5e7dd7Smrg}
5114f5e7dd7Smrg
5124f5e7dd7Smrg/*
5134f5e7dd7Smrg * Solaris version
5144f5e7dd7Smrg */
5154f5e7dd7Smrgstatic int
5164f5e7dd7Smrgpci_device_solx_devfs_probe( struct pci_device * dev )
5174f5e7dd7Smrg{
518cad31331Smrg    int err = 0;
5194f5e7dd7Smrg    di_node_t rnode = DI_NODE_NIL;
5204f5e7dd7Smrg    i_devnode_t args = { 0, 0, 0, DI_NODE_NIL };
5214f5e7dd7Smrg    int *regbuf;
5224f5e7dd7Smrg    pci_regspec_t *reg;
5234f5e7dd7Smrg    int i;
5244f5e7dd7Smrg    int len = 0;
5254f5e7dd7Smrg    uint ent = 0;
52686ea1d58Smrg    struct pci_device_private *priv =
52786ea1d58Smrg	(struct pci_device_private *) dev;
528cad31331Smrg    nexus_t *nexus;
5294f5e7dd7Smrg
530cad31331Smrg    if ( (nexus = find_nexus_for_bus(dev->domain, dev->bus)) == NULL )
531cad31331Smrg	return ENODEV;
5324f5e7dd7Smrg
53386ea1d58Smrg    pci_device_cfg_read_u8(dev, &priv->header_type, PCI_CONF_HEADER);
53486ea1d58Smrg
53586ea1d58Smrg    pci_device_cfg_read_u8(dev, (uint8_t *)&dev->irq, PCI_CONF_ILINE);
53686ea1d58Smrg
537cad31331Smrg    /*
538cad31331Smrg     * starting to find if it is MEM/MEM64/IO
539cad31331Smrg     * using libdevinfo
540cad31331Smrg     */
54149310723Smrg    if ((rnode = di_init(nexus->dev_path, DINFOCACHE)) == DI_NODE_NIL) {
542cad31331Smrg	err = errno;
543cad31331Smrg	(void) fprintf(stderr, "di_init failed: %s\n", strerror(errno));
544cad31331Smrg    } else {
545cad31331Smrg	args.bus = dev->bus;
546cad31331Smrg	args.dev = dev->dev;
547cad31331Smrg	args.func = dev->func;
548cad31331Smrg	(void) di_walk_node(rnode, DI_WALK_CLDFIRST,
549cad31331Smrg		(void *)&args, find_target_node);
550cad31331Smrg    }
5514f5e7dd7Smrg
552cad31331Smrg    if (args.node != DI_NODE_NIL) {
55386ea1d58Smrg	int *prop;
554cad31331Smrg#ifdef __sparc
555cad31331Smrg	di_minor_t minor;
556cad31331Smrg#endif
5574f5e7dd7Smrg
55886ea1d58Smrg	priv->is_primary = 0;
55986ea1d58Smrg
560cad31331Smrg#ifdef __sparc
561cad31331Smrg	if (minor = di_minor_next(args.node, DI_MINOR_NIL))
562cad31331Smrg	    MAPPING_DEV_PATH(dev) = di_devfs_minor_path (minor);
563cad31331Smrg	else
564cad31331Smrg	    MAPPING_DEV_PATH(dev) = NULL;
565cad31331Smrg#endif
5664f5e7dd7Smrg
56786ea1d58Smrg	if (di_prop_lookup_ints(DDI_DEV_T_ANY, args.node,
56886ea1d58Smrg				"primary-controller", &prop) >= 1) {
56986ea1d58Smrg	    if (prop[0])
57086ea1d58Smrg		priv->is_primary = 1;
57186ea1d58Smrg	}
57286ea1d58Smrg
5734f5e7dd7Smrg	/*
5744f5e7dd7Smrg	 * It will succeed for sure, because it was
5754f5e7dd7Smrg	 * successfully called in find_target_node
5764f5e7dd7Smrg	 */
5774f5e7dd7Smrg	len = di_prop_lookup_ints(DDI_DEV_T_ANY, args.node,
5784f5e7dd7Smrg				  "assigned-addresses",
5794f5e7dd7Smrg				  &regbuf);
5804f5e7dd7Smrg
581cad31331Smrg#ifdef __sparc
582cad31331Smrg	if ((len <= 0) && di_phdl) {
583cad31331Smrg	    len = di_prom_prop_lookup_ints(di_phdl, args.node,
584cad31331Smrg				"assigned-addresses", &regbuf);
585cad31331Smrg	}
586cad31331Smrg#endif
5874f5e7dd7Smrg    }
5884f5e7dd7Smrg
5894f5e7dd7Smrg    if (len <= 0)
5904f5e7dd7Smrg	goto cleanup;
5914f5e7dd7Smrg
5924f5e7dd7Smrg    /*
59386ea1d58Smrg     * Each BAR address get its own region slot in sequence.
59486ea1d58Smrg     * 32 bit BAR:
59586ea1d58Smrg     * BAR 0x10 -> slot0, BAR 0x14 -> slot1...
59686ea1d58Smrg     * 64 bit BAR:
59786ea1d58Smrg     * BAR 0x10 -> slot0, BAR 0x18 -> slot2...,
59886ea1d58Smrg     * slot1 is part of BAR 0x10
5994f5e7dd7Smrg     * Linux give two region slot for 64 bit address.
6004f5e7dd7Smrg     */
60186ea1d58Smrg    for (i = 0; i < len; i = i + (int)CELL_NUMS_1275) {
6024f5e7dd7Smrg
6034f5e7dd7Smrg	reg = (pci_regspec_t *)&regbuf[i];
6044f5e7dd7Smrg	ent = reg->pci_phys_hi & 0xff;
60586ea1d58Smrg
60686ea1d58Smrg	if (ent > PCI_CONF_ROM) {
6074f5e7dd7Smrg	    fprintf(stderr, "error ent = %d\n", ent);
6084f5e7dd7Smrg	    break;
6094f5e7dd7Smrg	}
6104f5e7dd7Smrg	/*
61186ea1d58Smrg	 * G35 broken in BAR0
6124f5e7dd7Smrg	 */
61386ea1d58Smrg	if (ent < PCI_CONF_BASE0) {
61486ea1d58Smrg	    /*
61586ea1d58Smrg	     * VGA resource here and ignore it
61686ea1d58Smrg	     */
6174f5e7dd7Smrg	    break;
61886ea1d58Smrg	} else if (ent == PCI_CONF_ROM) {
61986ea1d58Smrg	    priv->rom_base = reg->pci_phys_low |
62086ea1d58Smrg		((uint64_t)reg->pci_phys_mid << 32);
62186ea1d58Smrg	    dev->rom_size = reg->pci_size_low;
62286ea1d58Smrg	} else {
62386ea1d58Smrg	    ent = (ent - PCI_CONF_BASE0) >> 2;
62486ea1d58Smrg	    /*
62586ea1d58Smrg	     * non relocatable resource is excluded
62686ea1d58Smrg	     * such like 0xa0000, 0x3b0. If it is met,
62786ea1d58Smrg	     * the loop is broken;
62886ea1d58Smrg	     */
62986ea1d58Smrg	    if (!PCI_REG_REG_G(reg->pci_phys_hi))
63086ea1d58Smrg		break;
6314f5e7dd7Smrg
63286ea1d58Smrg	    if (reg->pci_phys_hi & PCI_PREFETCH_B) {
63386ea1d58Smrg		dev->regions[ent].is_prefetchable = 1;
63486ea1d58Smrg	    }
6354f5e7dd7Smrg
6364f5e7dd7Smrg
63786ea1d58Smrg	    dev->regions[ent].base_addr = reg->pci_phys_low |
63886ea1d58Smrg		((uint64_t)reg->pci_phys_mid << 32);
63986ea1d58Smrg	    dev->regions[ent].size = reg->pci_size_low |
64086ea1d58Smrg		((uint64_t)reg->pci_size_hi << 32);
64186ea1d58Smrg
64286ea1d58Smrg	    switch (reg->pci_phys_hi & PCI_REG_ADDR_M) {
64386ea1d58Smrg		case PCI_ADDR_IO:
64486ea1d58Smrg		    dev->regions[ent].is_IO = 1;
64586ea1d58Smrg		    break;
64686ea1d58Smrg		case PCI_ADDR_MEM32:
64786ea1d58Smrg		    break;
64886ea1d58Smrg		case PCI_ADDR_MEM64:
64986ea1d58Smrg		    dev->regions[ent].is_64 = 1;
65086ea1d58Smrg		    /*
65186ea1d58Smrg		     * Skip one slot for 64 bit address
65286ea1d58Smrg		     */
65386ea1d58Smrg		    break;
65486ea1d58Smrg	    }
6554f5e7dd7Smrg	}
6564f5e7dd7Smrg    }
6574f5e7dd7Smrg
6584f5e7dd7Smrg  cleanup:
6594f5e7dd7Smrg    if (rnode != DI_NODE_NIL) {
6604f5e7dd7Smrg	di_fini(rnode);
6614f5e7dd7Smrg    }
6624f5e7dd7Smrg    return (err);
6634f5e7dd7Smrg}
6644f5e7dd7Smrg
665cad31331Smrg/**
66686ea1d58Smrg * Map a memory region for a device using /dev/xsvc (x86) or fb device (sparc)
667cad31331Smrg *
668cad31331Smrg * \param dev   Device whose memory region is to be mapped.
669cad31331Smrg * \param map   Parameters of the mapping that is to be created.
670cad31331Smrg *
671cad31331Smrg * \return
672cad31331Smrg * Zero on success or an \c errno value on failure.
673cad31331Smrg */
674cad31331Smrgstatic int
675cad31331Smrgpci_device_solx_devfs_map_range(struct pci_device *dev,
676cad31331Smrg				struct pci_device_mapping *map)
677cad31331Smrg{
678cad31331Smrg    const int prot = ((map->flags & PCI_DEV_MAP_FLAG_WRITABLE) != 0)
679cad31331Smrg			? (PROT_READ | PROT_WRITE) : PROT_READ;
680cad31331Smrg    int err = 0;
681cad31331Smrg
68286ea1d58Smrg    const char *map_dev;
683cad31331Smrg    int		map_fd;
684cad31331Smrg
68586ea1d58Smrg#ifdef __sparc
68686ea1d58Smrg    char	map_dev_buf[128];
687cad31331Smrg
68886ea1d58Smrg    if (MAPPING_DEV_PATH(dev)) {
68986ea1d58Smrg	snprintf(map_dev_buf, sizeof (map_dev_buf), "%s%s",
69086ea1d58Smrg		 "/devices", MAPPING_DEV_PATH(dev));
69186ea1d58Smrg	map_dev = map_dev_buf;
692cad31331Smrg    }
69386ea1d58Smrg    else
69486ea1d58Smrg	map_dev = "/dev/fb0";
695cad31331Smrg
69686ea1d58Smrg    map_fd = -1;
697cad31331Smrg#else
698cad31331Smrg    /*
69986ea1d58Smrg     * Still uses xsvc to do the user space mapping on x86/x64,
70086ea1d58Smrg     * caches open fd across multiple calls.
701cad31331Smrg     */
70286ea1d58Smrg    map_dev = "/dev/xsvc";
70386ea1d58Smrg    map_fd = xsvc_fd;
70486ea1d58Smrg#endif
70586ea1d58Smrg
70686ea1d58Smrg    if (map_fd < 0) {
70786ea1d58Smrg	if ((map_fd = open(map_dev, O_RDWR | O_CLOEXEC)) < 0) {
708cad31331Smrg	    err = errno;
70986ea1d58Smrg	    (void) fprintf(stderr, "can not open %s: %s\n", map_dev,
710cad31331Smrg			   strerror(errno));
711cad31331Smrg	    return err;
712cad31331Smrg	}
7136a94483fSmrg#ifndef __sparc
7146a94483fSmrg        xsvc_fd = map_fd;
7156a94483fSmrg#endif
716cad31331Smrg    }
717cad31331Smrg
71886ea1d58Smrg    map->memory = mmap(NULL, map->size, prot, MAP_SHARED, map_fd, map->base);
719cad31331Smrg    if (map->memory == MAP_FAILED) {
720cad31331Smrg	err = errno;
721cad31331Smrg
722cad31331Smrg	(void) fprintf(stderr, "map rom region =%llx failed: %s\n",
72386ea1d58Smrg		       (unsigned long long) map->base, strerror(errno));
724cad31331Smrg    }
725cad31331Smrg
726cad31331Smrg#ifdef __sparc
727cad31331Smrg    close (map_fd);
728cad31331Smrg#endif
729cad31331Smrg
730cad31331Smrg    return err;
731cad31331Smrg}
732cad31331Smrg
7334f5e7dd7Smrg/*
7344f5e7dd7Smrg * Solaris version: read the VGA ROM data
7354f5e7dd7Smrg */
7364f5e7dd7Smrgstatic int
7374f5e7dd7Smrgpci_device_solx_devfs_read_rom( struct pci_device * dev, void * buffer )
7384f5e7dd7Smrg{
7394f5e7dd7Smrg    int err;
7404f5e7dd7Smrg    struct pci_device_mapping prom = {
7414f5e7dd7Smrg	.base = 0xC0000,
74286ea1d58Smrg	.size = 0x10000,
7434f5e7dd7Smrg	.flags = 0
7444f5e7dd7Smrg    };
74586ea1d58Smrg    struct pci_device_private *priv =
74686ea1d58Smrg	(struct pci_device_private *) dev;
74786ea1d58Smrg
74886ea1d58Smrg    if (priv->rom_base) {
74986ea1d58Smrg	prom.base = priv->rom_base;
75086ea1d58Smrg	prom.size = dev->rom_size;
75186ea1d58Smrg    }
7524f5e7dd7Smrg
7534f5e7dd7Smrg    err = pci_device_solx_devfs_map_range(dev, &prom);
7544f5e7dd7Smrg    if (err == 0) {
7554f5e7dd7Smrg	(void) bcopy(prom.memory, buffer, dev->rom_size);
7564f5e7dd7Smrg
75786ea1d58Smrg	if (munmap(prom.memory, prom.size) == -1) {
7584f5e7dd7Smrg	    err = errno;
7594f5e7dd7Smrg	}
7604f5e7dd7Smrg    }
7614f5e7dd7Smrg    return err;
7624f5e7dd7Smrg}
7634f5e7dd7Smrg
7644f5e7dd7Smrg/*
7654f5e7dd7Smrg * solaris version: Read the configurations space of the devices
7664f5e7dd7Smrg */
7674f5e7dd7Smrgstatic int
7684f5e7dd7Smrgpci_device_solx_devfs_read( struct pci_device * dev, void * data,
7694f5e7dd7Smrg			     pciaddr_t offset, pciaddr_t size,
7704f5e7dd7Smrg			     pciaddr_t * bytes_read )
7714f5e7dd7Smrg{
7724f5e7dd7Smrg    pcitool_reg_t cfg_prg;
7734f5e7dd7Smrg    int err = 0;
77486ea1d58Smrg    unsigned int i = 0;
775cad31331Smrg    nexus_t *nexus;
77649310723Smrg    int fd;
777cad31331Smrg
778cad31331Smrg    nexus = find_nexus_for_bus(dev->domain, dev->bus);
7794f5e7dd7Smrg
7804f5e7dd7Smrg    *bytes_read = 0;
7814f5e7dd7Smrg
7824f5e7dd7Smrg    if ( nexus == NULL ) {
7834f5e7dd7Smrg	return ENODEV;
7844f5e7dd7Smrg    }
7854f5e7dd7Smrg
7864f5e7dd7Smrg    cfg_prg.offset = offset;
7874f5e7dd7Smrg    cfg_prg.acc_attr = PCITOOL_ACC_ATTR_SIZE_1 + NATIVE_ENDIAN;
7884f5e7dd7Smrg    cfg_prg.bus_no = dev->bus;
7894f5e7dd7Smrg    cfg_prg.dev_no = dev->dev;
7904f5e7dd7Smrg    cfg_prg.func_no = dev->func;
7914f5e7dd7Smrg    cfg_prg.barnum = 0;
7924f5e7dd7Smrg    cfg_prg.user_version = PCITOOL_USER_VERSION;
7934f5e7dd7Smrg
79449310723Smrg    if ((fd = open(nexus->path, O_RDWR | O_CLOEXEC)) < 0)
79549310723Smrg	return ENOENT;
79649310723Smrg
7974f5e7dd7Smrg    for (i = 0; i < size; i += PCITOOL_ACC_ATTR_SIZE(PCITOOL_ACC_ATTR_SIZE_1))
7984f5e7dd7Smrg    {
7994f5e7dd7Smrg	cfg_prg.offset = offset + i;
8004f5e7dd7Smrg
80149310723Smrg	if ((err = ioctl(fd, PCITOOL_DEVICE_GET_REG, &cfg_prg)) != 0) {
8024f5e7dd7Smrg	    fprintf(stderr, "read bdf<%s,%x,%x,%x,%llx> config space failure\n",
8034f5e7dd7Smrg		    nexus->path,
8044f5e7dd7Smrg		    cfg_prg.bus_no,
8054f5e7dd7Smrg		    cfg_prg.dev_no,
8064f5e7dd7Smrg		    cfg_prg.func_no,
80786ea1d58Smrg		    (unsigned long long) cfg_prg.offset);
8084f5e7dd7Smrg	    fprintf(stderr, "Failure cause = %x\n", err);
8094f5e7dd7Smrg	    break;
8104f5e7dd7Smrg	}
8114f5e7dd7Smrg
8124f5e7dd7Smrg	((uint8_t *)data)[i] = (uint8_t)cfg_prg.data;
8134f5e7dd7Smrg	/*
8144f5e7dd7Smrg	 * DWORDS Offset or bytes Offset ??
8154f5e7dd7Smrg	 */
8164f5e7dd7Smrg    }
8174f5e7dd7Smrg    *bytes_read = i;
8184f5e7dd7Smrg
81949310723Smrg    close(fd);
82049310723Smrg
8214f5e7dd7Smrg    return (err);
8224f5e7dd7Smrg}
8234f5e7dd7Smrg
8244f5e7dd7Smrg/*
8254f5e7dd7Smrg * Solaris version
8264f5e7dd7Smrg */
8274f5e7dd7Smrgstatic int
8284f5e7dd7Smrgpci_device_solx_devfs_write( struct pci_device * dev, const void * data,
8294f5e7dd7Smrg			     pciaddr_t offset, pciaddr_t size,
8304f5e7dd7Smrg			     pciaddr_t * bytes_written )
8314f5e7dd7Smrg{
8324f5e7dd7Smrg    pcitool_reg_t cfg_prg;
8334f5e7dd7Smrg    int err = 0;
8344f5e7dd7Smrg    int cmd;
835cad31331Smrg    nexus_t *nexus;
83649310723Smrg    int fd;
837cad31331Smrg
838cad31331Smrg    nexus = find_nexus_for_bus(dev->domain, dev->bus);
8394f5e7dd7Smrg
8404f5e7dd7Smrg    if ( bytes_written != NULL ) {
8414f5e7dd7Smrg	*bytes_written = 0;
8424f5e7dd7Smrg    }
8434f5e7dd7Smrg
8444f5e7dd7Smrg    if ( nexus == NULL ) {
8454f5e7dd7Smrg	return ENODEV;
8464f5e7dd7Smrg    }
8474f5e7dd7Smrg
8484f5e7dd7Smrg    cfg_prg.offset = offset;
8494f5e7dd7Smrg    switch (size) {
8504f5e7dd7Smrg        case 1:
8514f5e7dd7Smrg	    cfg_prg.acc_attr = PCITOOL_ACC_ATTR_SIZE_1 + NATIVE_ENDIAN;
852cad31331Smrg	    cfg_prg.data = *((const uint8_t *)data);
8534f5e7dd7Smrg	    break;
8544f5e7dd7Smrg        case 2:
8554f5e7dd7Smrg	    cfg_prg.acc_attr = PCITOOL_ACC_ATTR_SIZE_2 + NATIVE_ENDIAN;
856cad31331Smrg	    cfg_prg.data = *((const uint16_t *)data);
8574f5e7dd7Smrg	    break;
8584f5e7dd7Smrg        case 4:
8594f5e7dd7Smrg	    cfg_prg.acc_attr = PCITOOL_ACC_ATTR_SIZE_4 + NATIVE_ENDIAN;
860cad31331Smrg	    cfg_prg.data = *((const uint32_t *)data);
8614f5e7dd7Smrg	    break;
8624f5e7dd7Smrg        case 8:
8634f5e7dd7Smrg	    cfg_prg.acc_attr = PCITOOL_ACC_ATTR_SIZE_8 + NATIVE_ENDIAN;
864cad31331Smrg	    cfg_prg.data = *((const uint64_t *)data);
8654f5e7dd7Smrg	    break;
8664f5e7dd7Smrg        default:
8674f5e7dd7Smrg	    return EINVAL;
8684f5e7dd7Smrg    }
8694f5e7dd7Smrg    cfg_prg.bus_no = dev->bus;
8704f5e7dd7Smrg    cfg_prg.dev_no = dev->dev;
8714f5e7dd7Smrg    cfg_prg.func_no = dev->func;
8724f5e7dd7Smrg    cfg_prg.barnum = 0;
8734f5e7dd7Smrg    cfg_prg.user_version = PCITOOL_USER_VERSION;
8744f5e7dd7Smrg
8754f5e7dd7Smrg    /*
8764f5e7dd7Smrg     * Check if this device is bridge device.
8774f5e7dd7Smrg     * If it is, it is also a nexus node???
8784f5e7dd7Smrg     * It seems that there is no explicit
8794f5e7dd7Smrg     * PCI nexus device for X86, so not applicable
8804f5e7dd7Smrg     * from pcitool_bus_reg_ops in pci_tools.c
8814f5e7dd7Smrg     */
8824f5e7dd7Smrg    cmd = PCITOOL_DEVICE_SET_REG;
8834f5e7dd7Smrg
88449310723Smrg    if ((fd = open(nexus->path, O_RDWR | O_CLOEXEC)) < 0)
88549310723Smrg	return ENOENT;
88649310723Smrg
88749310723Smrg    if ((err = ioctl(fd, cmd, &cfg_prg)) != 0) {
88849310723Smrg	close(fd);
8894f5e7dd7Smrg	return (err);
8904f5e7dd7Smrg    }
8914f5e7dd7Smrg    *bytes_written = size;
8924f5e7dd7Smrg
89349310723Smrg    close(fd);
89449310723Smrg
8954f5e7dd7Smrg    return (err);
8964f5e7dd7Smrg}
8974f5e7dd7Smrg
89886ea1d58Smrgstatic int pci_device_solx_devfs_boot_vga(struct pci_device *dev)
89986ea1d58Smrg{
90086ea1d58Smrg    struct pci_device_private *priv =
90186ea1d58Smrg	(struct pci_device_private *) dev;
90286ea1d58Smrg
90386ea1d58Smrg    return (priv->is_primary);
90486ea1d58Smrg
90586ea1d58Smrg}
90686ea1d58Smrg
90786ea1d58Smrgstatic struct pci_io_handle *
90886ea1d58Smrgpci_device_solx_devfs_open_legacy_io(struct pci_io_handle *ret,
90986ea1d58Smrg				     struct pci_device *dev,
91086ea1d58Smrg				     pciaddr_t base, pciaddr_t size)
91186ea1d58Smrg{
91286ea1d58Smrg#ifdef __x86
91386ea1d58Smrg    if (sysi86(SI86V86, V86SC_IOPL, PS_IOPL) == 0) {
91486ea1d58Smrg	ret->base = base;
91586ea1d58Smrg	ret->size = size;
9166a94483fSmrg	ret->is_legacy = 1;
91786ea1d58Smrg	return ret;
91886ea1d58Smrg    }
91986ea1d58Smrg#endif
92086ea1d58Smrg    return NULL;
92186ea1d58Smrg}
92286ea1d58Smrg
92386ea1d58Smrgstatic uint32_t
92486ea1d58Smrgpci_device_solx_devfs_read32(struct pci_io_handle *handle, uint32_t reg)
92586ea1d58Smrg{
92686ea1d58Smrg#ifdef __x86
92786ea1d58Smrg    uint16_t port = (uint16_t) (handle->base + reg);
92886ea1d58Smrg    uint32_t ret;
92986ea1d58Smrg    __asm__ __volatile__("inl %1,%0":"=a"(ret):"d"(port));
93086ea1d58Smrg    return ret;
93186ea1d58Smrg#else
93286ea1d58Smrg    return *(uint32_t *)((uintptr_t)handle->memory + reg);
93386ea1d58Smrg#endif
93486ea1d58Smrg}
93586ea1d58Smrg
93686ea1d58Smrgstatic uint16_t
93786ea1d58Smrgpci_device_solx_devfs_read16(struct pci_io_handle *handle, uint32_t reg)
93886ea1d58Smrg{
93986ea1d58Smrg#ifdef __x86
94086ea1d58Smrg    uint16_t port = (uint16_t) (handle->base + reg);
94186ea1d58Smrg    uint16_t ret;
94286ea1d58Smrg    __asm__ __volatile__("inw %1,%0":"=a"(ret):"d"(port));
94386ea1d58Smrg    return ret;
94486ea1d58Smrg#else
94586ea1d58Smrg    return *(uint16_t *)((uintptr_t)handle->memory + reg);
94686ea1d58Smrg#endif
94786ea1d58Smrg}
94886ea1d58Smrg
94986ea1d58Smrgstatic uint8_t
95086ea1d58Smrgpci_device_solx_devfs_read8(struct pci_io_handle *handle, uint32_t reg)
95186ea1d58Smrg{
95286ea1d58Smrg#ifdef __x86
95386ea1d58Smrg    uint16_t port = (uint16_t) (handle->base + reg);
95486ea1d58Smrg    uint8_t ret;
95586ea1d58Smrg    __asm__ __volatile__("inb %1,%0":"=a"(ret):"d"(port));
95686ea1d58Smrg    return ret;
95786ea1d58Smrg#else
95886ea1d58Smrg    return *(uint8_t *)((uintptr_t)handle->memory + reg);
95986ea1d58Smrg#endif
96086ea1d58Smrg}
96186ea1d58Smrg
96286ea1d58Smrgstatic void
96386ea1d58Smrgpci_device_solx_devfs_write32(struct pci_io_handle *handle, uint32_t reg,
96486ea1d58Smrg    uint32_t data)
96586ea1d58Smrg{
96686ea1d58Smrg#ifdef __x86
96786ea1d58Smrg      uint16_t port = (uint16_t) (handle->base + reg);
96886ea1d58Smrg      __asm__ __volatile__("outl %0,%1"::"a"(data), "d"(port));
96986ea1d58Smrg#else
97086ea1d58Smrg      *(uint16_t *)((uintptr_t)handle->memory + reg) = data;
97186ea1d58Smrg#endif
97286ea1d58Smrg}
97386ea1d58Smrg
97486ea1d58Smrgstatic void
97586ea1d58Smrgpci_device_solx_devfs_write16(struct pci_io_handle *handle, uint32_t reg,
97686ea1d58Smrg    uint16_t data)
97786ea1d58Smrg{
97886ea1d58Smrg#ifdef __x86
97986ea1d58Smrg      uint16_t port = (uint16_t) (handle->base + reg);
98086ea1d58Smrg      __asm__ __volatile__("outw %0,%1"::"a"(data), "d"(port));
98186ea1d58Smrg#else
98286ea1d58Smrg    *(uint8_t *)((uintptr_t)handle->memory + reg) = data;
98386ea1d58Smrg#endif
98486ea1d58Smrg}
98586ea1d58Smrg
98686ea1d58Smrgstatic void
98786ea1d58Smrgpci_device_solx_devfs_write8(struct pci_io_handle *handle, uint32_t reg,
98886ea1d58Smrg    uint8_t data)
98986ea1d58Smrg{
99086ea1d58Smrg#ifdef __x86
99186ea1d58Smrg      uint16_t port = (uint16_t) (handle->base + reg);
99286ea1d58Smrg      __asm__ __volatile__("outb %0,%1"::"a"(data), "d"(port));
99386ea1d58Smrg#else
99486ea1d58Smrg      *(uint32_t *)((uintptr_t)handle->memory + reg) = data;
99586ea1d58Smrg#endif
99686ea1d58Smrg}
99786ea1d58Smrg
99886ea1d58Smrgstatic int
99986ea1d58Smrgpci_device_solx_devfs_map_legacy(struct pci_device *dev, pciaddr_t base,
100086ea1d58Smrg				 pciaddr_t size, unsigned map_flags,
100186ea1d58Smrg				 void **addr)
100286ea1d58Smrg{
100386ea1d58Smrg    int err;
100486ea1d58Smrg    struct pci_device_mapping map = {
100586ea1d58Smrg	.base = base,
100686ea1d58Smrg	.size = size,
100786ea1d58Smrg	.flags = map_flags,
100886ea1d58Smrg    };
10094f5e7dd7Smrg
101086ea1d58Smrg    err = pci_device_solx_devfs_map_range(dev, &map);
101186ea1d58Smrg    if (err == 0)
101286ea1d58Smrg	*addr = map.memory;
101386ea1d58Smrg    return err;
101486ea1d58Smrg}
101586ea1d58Smrg
101686ea1d58Smrgstatic int
101786ea1d58Smrgpci_device_solx_devfs_unmap_legacy(struct pci_device *dev,
101886ea1d58Smrg				   void *addr, pciaddr_t size)
101986ea1d58Smrg{
102086ea1d58Smrg    struct pci_device_mapping map = {
102186ea1d58Smrg	.memory = addr,
102286ea1d58Smrg	.size = size,
102386ea1d58Smrg    };
102486ea1d58Smrg
102586ea1d58Smrg    return pci_device_generic_unmap_range(dev, &map);
102686ea1d58Smrg}
1027cad31331Smrg
1028cad31331Smrgstatic const struct pci_system_methods solx_devfs_methods = {
1029cad31331Smrg    .destroy = pci_system_solx_devfs_destroy,
103086ea1d58Smrg#ifdef __sparc
103186ea1d58Smrg    .destroy_device = pci_system_solx_devfs_destroy_device,
103286ea1d58Smrg#else
1033cad31331Smrg    .destroy_device = NULL,
103486ea1d58Smrg#endif
1035cad31331Smrg    .read_rom = pci_device_solx_devfs_read_rom,
1036cad31331Smrg    .probe = pci_device_solx_devfs_probe,
1037cad31331Smrg    .map_range = pci_device_solx_devfs_map_range,
1038cad31331Smrg    .unmap_range = pci_device_generic_unmap_range,
1039cad31331Smrg
1040cad31331Smrg    .read = pci_device_solx_devfs_read,
1041cad31331Smrg    .write = pci_device_solx_devfs_write,
1042cad31331Smrg
104386ea1d58Smrg    .fill_capabilities = pci_fill_capabilities_generic,
104486ea1d58Smrg    .boot_vga = pci_device_solx_devfs_boot_vga,
104586ea1d58Smrg
104686ea1d58Smrg    .open_legacy_io = pci_device_solx_devfs_open_legacy_io,
104786ea1d58Smrg    .read32 = pci_device_solx_devfs_read32,
104886ea1d58Smrg    .read16 = pci_device_solx_devfs_read16,
104986ea1d58Smrg    .read8 = pci_device_solx_devfs_read8,
105086ea1d58Smrg    .write32 = pci_device_solx_devfs_write32,
105186ea1d58Smrg    .write16 = pci_device_solx_devfs_write16,
105286ea1d58Smrg    .write8 = pci_device_solx_devfs_write8,
105386ea1d58Smrg    .map_legacy = pci_device_solx_devfs_map_legacy,
105486ea1d58Smrg    .unmap_legacy = pci_device_solx_devfs_unmap_legacy,
1055cad31331Smrg};
1056cad31331Smrg
1057cad31331Smrg/*
1058cad31331Smrg * Attempt to access PCI subsystem using Solaris's devfs interface.
1059cad31331Smrg * Solaris version
10604f5e7dd7Smrg */
1061cad31331Smrg_pci_hidden int
1062cad31331Smrgpci_system_solx_devfs_create( void )
10634f5e7dd7Smrg{
10644f5e7dd7Smrg    int err = 0;
1065cad31331Smrg    di_node_t di_node;
1066cad31331Smrg    probe_info_t pinfo;
1067cad31331Smrg    struct pci_device_private *devices;
10684f5e7dd7Smrg
1069cad31331Smrg    if (nexus_list != NULL) {
1070cad31331Smrg	return 0;
10714f5e7dd7Smrg    }
10724f5e7dd7Smrg
107349310723Smrg    if ((di_node = di_init("/", DINFOCACHE)) == DI_NODE_NIL) {
10744f5e7dd7Smrg	err = errno;
1075cad31331Smrg	(void) fprintf(stderr, "di_init() failed: %s\n",
1076cad31331Smrg		       strerror(errno));
1077cad31331Smrg	return (err);
1078cad31331Smrg    }
10794f5e7dd7Smrg
1080cad31331Smrg    if ((devices = calloc(INITIAL_NUM_DEVICES,
1081cad31331Smrg			sizeof (struct pci_device_private))) == NULL) {
1082cad31331Smrg	err = errno;
1083cad31331Smrg	di_fini(di_node);
1084cad31331Smrg	return (err);
10854f5e7dd7Smrg    }
10864f5e7dd7Smrg
1087cad31331Smrg#ifdef __sparc
1088cad31331Smrg    if ((di_phdl = di_prom_init()) == DI_PROM_HANDLE_NIL)
1089cad31331Smrg	(void) fprintf(stderr, "di_prom_init failed: %s\n", strerror(errno));
1090cad31331Smrg#endif
1091cad31331Smrg
1092cad31331Smrg    pinfo.num_allocated_elems = INITIAL_NUM_DEVICES;
1093cad31331Smrg    pinfo.num_devices = 0;
1094cad31331Smrg    pinfo.devices = devices;
109586ea1d58Smrg#ifdef __sparc
109686ea1d58Smrg    nexus_count = 0;
109786ea1d58Smrg#endif
1098cad31331Smrg    (void) di_walk_minor(di_node, DDI_NT_REGACC, 0, &pinfo, probe_nexus_node);
1099cad31331Smrg
1100cad31331Smrg    di_fini(di_node);
1101cad31331Smrg
1102cad31331Smrg    if ((pci_sys = calloc(1, sizeof (struct pci_system))) == NULL) {
1103cad31331Smrg	err = errno;
1104cad31331Smrg	free(devices);
1105cad31331Smrg	return (err);
1106cad31331Smrg    }
1107cad31331Smrg
1108cad31331Smrg    pci_sys->methods = &solx_devfs_methods;
1109cad31331Smrg    pci_sys->devices = pinfo.devices;
1110cad31331Smrg    pci_sys->num_devices = pinfo.num_devices;
1111cad31331Smrg
1112cad31331Smrg    return (err);
11134f5e7dd7Smrg}
1114