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
21666337f63Smrg    if (nexus->domain > 0xffff)
21766337f63Smrg	pci_base->domain_16 = 0xffff;
21866337f63Smrg    else
21966337f63Smrg	pci_base->domain_16 = nexus->domain;
22066337f63Smrg
22186ea1d58Smrg    /* Get property values */
22286ea1d58Smrg    for (i = 0; i < NUM_PROPERTIES; i++) {
22386ea1d58Smrg	len = di_prop_lookup_ints(DDI_DEV_T_ANY, node,
22486ea1d58Smrg		property_list[i].name, &retbuf);
225cad31331Smrg#ifdef __sparc
22686ea1d58Smrg	if ((len <= 0) && di_phdl)
22786ea1d58Smrg	    len = di_prom_prop_lookup_ints(di_phdl, node,
22886ea1d58Smrg		property_list[i].name, &retbuf);
229cad31331Smrg#endif
2304f5e7dd7Smrg
23186ea1d58Smrg	if (len > 0)
23286ea1d58Smrg	    property_list[i].value = retbuf[0];
23386ea1d58Smrg	else {
23486ea1d58Smrg	    /* a device must have property "class-code", "device-id", "vendor-id" */
23586ea1d58Smrg	    if (i < 3)
23686ea1d58Smrg		return DI_WALK_CONTINUE;
23786ea1d58Smrg#ifdef DEBUG
23886ea1d58Smrg	    fprintf(stderr, "cannot get property \"%s\" for nexus = %s :\n",
23986ea1d58Smrg		property_list[i].name, nexus->path);
24086ea1d58Smrg	    fprintf(stderr, "	domain = %x, busno = %x, devno = %x, funcno = %x\n",
24186ea1d58Smrg		pci_base->domain, pci_base->bus, pci_base->dev, pci_base->func);
24286ea1d58Smrg#endif
2434f5e7dd7Smrg	}
2444f5e7dd7Smrg    }
2454f5e7dd7Smrg
24686ea1d58Smrg    if ((property_list[1].value == 0) && (property_list[2].value == 0))
24786ea1d58Smrg	return DI_WALK_CONTINUE;
248cad31331Smrg
24986ea1d58Smrg    pci_base->device_class = property_list[0].value;
25086ea1d58Smrg    pci_base->device_id = property_list[1].value;
25186ea1d58Smrg    pci_base->vendor_id = property_list[2].value;
25286ea1d58Smrg    pci_base->revision = property_list[3].value;
25386ea1d58Smrg    pci_base->subvendor_id = property_list[4].value;
25486ea1d58Smrg    pci_base->subdevice_id = property_list[5].value;
255cad31331Smrg
25686ea1d58Smrg#ifdef DEBUG
25786ea1d58Smrg    fprintf(stderr,
25886ea1d58Smrg	    "nexus = %s, domain = %x, busno = %x, devno = %x, funcno = %x\n",
25986ea1d58Smrg	    nexus->path, pci_base->domain, pci_base->bus, pci_base->dev, pci_base->func);
26086ea1d58Smrg#endif
261cad31331Smrg
26286ea1d58Smrg    pinfo->num_devices++;
26386ea1d58Smrg    if (pinfo->num_devices == pinfo->num_allocated_elems) {
26486ea1d58Smrg	struct pci_device_private *new_devs;
26586ea1d58Smrg	size_t new_num_elems = pinfo->num_allocated_elems * 2;
26686ea1d58Smrg
26786ea1d58Smrg	new_devs = realloc(pinfo->devices,
26886ea1d58Smrg	new_num_elems * sizeof (struct pci_device_private));
26986ea1d58Smrg	if (new_devs == NULL) {
27086ea1d58Smrg	    (void) fprintf(stderr,
27186ea1d58Smrg	           "Error allocating memory for PCI devices:"
27286ea1d58Smrg		   " %s\n discarding additional devices\n",
27386ea1d58Smrg		   strerror(errno));
27486ea1d58Smrg	    ((probe_args_t *)arg)->ret = 1;
27586ea1d58Smrg	    return (DI_WALK_TERMINATE);
276cad31331Smrg	}
27786ea1d58Smrg	(void) memset(&new_devs[pinfo->num_devices], 0,
27886ea1d58Smrg		pinfo->num_allocated_elems *
27986ea1d58Smrg		sizeof (struct pci_device_private));
28086ea1d58Smrg	pinfo->num_allocated_elems = new_num_elems;
28186ea1d58Smrg	pinfo->devices = new_devs;
282cad31331Smrg    }
283cad31331Smrg
28486ea1d58Smrg    return (DI_WALK_CONTINUE);
285cad31331Smrg}
2864f5e7dd7Smrg/*
2874f5e7dd7Smrg * This function is called from di_walk_minor() when any PROBE is processed
2884f5e7dd7Smrg */
2894f5e7dd7Smrgstatic int
2904f5e7dd7Smrgprobe_nexus_node(di_node_t di_node, di_minor_t minor, void *arg)
2914f5e7dd7Smrg{
292cad31331Smrg    probe_info_t *pinfo = (probe_info_t *)arg;
293cad31331Smrg    char *nexus_name, *nexus_dev_path;
2944f5e7dd7Smrg    nexus_t *nexus;
2954f5e7dd7Smrg    int fd;
2964f5e7dd7Smrg    char nexus_path[MAXPATHLEN];
2974f5e7dd7Smrg
2984f5e7dd7Smrg    di_prop_t prop;
2994f5e7dd7Smrg    char *strings;
3004f5e7dd7Smrg    int *ints;
3014f5e7dd7Smrg    int numval;
3024f5e7dd7Smrg    int pci_node = 0;
3034f5e7dd7Smrg    int first_bus = 0, last_bus = PCI_REG_BUS_G(PCI_REG_BUS_M);
304cad31331Smrg    int domain = 0;
305cad31331Smrg#ifdef __sparc
306cad31331Smrg    int bus_range_found = 0;
307cad31331Smrg    int device_type_found = 0;
308cad31331Smrg    di_prom_prop_t prom_prop;
309cad31331Smrg#endif
310cad31331Smrg
3114f5e7dd7Smrg#ifdef DEBUG
3124f5e7dd7Smrg    nexus_name = di_devfs_minor_path(minor);
3134f5e7dd7Smrg    fprintf(stderr, "-- device name: %s\n", nexus_name);
31486ea1d58Smrg    di_devfs_path_free(nexus_name);
3154f5e7dd7Smrg#endif
3164f5e7dd7Smrg
3174f5e7dd7Smrg    for (prop = di_prop_next(di_node, NULL); prop != NULL;
3184f5e7dd7Smrg	 prop = di_prop_next(di_node, prop)) {
3194f5e7dd7Smrg
3204f5e7dd7Smrg	const char *prop_name = di_prop_name(prop);
3214f5e7dd7Smrg
3224f5e7dd7Smrg#ifdef DEBUG
3234f5e7dd7Smrg	fprintf(stderr, "   property: %s\n", prop_name);
3244f5e7dd7Smrg#endif
3254f5e7dd7Smrg
3264f5e7dd7Smrg	if (strcmp(prop_name, "device_type") == 0) {
3274f5e7dd7Smrg	    numval = di_prop_strings(prop, &strings);
328cad31331Smrg	    if (numval == 1) {
329cad31331Smrg		if (strncmp(strings, "pci", 3) != 0)
330cad31331Smrg		    /* not a PCI node, bail */
331cad31331Smrg		    return (DI_WALK_CONTINUE);
332cad31331Smrg		else {
333cad31331Smrg		    pci_node = 1;
334cad31331Smrg#ifdef __sparc
335cad31331Smrg		    device_type_found =  1;
336cad31331Smrg#endif
337cad31331Smrg		}
3384f5e7dd7Smrg	    }
3394f5e7dd7Smrg	}
3404f5e7dd7Smrg	else if (strcmp(prop_name, "class-code") == 0) {
3414f5e7dd7Smrg	    /* not a root bus node, bail */
3424f5e7dd7Smrg	    return (DI_WALK_CONTINUE);
3434f5e7dd7Smrg	}
3444f5e7dd7Smrg	else if (strcmp(prop_name, "bus-range") == 0) {
3454f5e7dd7Smrg	    numval = di_prop_ints(prop, &ints);
3464f5e7dd7Smrg	    if (numval == 2) {
3474f5e7dd7Smrg		first_bus = ints[0];
3484f5e7dd7Smrg		last_bus = ints[1];
349cad31331Smrg#ifdef __sparc
350cad31331Smrg		bus_range_found = 1;
351cad31331Smrg#endif
352cad31331Smrg	    }
353cad31331Smrg	}
35486ea1d58Smrg#ifdef __sparc
35586ea1d58Smrg	domain = nexus_count;
35686ea1d58Smrg#else
357cad31331Smrg	else if (strcmp(prop_name, "pciseg") == 0) {
358cad31331Smrg	    numval = di_prop_ints(prop, &ints);
359cad31331Smrg	    if (numval == 1) {
360cad31331Smrg		domain = ints[0];
3614f5e7dd7Smrg	    }
3624f5e7dd7Smrg	}
36386ea1d58Smrg#endif
3644f5e7dd7Smrg    }
3654f5e7dd7Smrg
366cad31331Smrg#ifdef __sparc
367cad31331Smrg    if ((!device_type_found) && di_phdl) {
368cad31331Smrg	numval = di_prom_prop_lookup_strings(di_phdl, di_node,
369cad31331Smrg	    "device_type", &strings);
370cad31331Smrg	if (numval == 1) {
371cad31331Smrg	    if (strncmp(strings, "pci", 3) != 0)
372cad31331Smrg		return (DI_WALK_CONTINUE);
373cad31331Smrg	    else
374cad31331Smrg		pci_node = 1;
375cad31331Smrg	}
376cad31331Smrg    }
377cad31331Smrg
378cad31331Smrg    if ((!bus_range_found) && di_phdl) {
379cad31331Smrg	numval = di_prom_prop_lookup_ints(di_phdl, di_node,
380cad31331Smrg	    "bus-range", &ints);
381cad31331Smrg	if (numval == 2) {
382cad31331Smrg	    first_bus = ints[0];
383cad31331Smrg	    last_bus = ints[1];
384cad31331Smrg	}
385cad31331Smrg    }
386cad31331Smrg#endif
387cad31331Smrg
3884f5e7dd7Smrg    if (pci_node != 1)
3894f5e7dd7Smrg	return (DI_WALK_CONTINUE);
3904f5e7dd7Smrg
3914f5e7dd7Smrg    /* we have a PCI root bus node. */
3924f5e7dd7Smrg    nexus = calloc(1, sizeof(nexus_t));
3934f5e7dd7Smrg    if (nexus == NULL) {
3944f5e7dd7Smrg	(void) fprintf(stderr, "Error allocating memory for nexus: %s\n",
3954f5e7dd7Smrg		       strerror(errno));
3964f5e7dd7Smrg	return (DI_WALK_TERMINATE);
3974f5e7dd7Smrg    }
3984f5e7dd7Smrg    nexus->first_bus = first_bus;
3994f5e7dd7Smrg    nexus->last_bus = last_bus;
400cad31331Smrg    nexus->domain = domain;
401cad31331Smrg
402cad31331Smrg#ifdef __sparc
40386ea1d58Smrg    nexus_count++;
404cad31331Smrg#endif
4054f5e7dd7Smrg
4064f5e7dd7Smrg    nexus_name = di_devfs_minor_path(minor);
4074f5e7dd7Smrg    if (nexus_name == NULL) {
4084f5e7dd7Smrg	(void) fprintf(stderr, "Error getting nexus path: %s\n",
4094f5e7dd7Smrg		       strerror(errno));
4104f5e7dd7Smrg	free(nexus);
4114f5e7dd7Smrg	return (DI_WALK_CONTINUE);
4124f5e7dd7Smrg    }
4134f5e7dd7Smrg
4144f5e7dd7Smrg    snprintf(nexus_path, sizeof(nexus_path), "/devices%s", nexus_name);
4154f5e7dd7Smrg    di_devfs_path_free(nexus_name);
4164f5e7dd7Smrg
4174f5e7dd7Smrg#ifdef DEBUG
4184f5e7dd7Smrg    fprintf(stderr, "nexus = %s, bus-range = %d - %d\n",
4194f5e7dd7Smrg	    nexus_path, first_bus, last_bus);
4204f5e7dd7Smrg#endif
4214f5e7dd7Smrg
422cad31331Smrg    if ((fd = open(nexus_path, O_RDWR | O_CLOEXEC)) >= 0) {
42386ea1d58Smrg	probe_args_t args;
42486ea1d58Smrg
4254f5e7dd7Smrg	nexus->path = strdup(nexus_path);
426cad31331Smrg	nexus_dev_path = di_devfs_path(di_node);
427cad31331Smrg	nexus->dev_path = strdup(nexus_dev_path);
428cad31331Smrg	di_devfs_path_free(nexus_dev_path);
42986ea1d58Smrg
43086ea1d58Smrg	/* Walk through devices under the rnode */
43186ea1d58Smrg	args.pinfo = pinfo;
43286ea1d58Smrg	args.nexus = nexus;
43386ea1d58Smrg	args.ret = 0;
43486ea1d58Smrg
43549310723Smrg	(void) di_walk_node(di_node, DI_WALK_CLDFIRST, (void *)&args, probe_device_node);
43649310723Smrg
43749310723Smrg	close(fd);
43849310723Smrg
43986ea1d58Smrg	if (args.ret) {
44086ea1d58Smrg	    free(nexus->path);
44186ea1d58Smrg	    free(nexus->dev_path);
44286ea1d58Smrg	    free(nexus);
44386ea1d58Smrg	    return (DI_WALK_TERMINATE);
44486ea1d58Smrg	}
44586ea1d58Smrg
44686ea1d58Smrg	nexus->next = nexus_list;
44786ea1d58Smrg	nexus_list = nexus;
4484f5e7dd7Smrg    } else {
4494f5e7dd7Smrg	(void) fprintf(stderr, "Error opening %s: %s\n",
4504f5e7dd7Smrg		       nexus_path, strerror(errno));
4514f5e7dd7Smrg	free(nexus);
4524f5e7dd7Smrg    }
4534f5e7dd7Smrg
4544f5e7dd7Smrg    return DI_WALK_CONTINUE;
4554f5e7dd7Smrg}
4564f5e7dd7Smrg
4574f5e7dd7Smrgstatic int
4584f5e7dd7Smrgfind_target_node(di_node_t node, void *arg)
4594f5e7dd7Smrg{
4604f5e7dd7Smrg    int *regbuf = NULL;
4614f5e7dd7Smrg    int len = 0;
4624f5e7dd7Smrg    uint32_t busno, funcno, devno;
4634f5e7dd7Smrg    i_devnode_t *devnode = (i_devnode_t *)arg;
4644f5e7dd7Smrg
4654f5e7dd7Smrg    /*
4664f5e7dd7Smrg     * Test the property functions, only for testing
4674f5e7dd7Smrg     */
4684f5e7dd7Smrg    /*
4694f5e7dd7Smrg    void *prop = DI_PROP_NIL;
4704f5e7dd7Smrg
4714f5e7dd7Smrg    (void) fprintf(stderr, "start of node 0x%x\n", node->nodeid);
4724f5e7dd7Smrg    while ((prop = di_prop_hw_next(node, prop)) != DI_PROP_NIL) {
4734f5e7dd7Smrg	int i;
4744f5e7dd7Smrg	(void) fprintf(stderr, "name=%s: ", di_prop_name(prop));
4754f5e7dd7Smrg	len = 0;
4764f5e7dd7Smrg	if (!strcmp(di_prop_name(prop), "reg")) {
4774f5e7dd7Smrg	    len = di_prop_ints(prop, &regbuf);
4784f5e7dd7Smrg	}
4794f5e7dd7Smrg	for (i = 0; i < len; i++) {
4804f5e7dd7Smrg	    fprintf(stderr, "0x%0x.", regbuf[i]);
4814f5e7dd7Smrg	}
4824f5e7dd7Smrg	fprintf(stderr, "\n");
4834f5e7dd7Smrg    }
4844f5e7dd7Smrg    (void) fprintf(stderr, "end of node 0x%x\n", node->nodeid);
4854f5e7dd7Smrg    */
4864f5e7dd7Smrg
4874f5e7dd7Smrg    len = di_prop_lookup_ints(DDI_DEV_T_ANY, node, "reg", &regbuf);
4884f5e7dd7Smrg
489cad31331Smrg#ifdef __sparc
490cad31331Smrg    if ((len <= 0) && di_phdl)
491cad31331Smrg	len = di_prom_prop_lookup_ints(di_phdl, node, "reg", &regbuf);
492cad31331Smrg#endif
493cad31331Smrg
4944f5e7dd7Smrg    if (len <= 0) {
4954f5e7dd7Smrg#ifdef DEBUG
4964f5e7dd7Smrg	fprintf(stderr, "error = %x\n", errno);
4974f5e7dd7Smrg	fprintf(stderr, "can not find assigned-address\n");
4984f5e7dd7Smrg#endif
4994f5e7dd7Smrg	return (DI_WALK_CONTINUE);
5004f5e7dd7Smrg    }
5014f5e7dd7Smrg
5024f5e7dd7Smrg    busno = PCI_REG_BUS_G(regbuf[0]);
5034f5e7dd7Smrg    devno = PCI_REG_DEV_G(regbuf[0]);
5044f5e7dd7Smrg    funcno = PCI_REG_FUNC_G(regbuf[0]);
5054f5e7dd7Smrg
5064f5e7dd7Smrg    if ((busno == devnode->bus) &&
5074f5e7dd7Smrg	(devno == devnode->dev) &&
5084f5e7dd7Smrg	(funcno == devnode->func)) {
5094f5e7dd7Smrg	devnode->node = node;
5104f5e7dd7Smrg
5114f5e7dd7Smrg	return (DI_WALK_TERMINATE);
5124f5e7dd7Smrg    }
5134f5e7dd7Smrg
5144f5e7dd7Smrg    return (DI_WALK_CONTINUE);
5154f5e7dd7Smrg}
5164f5e7dd7Smrg
5174f5e7dd7Smrg/*
5184f5e7dd7Smrg * Solaris version
5194f5e7dd7Smrg */
5204f5e7dd7Smrgstatic int
5214f5e7dd7Smrgpci_device_solx_devfs_probe( struct pci_device * dev )
5224f5e7dd7Smrg{
523cad31331Smrg    int err = 0;
5244f5e7dd7Smrg    di_node_t rnode = DI_NODE_NIL;
5254f5e7dd7Smrg    i_devnode_t args = { 0, 0, 0, DI_NODE_NIL };
5264f5e7dd7Smrg    int *regbuf;
5274f5e7dd7Smrg    pci_regspec_t *reg;
5284f5e7dd7Smrg    int i;
5294f5e7dd7Smrg    int len = 0;
5304f5e7dd7Smrg    uint ent = 0;
53186ea1d58Smrg    struct pci_device_private *priv =
53286ea1d58Smrg	(struct pci_device_private *) dev;
533cad31331Smrg    nexus_t *nexus;
5344f5e7dd7Smrg
535cad31331Smrg    if ( (nexus = find_nexus_for_bus(dev->domain, dev->bus)) == NULL )
536cad31331Smrg	return ENODEV;
5374f5e7dd7Smrg
53886ea1d58Smrg    pci_device_cfg_read_u8(dev, &priv->header_type, PCI_CONF_HEADER);
53986ea1d58Smrg
54086ea1d58Smrg    pci_device_cfg_read_u8(dev, (uint8_t *)&dev->irq, PCI_CONF_ILINE);
54186ea1d58Smrg
542cad31331Smrg    /*
543cad31331Smrg     * starting to find if it is MEM/MEM64/IO
544cad31331Smrg     * using libdevinfo
545cad31331Smrg     */
54649310723Smrg    if ((rnode = di_init(nexus->dev_path, DINFOCACHE)) == DI_NODE_NIL) {
547cad31331Smrg	err = errno;
548cad31331Smrg	(void) fprintf(stderr, "di_init failed: %s\n", strerror(errno));
549cad31331Smrg    } else {
550cad31331Smrg	args.bus = dev->bus;
551cad31331Smrg	args.dev = dev->dev;
552cad31331Smrg	args.func = dev->func;
553cad31331Smrg	(void) di_walk_node(rnode, DI_WALK_CLDFIRST,
554cad31331Smrg		(void *)&args, find_target_node);
555cad31331Smrg    }
5564f5e7dd7Smrg
557cad31331Smrg    if (args.node != DI_NODE_NIL) {
55886ea1d58Smrg	int *prop;
559cad31331Smrg#ifdef __sparc
560cad31331Smrg	di_minor_t minor;
561cad31331Smrg#endif
5624f5e7dd7Smrg
56386ea1d58Smrg	priv->is_primary = 0;
56486ea1d58Smrg
565cad31331Smrg#ifdef __sparc
566cad31331Smrg	if (minor = di_minor_next(args.node, DI_MINOR_NIL))
567cad31331Smrg	    MAPPING_DEV_PATH(dev) = di_devfs_minor_path (minor);
568cad31331Smrg	else
569cad31331Smrg	    MAPPING_DEV_PATH(dev) = NULL;
570cad31331Smrg#endif
5714f5e7dd7Smrg
57286ea1d58Smrg	if (di_prop_lookup_ints(DDI_DEV_T_ANY, args.node,
57386ea1d58Smrg				"primary-controller", &prop) >= 1) {
57486ea1d58Smrg	    if (prop[0])
57586ea1d58Smrg		priv->is_primary = 1;
57686ea1d58Smrg	}
57786ea1d58Smrg
5784f5e7dd7Smrg	/*
5794f5e7dd7Smrg	 * It will succeed for sure, because it was
5804f5e7dd7Smrg	 * successfully called in find_target_node
5814f5e7dd7Smrg	 */
5824f5e7dd7Smrg	len = di_prop_lookup_ints(DDI_DEV_T_ANY, args.node,
5834f5e7dd7Smrg				  "assigned-addresses",
5844f5e7dd7Smrg				  &regbuf);
5854f5e7dd7Smrg
586cad31331Smrg#ifdef __sparc
587cad31331Smrg	if ((len <= 0) && di_phdl) {
588cad31331Smrg	    len = di_prom_prop_lookup_ints(di_phdl, args.node,
589cad31331Smrg				"assigned-addresses", &regbuf);
590cad31331Smrg	}
591cad31331Smrg#endif
5924f5e7dd7Smrg    }
5934f5e7dd7Smrg
5944f5e7dd7Smrg    if (len <= 0)
5954f5e7dd7Smrg	goto cleanup;
5964f5e7dd7Smrg
5974f5e7dd7Smrg    /*
59886ea1d58Smrg     * Each BAR address get its own region slot in sequence.
59986ea1d58Smrg     * 32 bit BAR:
60086ea1d58Smrg     * BAR 0x10 -> slot0, BAR 0x14 -> slot1...
60186ea1d58Smrg     * 64 bit BAR:
60286ea1d58Smrg     * BAR 0x10 -> slot0, BAR 0x18 -> slot2...,
60386ea1d58Smrg     * slot1 is part of BAR 0x10
6044f5e7dd7Smrg     * Linux give two region slot for 64 bit address.
6054f5e7dd7Smrg     */
60686ea1d58Smrg    for (i = 0; i < len; i = i + (int)CELL_NUMS_1275) {
6074f5e7dd7Smrg
6084f5e7dd7Smrg	reg = (pci_regspec_t *)&regbuf[i];
6094f5e7dd7Smrg	ent = reg->pci_phys_hi & 0xff;
61086ea1d58Smrg
61186ea1d58Smrg	if (ent > PCI_CONF_ROM) {
6124f5e7dd7Smrg	    fprintf(stderr, "error ent = %d\n", ent);
6134f5e7dd7Smrg	    break;
6144f5e7dd7Smrg	}
6154f5e7dd7Smrg	/*
61686ea1d58Smrg	 * G35 broken in BAR0
6174f5e7dd7Smrg	 */
61886ea1d58Smrg	if (ent < PCI_CONF_BASE0) {
61986ea1d58Smrg	    /*
62086ea1d58Smrg	     * VGA resource here and ignore it
62186ea1d58Smrg	     */
6224f5e7dd7Smrg	    break;
62386ea1d58Smrg	} else if (ent == PCI_CONF_ROM) {
62486ea1d58Smrg	    priv->rom_base = reg->pci_phys_low |
62586ea1d58Smrg		((uint64_t)reg->pci_phys_mid << 32);
62686ea1d58Smrg	    dev->rom_size = reg->pci_size_low;
62786ea1d58Smrg	} else {
62886ea1d58Smrg	    ent = (ent - PCI_CONF_BASE0) >> 2;
62986ea1d58Smrg	    /*
63086ea1d58Smrg	     * non relocatable resource is excluded
63186ea1d58Smrg	     * such like 0xa0000, 0x3b0. If it is met,
63286ea1d58Smrg	     * the loop is broken;
63386ea1d58Smrg	     */
63486ea1d58Smrg	    if (!PCI_REG_REG_G(reg->pci_phys_hi))
63586ea1d58Smrg		break;
6364f5e7dd7Smrg
63786ea1d58Smrg	    if (reg->pci_phys_hi & PCI_PREFETCH_B) {
63886ea1d58Smrg		dev->regions[ent].is_prefetchable = 1;
63986ea1d58Smrg	    }
6404f5e7dd7Smrg
6414f5e7dd7Smrg
64286ea1d58Smrg	    dev->regions[ent].base_addr = reg->pci_phys_low |
64386ea1d58Smrg		((uint64_t)reg->pci_phys_mid << 32);
64486ea1d58Smrg	    dev->regions[ent].size = reg->pci_size_low |
64586ea1d58Smrg		((uint64_t)reg->pci_size_hi << 32);
64686ea1d58Smrg
64786ea1d58Smrg	    switch (reg->pci_phys_hi & PCI_REG_ADDR_M) {
64886ea1d58Smrg		case PCI_ADDR_IO:
64986ea1d58Smrg		    dev->regions[ent].is_IO = 1;
65086ea1d58Smrg		    break;
65186ea1d58Smrg		case PCI_ADDR_MEM32:
65286ea1d58Smrg		    break;
65386ea1d58Smrg		case PCI_ADDR_MEM64:
65486ea1d58Smrg		    dev->regions[ent].is_64 = 1;
65586ea1d58Smrg		    /*
65686ea1d58Smrg		     * Skip one slot for 64 bit address
65786ea1d58Smrg		     */
65886ea1d58Smrg		    break;
65986ea1d58Smrg	    }
6604f5e7dd7Smrg	}
6614f5e7dd7Smrg    }
6624f5e7dd7Smrg
6634f5e7dd7Smrg  cleanup:
6644f5e7dd7Smrg    if (rnode != DI_NODE_NIL) {
6654f5e7dd7Smrg	di_fini(rnode);
6664f5e7dd7Smrg    }
6674f5e7dd7Smrg    return (err);
6684f5e7dd7Smrg}
6694f5e7dd7Smrg
670cad31331Smrg/**
67186ea1d58Smrg * Map a memory region for a device using /dev/xsvc (x86) or fb device (sparc)
672cad31331Smrg *
673cad31331Smrg * \param dev   Device whose memory region is to be mapped.
674cad31331Smrg * \param map   Parameters of the mapping that is to be created.
675cad31331Smrg *
676cad31331Smrg * \return
677cad31331Smrg * Zero on success or an \c errno value on failure.
678cad31331Smrg */
679cad31331Smrgstatic int
680cad31331Smrgpci_device_solx_devfs_map_range(struct pci_device *dev,
681cad31331Smrg				struct pci_device_mapping *map)
682cad31331Smrg{
683cad31331Smrg    const int prot = ((map->flags & PCI_DEV_MAP_FLAG_WRITABLE) != 0)
684cad31331Smrg			? (PROT_READ | PROT_WRITE) : PROT_READ;
685cad31331Smrg    int err = 0;
686cad31331Smrg
68786ea1d58Smrg    const char *map_dev;
688cad31331Smrg    int		map_fd;
689cad31331Smrg
69086ea1d58Smrg#ifdef __sparc
69186ea1d58Smrg    char	map_dev_buf[128];
692cad31331Smrg
69386ea1d58Smrg    if (MAPPING_DEV_PATH(dev)) {
69486ea1d58Smrg	snprintf(map_dev_buf, sizeof (map_dev_buf), "%s%s",
69586ea1d58Smrg		 "/devices", MAPPING_DEV_PATH(dev));
69686ea1d58Smrg	map_dev = map_dev_buf;
697cad31331Smrg    }
69886ea1d58Smrg    else
69986ea1d58Smrg	map_dev = "/dev/fb0";
700cad31331Smrg
70186ea1d58Smrg    map_fd = -1;
702cad31331Smrg#else
703cad31331Smrg    /*
70486ea1d58Smrg     * Still uses xsvc to do the user space mapping on x86/x64,
70586ea1d58Smrg     * caches open fd across multiple calls.
706cad31331Smrg     */
70786ea1d58Smrg    map_dev = "/dev/xsvc";
70886ea1d58Smrg    map_fd = xsvc_fd;
70986ea1d58Smrg#endif
71086ea1d58Smrg
71186ea1d58Smrg    if (map_fd < 0) {
71286ea1d58Smrg	if ((map_fd = open(map_dev, O_RDWR | O_CLOEXEC)) < 0) {
713cad31331Smrg	    err = errno;
71486ea1d58Smrg	    (void) fprintf(stderr, "can not open %s: %s\n", map_dev,
715cad31331Smrg			   strerror(errno));
716cad31331Smrg	    return err;
717cad31331Smrg	}
7186a94483fSmrg#ifndef __sparc
7196a94483fSmrg        xsvc_fd = map_fd;
7206a94483fSmrg#endif
721cad31331Smrg    }
722cad31331Smrg
72386ea1d58Smrg    map->memory = mmap(NULL, map->size, prot, MAP_SHARED, map_fd, map->base);
724cad31331Smrg    if (map->memory == MAP_FAILED) {
725cad31331Smrg	err = errno;
726cad31331Smrg
727cad31331Smrg	(void) fprintf(stderr, "map rom region =%llx failed: %s\n",
72886ea1d58Smrg		       (unsigned long long) map->base, strerror(errno));
729cad31331Smrg    }
730cad31331Smrg
731cad31331Smrg#ifdef __sparc
732cad31331Smrg    close (map_fd);
733cad31331Smrg#endif
734cad31331Smrg
735cad31331Smrg    return err;
736cad31331Smrg}
737cad31331Smrg
7384f5e7dd7Smrg/*
7394f5e7dd7Smrg * Solaris version: read the VGA ROM data
7404f5e7dd7Smrg */
7414f5e7dd7Smrgstatic int
7424f5e7dd7Smrgpci_device_solx_devfs_read_rom( struct pci_device * dev, void * buffer )
7434f5e7dd7Smrg{
7444f5e7dd7Smrg    int err;
7454f5e7dd7Smrg    struct pci_device_mapping prom = {
7464f5e7dd7Smrg	.base = 0xC0000,
74786ea1d58Smrg	.size = 0x10000,
7484f5e7dd7Smrg	.flags = 0
7494f5e7dd7Smrg    };
75086ea1d58Smrg    struct pci_device_private *priv =
75186ea1d58Smrg	(struct pci_device_private *) dev;
75286ea1d58Smrg
75386ea1d58Smrg    if (priv->rom_base) {
75486ea1d58Smrg	prom.base = priv->rom_base;
75586ea1d58Smrg	prom.size = dev->rom_size;
75686ea1d58Smrg    }
7574f5e7dd7Smrg
7584f5e7dd7Smrg    err = pci_device_solx_devfs_map_range(dev, &prom);
7594f5e7dd7Smrg    if (err == 0) {
7604f5e7dd7Smrg	(void) bcopy(prom.memory, buffer, dev->rom_size);
7614f5e7dd7Smrg
76286ea1d58Smrg	if (munmap(prom.memory, prom.size) == -1) {
7634f5e7dd7Smrg	    err = errno;
7644f5e7dd7Smrg	}
7654f5e7dd7Smrg    }
7664f5e7dd7Smrg    return err;
7674f5e7dd7Smrg}
7684f5e7dd7Smrg
7694f5e7dd7Smrg/*
7704f5e7dd7Smrg * solaris version: Read the configurations space of the devices
7714f5e7dd7Smrg */
7724f5e7dd7Smrgstatic int
7734f5e7dd7Smrgpci_device_solx_devfs_read( struct pci_device * dev, void * data,
7744f5e7dd7Smrg			     pciaddr_t offset, pciaddr_t size,
7754f5e7dd7Smrg			     pciaddr_t * bytes_read )
7764f5e7dd7Smrg{
7774f5e7dd7Smrg    pcitool_reg_t cfg_prg;
7784f5e7dd7Smrg    int err = 0;
77986ea1d58Smrg    unsigned int i = 0;
780cad31331Smrg    nexus_t *nexus;
78149310723Smrg    int fd;
782cad31331Smrg
783cad31331Smrg    nexus = find_nexus_for_bus(dev->domain, dev->bus);
7844f5e7dd7Smrg
7854f5e7dd7Smrg    *bytes_read = 0;
7864f5e7dd7Smrg
7874f5e7dd7Smrg    if ( nexus == NULL ) {
7884f5e7dd7Smrg	return ENODEV;
7894f5e7dd7Smrg    }
7904f5e7dd7Smrg
7914f5e7dd7Smrg    cfg_prg.offset = offset;
7924f5e7dd7Smrg    cfg_prg.acc_attr = PCITOOL_ACC_ATTR_SIZE_1 + NATIVE_ENDIAN;
7934f5e7dd7Smrg    cfg_prg.bus_no = dev->bus;
7944f5e7dd7Smrg    cfg_prg.dev_no = dev->dev;
7954f5e7dd7Smrg    cfg_prg.func_no = dev->func;
7964f5e7dd7Smrg    cfg_prg.barnum = 0;
7974f5e7dd7Smrg    cfg_prg.user_version = PCITOOL_USER_VERSION;
7984f5e7dd7Smrg
79949310723Smrg    if ((fd = open(nexus->path, O_RDWR | O_CLOEXEC)) < 0)
80049310723Smrg	return ENOENT;
80149310723Smrg
8024f5e7dd7Smrg    for (i = 0; i < size; i += PCITOOL_ACC_ATTR_SIZE(PCITOOL_ACC_ATTR_SIZE_1))
8034f5e7dd7Smrg    {
8044f5e7dd7Smrg	cfg_prg.offset = offset + i;
8054f5e7dd7Smrg
80649310723Smrg	if ((err = ioctl(fd, PCITOOL_DEVICE_GET_REG, &cfg_prg)) != 0) {
8074f5e7dd7Smrg	    fprintf(stderr, "read bdf<%s,%x,%x,%x,%llx> config space failure\n",
8084f5e7dd7Smrg		    nexus->path,
8094f5e7dd7Smrg		    cfg_prg.bus_no,
8104f5e7dd7Smrg		    cfg_prg.dev_no,
8114f5e7dd7Smrg		    cfg_prg.func_no,
81286ea1d58Smrg		    (unsigned long long) cfg_prg.offset);
8134f5e7dd7Smrg	    fprintf(stderr, "Failure cause = %x\n", err);
8144f5e7dd7Smrg	    break;
8154f5e7dd7Smrg	}
8164f5e7dd7Smrg
8174f5e7dd7Smrg	((uint8_t *)data)[i] = (uint8_t)cfg_prg.data;
8184f5e7dd7Smrg	/*
8194f5e7dd7Smrg	 * DWORDS Offset or bytes Offset ??
8204f5e7dd7Smrg	 */
8214f5e7dd7Smrg    }
8224f5e7dd7Smrg    *bytes_read = i;
8234f5e7dd7Smrg
82449310723Smrg    close(fd);
82549310723Smrg
8264f5e7dd7Smrg    return (err);
8274f5e7dd7Smrg}
8284f5e7dd7Smrg
8294f5e7dd7Smrg/*
8304f5e7dd7Smrg * Solaris version
8314f5e7dd7Smrg */
8324f5e7dd7Smrgstatic int
8334f5e7dd7Smrgpci_device_solx_devfs_write( struct pci_device * dev, const void * data,
8344f5e7dd7Smrg			     pciaddr_t offset, pciaddr_t size,
8354f5e7dd7Smrg			     pciaddr_t * bytes_written )
8364f5e7dd7Smrg{
8374f5e7dd7Smrg    pcitool_reg_t cfg_prg;
8384f5e7dd7Smrg    int err = 0;
8394f5e7dd7Smrg    int cmd;
840cad31331Smrg    nexus_t *nexus;
84149310723Smrg    int fd;
842cad31331Smrg
843cad31331Smrg    nexus = find_nexus_for_bus(dev->domain, dev->bus);
8444f5e7dd7Smrg
8454f5e7dd7Smrg    if ( bytes_written != NULL ) {
8464f5e7dd7Smrg	*bytes_written = 0;
8474f5e7dd7Smrg    }
8484f5e7dd7Smrg
8494f5e7dd7Smrg    if ( nexus == NULL ) {
8504f5e7dd7Smrg	return ENODEV;
8514f5e7dd7Smrg    }
8524f5e7dd7Smrg
8534f5e7dd7Smrg    cfg_prg.offset = offset;
8544f5e7dd7Smrg    switch (size) {
8554f5e7dd7Smrg        case 1:
8564f5e7dd7Smrg	    cfg_prg.acc_attr = PCITOOL_ACC_ATTR_SIZE_1 + NATIVE_ENDIAN;
857cad31331Smrg	    cfg_prg.data = *((const uint8_t *)data);
8584f5e7dd7Smrg	    break;
8594f5e7dd7Smrg        case 2:
8604f5e7dd7Smrg	    cfg_prg.acc_attr = PCITOOL_ACC_ATTR_SIZE_2 + NATIVE_ENDIAN;
861cad31331Smrg	    cfg_prg.data = *((const uint16_t *)data);
8624f5e7dd7Smrg	    break;
8634f5e7dd7Smrg        case 4:
8644f5e7dd7Smrg	    cfg_prg.acc_attr = PCITOOL_ACC_ATTR_SIZE_4 + NATIVE_ENDIAN;
865cad31331Smrg	    cfg_prg.data = *((const uint32_t *)data);
8664f5e7dd7Smrg	    break;
8674f5e7dd7Smrg        case 8:
8684f5e7dd7Smrg	    cfg_prg.acc_attr = PCITOOL_ACC_ATTR_SIZE_8 + NATIVE_ENDIAN;
869cad31331Smrg	    cfg_prg.data = *((const uint64_t *)data);
8704f5e7dd7Smrg	    break;
8714f5e7dd7Smrg        default:
8724f5e7dd7Smrg	    return EINVAL;
8734f5e7dd7Smrg    }
8744f5e7dd7Smrg    cfg_prg.bus_no = dev->bus;
8754f5e7dd7Smrg    cfg_prg.dev_no = dev->dev;
8764f5e7dd7Smrg    cfg_prg.func_no = dev->func;
8774f5e7dd7Smrg    cfg_prg.barnum = 0;
8784f5e7dd7Smrg    cfg_prg.user_version = PCITOOL_USER_VERSION;
8794f5e7dd7Smrg
8804f5e7dd7Smrg    /*
8814f5e7dd7Smrg     * Check if this device is bridge device.
8824f5e7dd7Smrg     * If it is, it is also a nexus node???
8834f5e7dd7Smrg     * It seems that there is no explicit
8844f5e7dd7Smrg     * PCI nexus device for X86, so not applicable
8854f5e7dd7Smrg     * from pcitool_bus_reg_ops in pci_tools.c
8864f5e7dd7Smrg     */
8874f5e7dd7Smrg    cmd = PCITOOL_DEVICE_SET_REG;
8884f5e7dd7Smrg
88949310723Smrg    if ((fd = open(nexus->path, O_RDWR | O_CLOEXEC)) < 0)
89049310723Smrg	return ENOENT;
89149310723Smrg
89249310723Smrg    if ((err = ioctl(fd, cmd, &cfg_prg)) != 0) {
89349310723Smrg	close(fd);
8944f5e7dd7Smrg	return (err);
8954f5e7dd7Smrg    }
8964f5e7dd7Smrg    *bytes_written = size;
8974f5e7dd7Smrg
89849310723Smrg    close(fd);
89949310723Smrg
9004f5e7dd7Smrg    return (err);
9014f5e7dd7Smrg}
9024f5e7dd7Smrg
90386ea1d58Smrgstatic int pci_device_solx_devfs_boot_vga(struct pci_device *dev)
90486ea1d58Smrg{
90586ea1d58Smrg    struct pci_device_private *priv =
90686ea1d58Smrg	(struct pci_device_private *) dev;
90786ea1d58Smrg
90886ea1d58Smrg    return (priv->is_primary);
90986ea1d58Smrg
91086ea1d58Smrg}
91186ea1d58Smrg
91286ea1d58Smrgstatic struct pci_io_handle *
91386ea1d58Smrgpci_device_solx_devfs_open_legacy_io(struct pci_io_handle *ret,
91486ea1d58Smrg				     struct pci_device *dev,
91586ea1d58Smrg				     pciaddr_t base, pciaddr_t size)
91686ea1d58Smrg{
91786ea1d58Smrg#ifdef __x86
91886ea1d58Smrg    if (sysi86(SI86V86, V86SC_IOPL, PS_IOPL) == 0) {
91986ea1d58Smrg	ret->base = base;
92086ea1d58Smrg	ret->size = size;
9216a94483fSmrg	ret->is_legacy = 1;
92286ea1d58Smrg	return ret;
92386ea1d58Smrg    }
92486ea1d58Smrg#endif
92586ea1d58Smrg    return NULL;
92686ea1d58Smrg}
92786ea1d58Smrg
92886ea1d58Smrgstatic uint32_t
92986ea1d58Smrgpci_device_solx_devfs_read32(struct pci_io_handle *handle, uint32_t reg)
93086ea1d58Smrg{
93186ea1d58Smrg#ifdef __x86
93286ea1d58Smrg    uint16_t port = (uint16_t) (handle->base + reg);
93386ea1d58Smrg    uint32_t ret;
93486ea1d58Smrg    __asm__ __volatile__("inl %1,%0":"=a"(ret):"d"(port));
93586ea1d58Smrg    return ret;
93686ea1d58Smrg#else
93786ea1d58Smrg    return *(uint32_t *)((uintptr_t)handle->memory + reg);
93886ea1d58Smrg#endif
93986ea1d58Smrg}
94086ea1d58Smrg
94186ea1d58Smrgstatic uint16_t
94286ea1d58Smrgpci_device_solx_devfs_read16(struct pci_io_handle *handle, uint32_t reg)
94386ea1d58Smrg{
94486ea1d58Smrg#ifdef __x86
94586ea1d58Smrg    uint16_t port = (uint16_t) (handle->base + reg);
94686ea1d58Smrg    uint16_t ret;
94786ea1d58Smrg    __asm__ __volatile__("inw %1,%0":"=a"(ret):"d"(port));
94886ea1d58Smrg    return ret;
94986ea1d58Smrg#else
95086ea1d58Smrg    return *(uint16_t *)((uintptr_t)handle->memory + reg);
95186ea1d58Smrg#endif
95286ea1d58Smrg}
95386ea1d58Smrg
95486ea1d58Smrgstatic uint8_t
95586ea1d58Smrgpci_device_solx_devfs_read8(struct pci_io_handle *handle, uint32_t reg)
95686ea1d58Smrg{
95786ea1d58Smrg#ifdef __x86
95886ea1d58Smrg    uint16_t port = (uint16_t) (handle->base + reg);
95986ea1d58Smrg    uint8_t ret;
96086ea1d58Smrg    __asm__ __volatile__("inb %1,%0":"=a"(ret):"d"(port));
96186ea1d58Smrg    return ret;
96286ea1d58Smrg#else
96386ea1d58Smrg    return *(uint8_t *)((uintptr_t)handle->memory + reg);
96486ea1d58Smrg#endif
96586ea1d58Smrg}
96686ea1d58Smrg
96786ea1d58Smrgstatic void
96886ea1d58Smrgpci_device_solx_devfs_write32(struct pci_io_handle *handle, uint32_t reg,
96986ea1d58Smrg    uint32_t data)
97086ea1d58Smrg{
97186ea1d58Smrg#ifdef __x86
97286ea1d58Smrg      uint16_t port = (uint16_t) (handle->base + reg);
97386ea1d58Smrg      __asm__ __volatile__("outl %0,%1"::"a"(data), "d"(port));
97486ea1d58Smrg#else
97586ea1d58Smrg      *(uint16_t *)((uintptr_t)handle->memory + reg) = data;
97686ea1d58Smrg#endif
97786ea1d58Smrg}
97886ea1d58Smrg
97986ea1d58Smrgstatic void
98086ea1d58Smrgpci_device_solx_devfs_write16(struct pci_io_handle *handle, uint32_t reg,
98186ea1d58Smrg    uint16_t data)
98286ea1d58Smrg{
98386ea1d58Smrg#ifdef __x86
98486ea1d58Smrg      uint16_t port = (uint16_t) (handle->base + reg);
98586ea1d58Smrg      __asm__ __volatile__("outw %0,%1"::"a"(data), "d"(port));
98686ea1d58Smrg#else
98786ea1d58Smrg    *(uint8_t *)((uintptr_t)handle->memory + reg) = data;
98886ea1d58Smrg#endif
98986ea1d58Smrg}
99086ea1d58Smrg
99186ea1d58Smrgstatic void
99286ea1d58Smrgpci_device_solx_devfs_write8(struct pci_io_handle *handle, uint32_t reg,
99386ea1d58Smrg    uint8_t data)
99486ea1d58Smrg{
99586ea1d58Smrg#ifdef __x86
99686ea1d58Smrg      uint16_t port = (uint16_t) (handle->base + reg);
99786ea1d58Smrg      __asm__ __volatile__("outb %0,%1"::"a"(data), "d"(port));
99886ea1d58Smrg#else
99986ea1d58Smrg      *(uint32_t *)((uintptr_t)handle->memory + reg) = data;
100086ea1d58Smrg#endif
100186ea1d58Smrg}
100286ea1d58Smrg
100386ea1d58Smrgstatic int
100486ea1d58Smrgpci_device_solx_devfs_map_legacy(struct pci_device *dev, pciaddr_t base,
100586ea1d58Smrg				 pciaddr_t size, unsigned map_flags,
100686ea1d58Smrg				 void **addr)
100786ea1d58Smrg{
100886ea1d58Smrg    int err;
100986ea1d58Smrg    struct pci_device_mapping map = {
101086ea1d58Smrg	.base = base,
101186ea1d58Smrg	.size = size,
101286ea1d58Smrg	.flags = map_flags,
101386ea1d58Smrg    };
10144f5e7dd7Smrg
101586ea1d58Smrg    err = pci_device_solx_devfs_map_range(dev, &map);
101686ea1d58Smrg    if (err == 0)
101786ea1d58Smrg	*addr = map.memory;
101886ea1d58Smrg    return err;
101986ea1d58Smrg}
102086ea1d58Smrg
102186ea1d58Smrgstatic int
102286ea1d58Smrgpci_device_solx_devfs_unmap_legacy(struct pci_device *dev,
102386ea1d58Smrg				   void *addr, pciaddr_t size)
102486ea1d58Smrg{
102586ea1d58Smrg    struct pci_device_mapping map = {
102686ea1d58Smrg	.memory = addr,
102786ea1d58Smrg	.size = size,
102886ea1d58Smrg    };
102986ea1d58Smrg
103086ea1d58Smrg    return pci_device_generic_unmap_range(dev, &map);
103186ea1d58Smrg}
1032cad31331Smrg
1033cad31331Smrgstatic const struct pci_system_methods solx_devfs_methods = {
1034cad31331Smrg    .destroy = pci_system_solx_devfs_destroy,
103586ea1d58Smrg#ifdef __sparc
103686ea1d58Smrg    .destroy_device = pci_system_solx_devfs_destroy_device,
103786ea1d58Smrg#else
1038cad31331Smrg    .destroy_device = NULL,
103986ea1d58Smrg#endif
1040cad31331Smrg    .read_rom = pci_device_solx_devfs_read_rom,
1041cad31331Smrg    .probe = pci_device_solx_devfs_probe,
1042cad31331Smrg    .map_range = pci_device_solx_devfs_map_range,
1043cad31331Smrg    .unmap_range = pci_device_generic_unmap_range,
1044cad31331Smrg
1045cad31331Smrg    .read = pci_device_solx_devfs_read,
1046cad31331Smrg    .write = pci_device_solx_devfs_write,
1047cad31331Smrg
104886ea1d58Smrg    .fill_capabilities = pci_fill_capabilities_generic,
104986ea1d58Smrg    .boot_vga = pci_device_solx_devfs_boot_vga,
105086ea1d58Smrg
105186ea1d58Smrg    .open_legacy_io = pci_device_solx_devfs_open_legacy_io,
105286ea1d58Smrg    .read32 = pci_device_solx_devfs_read32,
105386ea1d58Smrg    .read16 = pci_device_solx_devfs_read16,
105486ea1d58Smrg    .read8 = pci_device_solx_devfs_read8,
105586ea1d58Smrg    .write32 = pci_device_solx_devfs_write32,
105686ea1d58Smrg    .write16 = pci_device_solx_devfs_write16,
105786ea1d58Smrg    .write8 = pci_device_solx_devfs_write8,
105886ea1d58Smrg    .map_legacy = pci_device_solx_devfs_map_legacy,
105986ea1d58Smrg    .unmap_legacy = pci_device_solx_devfs_unmap_legacy,
1060cad31331Smrg};
1061cad31331Smrg
1062cad31331Smrg/*
1063cad31331Smrg * Attempt to access PCI subsystem using Solaris's devfs interface.
1064cad31331Smrg * Solaris version
10654f5e7dd7Smrg */
1066cad31331Smrg_pci_hidden int
1067cad31331Smrgpci_system_solx_devfs_create( void )
10684f5e7dd7Smrg{
10694f5e7dd7Smrg    int err = 0;
1070cad31331Smrg    di_node_t di_node;
1071cad31331Smrg    probe_info_t pinfo;
1072cad31331Smrg    struct pci_device_private *devices;
10734f5e7dd7Smrg
1074cad31331Smrg    if (nexus_list != NULL) {
1075cad31331Smrg	return 0;
10764f5e7dd7Smrg    }
10774f5e7dd7Smrg
107849310723Smrg    if ((di_node = di_init("/", DINFOCACHE)) == DI_NODE_NIL) {
10794f5e7dd7Smrg	err = errno;
1080cad31331Smrg	(void) fprintf(stderr, "di_init() failed: %s\n",
1081cad31331Smrg		       strerror(errno));
1082cad31331Smrg	return (err);
1083cad31331Smrg    }
10844f5e7dd7Smrg
1085cad31331Smrg    if ((devices = calloc(INITIAL_NUM_DEVICES,
1086cad31331Smrg			sizeof (struct pci_device_private))) == NULL) {
1087cad31331Smrg	err = errno;
1088cad31331Smrg	di_fini(di_node);
1089cad31331Smrg	return (err);
10904f5e7dd7Smrg    }
10914f5e7dd7Smrg
1092cad31331Smrg#ifdef __sparc
1093cad31331Smrg    if ((di_phdl = di_prom_init()) == DI_PROM_HANDLE_NIL)
1094cad31331Smrg	(void) fprintf(stderr, "di_prom_init failed: %s\n", strerror(errno));
1095cad31331Smrg#endif
1096cad31331Smrg
1097cad31331Smrg    pinfo.num_allocated_elems = INITIAL_NUM_DEVICES;
1098cad31331Smrg    pinfo.num_devices = 0;
1099cad31331Smrg    pinfo.devices = devices;
110086ea1d58Smrg#ifdef __sparc
110186ea1d58Smrg    nexus_count = 0;
110286ea1d58Smrg#endif
1103cad31331Smrg    (void) di_walk_minor(di_node, DDI_NT_REGACC, 0, &pinfo, probe_nexus_node);
1104cad31331Smrg
1105cad31331Smrg    di_fini(di_node);
1106cad31331Smrg
1107cad31331Smrg    if ((pci_sys = calloc(1, sizeof (struct pci_system))) == NULL) {
1108cad31331Smrg	err = errno;
1109cad31331Smrg	free(devices);
1110cad31331Smrg	return (err);
1111cad31331Smrg    }
1112cad31331Smrg
1113cad31331Smrg    pci_sys->methods = &solx_devfs_methods;
1114cad31331Smrg    pci_sys->devices = pinfo.devices;
1115cad31331Smrg    pci_sys->num_devices = pinfo.num_devices;
1116cad31331Smrg
1117cad31331Smrg    return (err);
11184f5e7dd7Smrg}
1119