solx_devfs.c revision 49310723
1/*
2 * (C) Copyright IBM Corporation 2006
3 * Copyright (c) 2007, 2009, 2011, 2012, 2016 Oracle and/or its affiliates.
4 * All Rights Reserved.
5 *
6 * Permission is hereby granted, free of charge, to any person obtaining a
7 * copy of this software and associated documentation files (the "Software"),
8 * to deal in the Software without restriction, including without limitation
9 * on the rights to use, copy, modify, merge, publish, distribute, sub
10 * license, and/or sell copies of the Software, and to permit persons to whom
11 * the Software is furnished to do so, subject to the following conditions:
12 *
13 * The above copyright notice and this permission notice (including the next
14 * paragraph) shall be included in all copies or substantial portions of the
15 * Software.
16 *
17 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19 * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT.  IN NO EVENT SHALL
20 * IBM AND/OR THEIR SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
22 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
23 * DEALINGS IN THE SOFTWARE.
24 */
25/*
26 * Solaris devfs interfaces
27 */
28#ifdef HAVE_CONFIG_H
29#include "config.h"
30#endif
31
32#include <stdlib.h>
33#include <strings.h>
34#include <stdio.h>
35#include <unistd.h>
36#include <sys/types.h>
37#include <fcntl.h>
38#include <sys/mman.h>
39#include <errno.h>
40#include <sys/pci.h>
41#include <libdevinfo.h>
42#include "pci_tools.h"
43
44#ifdef __x86
45# include <sys/sysi86.h>
46# include <sys/psw.h>
47#endif
48
49#include "pciaccess.h"
50#include "pciaccess_private.h"
51
52/* #define DEBUG */
53
54#define	INITIAL_NUM_DEVICES	256
55#define	CELL_NUMS_1275	(sizeof(pci_regspec_t) / sizeof(uint_t))
56
57typedef struct i_devnode {
58    uint8_t bus;
59    uint8_t dev;
60    uint8_t func;
61    di_node_t node;
62} i_devnode_t;
63
64typedef struct nexus {
65    int first_bus;
66    int last_bus;
67    int domain;
68    char *path;			/* for open */
69    char *dev_path;
70    struct nexus *next;
71} nexus_t;
72
73typedef struct probe_info {
74    volatile size_t num_allocated_elems;
75    volatile size_t num_devices;
76    struct pci_device_private * volatile devices;
77} probe_info_t;
78
79typedef struct probe_args {
80    probe_info_t *pinfo;
81    nexus_t *nexus;
82    int ret;
83} probe_args_t;
84
85typedef struct property_info {
86    const char *name;
87    int value;
88} property_info_t;
89
90static nexus_t *nexus_list = NULL;
91#if !defined(__sparc)
92static int xsvc_fd = -1;
93#endif
94
95#ifdef __sparc
96static di_prom_handle_t di_phdl;
97static size_t  nexus_count = 0;
98#endif
99
100/*
101 * Read config space in native processor endianness.  Endian-neutral
102 * processing can then take place.  On big endian machines, MSB and LSB
103 * of little endian data end up switched if read as little endian.
104 * They are in correct order if read as big endian.
105 */
106#if defined(__sparc)
107# define NATIVE_ENDIAN	PCITOOL_ACC_ATTR_ENDN_BIG
108#elif defined(__x86)
109# define NATIVE_ENDIAN	PCITOOL_ACC_ATTR_ENDN_LTL
110#else
111# error "ISA is neither __sparc nor __x86"
112#endif
113
114#ifdef __sparc
115#define MAPPING_DEV_PATH(dev)	 (((struct pci_device_private *) dev)->device_string)
116#endif
117
118static nexus_t *
119find_nexus_for_bus( int domain, int bus )
120{
121    nexus_t *nexus;
122
123    for (nexus = nexus_list ; nexus != NULL ; nexus = nexus->next) {
124	if ((domain == nexus->domain) &&
125	    (bus >= nexus->first_bus) && (bus <= nexus->last_bus)) {
126	    return nexus;
127	}
128    }
129    return NULL;
130}
131
132/*
133 * Release all the resources
134 * Solaris version
135 */
136static void
137pci_system_solx_devfs_destroy( void )
138{
139    /*
140     * The memory allocated for pci_sys & devices in create routines
141     * will be freed in pci_system_cleanup.
142     * Need to free system-specific allocations here.
143     */
144    nexus_t *nexus, *next;
145
146    for (nexus = nexus_list ; nexus != NULL ; nexus = next) {
147	next = nexus->next;
148	free(nexus->path);
149	free(nexus->dev_path);
150	free(nexus);
151    }
152    nexus_list = NULL;
153
154#ifdef __sparc
155    if (di_phdl != DI_PROM_HANDLE_NIL)
156	(void) di_prom_fini(di_phdl);
157#else
158    if (xsvc_fd >= 0) {
159	close(xsvc_fd);
160	xsvc_fd = -1;
161    }
162#endif
163}
164
165
166#ifdef __sparc
167/*
168 * Release resources per device
169 */
170static void
171pci_system_solx_devfs_destroy_device( struct pci_device *dev )
172{
173   if (MAPPING_DEV_PATH(dev))
174	di_devfs_path_free((char *) MAPPING_DEV_PATH(dev));
175}
176#endif
177
178
179static int
180probe_device_node(di_node_t node, void *arg)
181{
182    int *retbuf = NULL;
183    int len = 0, i;
184    struct pci_device	*pci_base;
185    probe_info_t *pinfo = ((probe_args_t *)arg)->pinfo;
186    nexus_t *nexus = ((probe_args_t *)arg)->nexus;
187    property_info_t property_list[] = {
188        { "class-code", 0 },
189        { "device-id", 0 },
190        { "vendor-id", 0 },
191        { "revision-id", 0},
192        { "subsystem-vendor-id", 0},
193        { "subsystem-id", 0},
194    };
195#define NUM_PROPERTIES		sizeof(property_list)/sizeof(property_info_t)
196
197    len = di_prop_lookup_ints(DDI_DEV_T_ANY, node, "reg", &retbuf);
198
199#ifdef __sparc
200    if ((len <= 0) && di_phdl)
201	len = di_prom_prop_lookup_ints(di_phdl, node, "reg", &retbuf);
202#endif
203
204    /* Exclude usb devices */
205    if (len < 5) {
206	return DI_WALK_CONTINUE;
207    }
208
209    pci_base = &pinfo->devices[pinfo->num_devices].base;
210
211    pci_base->domain = nexus->domain;
212    pci_base->bus = PCI_REG_BUS_G(retbuf[0]);
213    pci_base->dev = PCI_REG_DEV_G(retbuf[0]);
214    pci_base->func  = PCI_REG_FUNC_G(retbuf[0]);
215
216    /* Get property values */
217    for (i = 0; i < NUM_PROPERTIES; i++) {
218	len = di_prop_lookup_ints(DDI_DEV_T_ANY, node,
219		property_list[i].name, &retbuf);
220#ifdef __sparc
221	if ((len <= 0) && di_phdl)
222	    len = di_prom_prop_lookup_ints(di_phdl, node,
223		property_list[i].name, &retbuf);
224#endif
225
226	if (len > 0)
227	    property_list[i].value = retbuf[0];
228	else {
229	    /* a device must have property "class-code", "device-id", "vendor-id" */
230	    if (i < 3)
231		return DI_WALK_CONTINUE;
232#ifdef DEBUG
233	    fprintf(stderr, "cannot get property \"%s\" for nexus = %s :\n",
234		property_list[i].name, nexus->path);
235	    fprintf(stderr, "	domain = %x, busno = %x, devno = %x, funcno = %x\n",
236		pci_base->domain, pci_base->bus, pci_base->dev, pci_base->func);
237#endif
238	}
239    }
240
241    if ((property_list[1].value == 0) && (property_list[2].value == 0))
242	return DI_WALK_CONTINUE;
243
244    pci_base->device_class = property_list[0].value;
245    pci_base->device_id = property_list[1].value;
246    pci_base->vendor_id = property_list[2].value;
247    pci_base->revision = property_list[3].value;
248    pci_base->subvendor_id = property_list[4].value;
249    pci_base->subdevice_id = property_list[5].value;
250
251#ifdef DEBUG
252    fprintf(stderr,
253	    "nexus = %s, domain = %x, busno = %x, devno = %x, funcno = %x\n",
254	    nexus->path, pci_base->domain, pci_base->bus, pci_base->dev, pci_base->func);
255#endif
256
257    pinfo->num_devices++;
258    if (pinfo->num_devices == pinfo->num_allocated_elems) {
259	struct pci_device_private *new_devs;
260	size_t new_num_elems = pinfo->num_allocated_elems * 2;
261
262	new_devs = realloc(pinfo->devices,
263	new_num_elems * sizeof (struct pci_device_private));
264	if (new_devs == NULL) {
265	    (void) fprintf(stderr,
266	           "Error allocating memory for PCI devices:"
267		   " %s\n discarding additional devices\n",
268		   strerror(errno));
269	    ((probe_args_t *)arg)->ret = 1;
270	    return (DI_WALK_TERMINATE);
271	}
272	(void) memset(&new_devs[pinfo->num_devices], 0,
273		pinfo->num_allocated_elems *
274		sizeof (struct pci_device_private));
275	pinfo->num_allocated_elems = new_num_elems;
276	pinfo->devices = new_devs;
277    }
278
279    return (DI_WALK_CONTINUE);
280}
281/*
282 * This function is called from di_walk_minor() when any PROBE is processed
283 */
284static int
285probe_nexus_node(di_node_t di_node, di_minor_t minor, void *arg)
286{
287    probe_info_t *pinfo = (probe_info_t *)arg;
288    char *nexus_name, *nexus_dev_path;
289    nexus_t *nexus;
290    int fd;
291    char nexus_path[MAXPATHLEN];
292
293    di_prop_t prop;
294    char *strings;
295    int *ints;
296    int numval;
297    int pci_node = 0;
298    int first_bus = 0, last_bus = PCI_REG_BUS_G(PCI_REG_BUS_M);
299    int domain = 0;
300#ifdef __sparc
301    int bus_range_found = 0;
302    int device_type_found = 0;
303    di_prom_prop_t prom_prop;
304#endif
305
306#ifdef DEBUG
307    nexus_name = di_devfs_minor_path(minor);
308    fprintf(stderr, "-- device name: %s\n", nexus_name);
309    di_devfs_path_free(nexus_name);
310#endif
311
312    for (prop = di_prop_next(di_node, NULL); prop != NULL;
313	 prop = di_prop_next(di_node, prop)) {
314
315	const char *prop_name = di_prop_name(prop);
316
317#ifdef DEBUG
318	fprintf(stderr, "   property: %s\n", prop_name);
319#endif
320
321	if (strcmp(prop_name, "device_type") == 0) {
322	    numval = di_prop_strings(prop, &strings);
323	    if (numval == 1) {
324		if (strncmp(strings, "pci", 3) != 0)
325		    /* not a PCI node, bail */
326		    return (DI_WALK_CONTINUE);
327		else {
328		    pci_node = 1;
329#ifdef __sparc
330		    device_type_found =  1;
331#endif
332		}
333	    }
334	}
335	else if (strcmp(prop_name, "class-code") == 0) {
336	    /* not a root bus node, bail */
337	    return (DI_WALK_CONTINUE);
338	}
339	else if (strcmp(prop_name, "bus-range") == 0) {
340	    numval = di_prop_ints(prop, &ints);
341	    if (numval == 2) {
342		first_bus = ints[0];
343		last_bus = ints[1];
344#ifdef __sparc
345		bus_range_found = 1;
346#endif
347	    }
348	}
349#ifdef __sparc
350	domain = nexus_count;
351#else
352	else if (strcmp(prop_name, "pciseg") == 0) {
353	    numval = di_prop_ints(prop, &ints);
354	    if (numval == 1) {
355		domain = ints[0];
356	    }
357	}
358#endif
359    }
360
361#ifdef __sparc
362    if ((!device_type_found) && di_phdl) {
363	numval = di_prom_prop_lookup_strings(di_phdl, di_node,
364	    "device_type", &strings);
365	if (numval == 1) {
366	    if (strncmp(strings, "pci", 3) != 0)
367		return (DI_WALK_CONTINUE);
368	    else
369		pci_node = 1;
370	}
371    }
372
373    if ((!bus_range_found) && di_phdl) {
374	numval = di_prom_prop_lookup_ints(di_phdl, di_node,
375	    "bus-range", &ints);
376	if (numval == 2) {
377	    first_bus = ints[0];
378	    last_bus = ints[1];
379	}
380    }
381#endif
382
383    if (pci_node != 1)
384	return (DI_WALK_CONTINUE);
385
386    /* we have a PCI root bus node. */
387    nexus = calloc(1, sizeof(nexus_t));
388    if (nexus == NULL) {
389	(void) fprintf(stderr, "Error allocating memory for nexus: %s\n",
390		       strerror(errno));
391	return (DI_WALK_TERMINATE);
392    }
393    nexus->first_bus = first_bus;
394    nexus->last_bus = last_bus;
395    nexus->domain = domain;
396
397#ifdef __sparc
398    nexus_count++;
399#endif
400
401    nexus_name = di_devfs_minor_path(minor);
402    if (nexus_name == NULL) {
403	(void) fprintf(stderr, "Error getting nexus path: %s\n",
404		       strerror(errno));
405	free(nexus);
406	return (DI_WALK_CONTINUE);
407    }
408
409    snprintf(nexus_path, sizeof(nexus_path), "/devices%s", nexus_name);
410    di_devfs_path_free(nexus_name);
411
412#ifdef DEBUG
413    fprintf(stderr, "nexus = %s, bus-range = %d - %d\n",
414	    nexus_path, first_bus, last_bus);
415#endif
416
417    if ((fd = open(nexus_path, O_RDWR | O_CLOEXEC)) >= 0) {
418	probe_args_t args;
419
420	nexus->path = strdup(nexus_path);
421	nexus_dev_path = di_devfs_path(di_node);
422	nexus->dev_path = strdup(nexus_dev_path);
423	di_devfs_path_free(nexus_dev_path);
424
425	/* Walk through devices under the rnode */
426	args.pinfo = pinfo;
427	args.nexus = nexus;
428	args.ret = 0;
429
430	(void) di_walk_node(di_node, DI_WALK_CLDFIRST, (void *)&args, probe_device_node);
431
432	close(fd);
433
434	if (args.ret) {
435	    free(nexus->path);
436	    free(nexus->dev_path);
437	    free(nexus);
438	    return (DI_WALK_TERMINATE);
439	}
440
441	nexus->next = nexus_list;
442	nexus_list = nexus;
443    } else {
444	(void) fprintf(stderr, "Error opening %s: %s\n",
445		       nexus_path, strerror(errno));
446	free(nexus);
447    }
448
449    return DI_WALK_CONTINUE;
450}
451
452static int
453find_target_node(di_node_t node, void *arg)
454{
455    int *regbuf = NULL;
456    int len = 0;
457    uint32_t busno, funcno, devno;
458    i_devnode_t *devnode = (i_devnode_t *)arg;
459
460    /*
461     * Test the property functions, only for testing
462     */
463    /*
464    void *prop = DI_PROP_NIL;
465
466    (void) fprintf(stderr, "start of node 0x%x\n", node->nodeid);
467    while ((prop = di_prop_hw_next(node, prop)) != DI_PROP_NIL) {
468	int i;
469	(void) fprintf(stderr, "name=%s: ", di_prop_name(prop));
470	len = 0;
471	if (!strcmp(di_prop_name(prop), "reg")) {
472	    len = di_prop_ints(prop, &regbuf);
473	}
474	for (i = 0; i < len; i++) {
475	    fprintf(stderr, "0x%0x.", regbuf[i]);
476	}
477	fprintf(stderr, "\n");
478    }
479    (void) fprintf(stderr, "end of node 0x%x\n", node->nodeid);
480    */
481
482    len = di_prop_lookup_ints(DDI_DEV_T_ANY, node, "reg", &regbuf);
483
484#ifdef __sparc
485    if ((len <= 0) && di_phdl)
486	len = di_prom_prop_lookup_ints(di_phdl, node, "reg", &regbuf);
487#endif
488
489    if (len <= 0) {
490#ifdef DEBUG
491	fprintf(stderr, "error = %x\n", errno);
492	fprintf(stderr, "can not find assigned-address\n");
493#endif
494	return (DI_WALK_CONTINUE);
495    }
496
497    busno = PCI_REG_BUS_G(regbuf[0]);
498    devno = PCI_REG_DEV_G(regbuf[0]);
499    funcno = PCI_REG_FUNC_G(regbuf[0]);
500
501    if ((busno == devnode->bus) &&
502	(devno == devnode->dev) &&
503	(funcno == devnode->func)) {
504	devnode->node = node;
505
506	return (DI_WALK_TERMINATE);
507    }
508
509    return (DI_WALK_CONTINUE);
510}
511
512/*
513 * Solaris version
514 */
515static int
516pci_device_solx_devfs_probe( struct pci_device * dev )
517{
518    int err = 0;
519    di_node_t rnode = DI_NODE_NIL;
520    i_devnode_t args = { 0, 0, 0, DI_NODE_NIL };
521    int *regbuf;
522    pci_regspec_t *reg;
523    int i;
524    int len = 0;
525    uint ent = 0;
526    struct pci_device_private *priv =
527	(struct pci_device_private *) dev;
528    nexus_t *nexus;
529
530    if ( (nexus = find_nexus_for_bus(dev->domain, dev->bus)) == NULL )
531	return ENODEV;
532
533    pci_device_cfg_read_u8(dev, &priv->header_type, PCI_CONF_HEADER);
534
535    pci_device_cfg_read_u8(dev, (uint8_t *)&dev->irq, PCI_CONF_ILINE);
536
537    /*
538     * starting to find if it is MEM/MEM64/IO
539     * using libdevinfo
540     */
541    if ((rnode = di_init(nexus->dev_path, DINFOCACHE)) == DI_NODE_NIL) {
542	err = errno;
543	(void) fprintf(stderr, "di_init failed: %s\n", strerror(errno));
544    } else {
545	args.bus = dev->bus;
546	args.dev = dev->dev;
547	args.func = dev->func;
548	(void) di_walk_node(rnode, DI_WALK_CLDFIRST,
549		(void *)&args, find_target_node);
550    }
551
552    if (args.node != DI_NODE_NIL) {
553	int *prop;
554#ifdef __sparc
555	di_minor_t minor;
556#endif
557
558	priv->is_primary = 0;
559
560#ifdef __sparc
561	if (minor = di_minor_next(args.node, DI_MINOR_NIL))
562	    MAPPING_DEV_PATH(dev) = di_devfs_minor_path (minor);
563	else
564	    MAPPING_DEV_PATH(dev) = NULL;
565#endif
566
567	if (di_prop_lookup_ints(DDI_DEV_T_ANY, args.node,
568				"primary-controller", &prop) >= 1) {
569	    if (prop[0])
570		priv->is_primary = 1;
571	}
572
573	/*
574	 * It will succeed for sure, because it was
575	 * successfully called in find_target_node
576	 */
577	len = di_prop_lookup_ints(DDI_DEV_T_ANY, args.node,
578				  "assigned-addresses",
579				  &regbuf);
580
581#ifdef __sparc
582	if ((len <= 0) && di_phdl) {
583	    len = di_prom_prop_lookup_ints(di_phdl, args.node,
584				"assigned-addresses", &regbuf);
585	}
586#endif
587    }
588
589    if (len <= 0)
590	goto cleanup;
591
592    /*
593     * Each BAR address get its own region slot in sequence.
594     * 32 bit BAR:
595     * BAR 0x10 -> slot0, BAR 0x14 -> slot1...
596     * 64 bit BAR:
597     * BAR 0x10 -> slot0, BAR 0x18 -> slot2...,
598     * slot1 is part of BAR 0x10
599     * Linux give two region slot for 64 bit address.
600     */
601    for (i = 0; i < len; i = i + (int)CELL_NUMS_1275) {
602
603	reg = (pci_regspec_t *)&regbuf[i];
604	ent = reg->pci_phys_hi & 0xff;
605
606	if (ent > PCI_CONF_ROM) {
607	    fprintf(stderr, "error ent = %d\n", ent);
608	    break;
609	}
610	/*
611	 * G35 broken in BAR0
612	 */
613	if (ent < PCI_CONF_BASE0) {
614	    /*
615	     * VGA resource here and ignore it
616	     */
617	    break;
618	} else if (ent == PCI_CONF_ROM) {
619	    priv->rom_base = reg->pci_phys_low |
620		((uint64_t)reg->pci_phys_mid << 32);
621	    dev->rom_size = reg->pci_size_low;
622	} else {
623	    ent = (ent - PCI_CONF_BASE0) >> 2;
624	    /*
625	     * non relocatable resource is excluded
626	     * such like 0xa0000, 0x3b0. If it is met,
627	     * the loop is broken;
628	     */
629	    if (!PCI_REG_REG_G(reg->pci_phys_hi))
630		break;
631
632	    if (reg->pci_phys_hi & PCI_PREFETCH_B) {
633		dev->regions[ent].is_prefetchable = 1;
634	    }
635
636
637	    dev->regions[ent].base_addr = reg->pci_phys_low |
638		((uint64_t)reg->pci_phys_mid << 32);
639	    dev->regions[ent].size = reg->pci_size_low |
640		((uint64_t)reg->pci_size_hi << 32);
641
642	    switch (reg->pci_phys_hi & PCI_REG_ADDR_M) {
643		case PCI_ADDR_IO:
644		    dev->regions[ent].is_IO = 1;
645		    break;
646		case PCI_ADDR_MEM32:
647		    break;
648		case PCI_ADDR_MEM64:
649		    dev->regions[ent].is_64 = 1;
650		    /*
651		     * Skip one slot for 64 bit address
652		     */
653		    break;
654	    }
655	}
656    }
657
658  cleanup:
659    if (rnode != DI_NODE_NIL) {
660	di_fini(rnode);
661    }
662    return (err);
663}
664
665/**
666 * Map a memory region for a device using /dev/xsvc (x86) or fb device (sparc)
667 *
668 * \param dev   Device whose memory region is to be mapped.
669 * \param map   Parameters of the mapping that is to be created.
670 *
671 * \return
672 * Zero on success or an \c errno value on failure.
673 */
674static int
675pci_device_solx_devfs_map_range(struct pci_device *dev,
676				struct pci_device_mapping *map)
677{
678    const int prot = ((map->flags & PCI_DEV_MAP_FLAG_WRITABLE) != 0)
679			? (PROT_READ | PROT_WRITE) : PROT_READ;
680    int err = 0;
681
682    const char *map_dev;
683    int		map_fd;
684
685#ifdef __sparc
686    char	map_dev_buf[128];
687
688    if (MAPPING_DEV_PATH(dev)) {
689	snprintf(map_dev_buf, sizeof (map_dev_buf), "%s%s",
690		 "/devices", MAPPING_DEV_PATH(dev));
691	map_dev = map_dev_buf;
692    }
693    else
694	map_dev = "/dev/fb0";
695
696    map_fd = -1;
697#else
698    /*
699     * Still uses xsvc to do the user space mapping on x86/x64,
700     * caches open fd across multiple calls.
701     */
702    map_dev = "/dev/xsvc";
703    map_fd = xsvc_fd;
704#endif
705
706    if (map_fd < 0) {
707	if ((map_fd = open(map_dev, O_RDWR | O_CLOEXEC)) < 0) {
708	    err = errno;
709	    (void) fprintf(stderr, "can not open %s: %s\n", map_dev,
710			   strerror(errno));
711	    return err;
712	}
713#ifndef __sparc
714        xsvc_fd = map_fd;
715#endif
716    }
717
718    map->memory = mmap(NULL, map->size, prot, MAP_SHARED, map_fd, map->base);
719    if (map->memory == MAP_FAILED) {
720	err = errno;
721
722	(void) fprintf(stderr, "map rom region =%llx failed: %s\n",
723		       (unsigned long long) map->base, strerror(errno));
724    }
725
726#ifdef __sparc
727    close (map_fd);
728#endif
729
730    return err;
731}
732
733/*
734 * Solaris version: read the VGA ROM data
735 */
736static int
737pci_device_solx_devfs_read_rom( struct pci_device * dev, void * buffer )
738{
739    int err;
740    struct pci_device_mapping prom = {
741	.base = 0xC0000,
742	.size = 0x10000,
743	.flags = 0
744    };
745    struct pci_device_private *priv =
746	(struct pci_device_private *) dev;
747
748    if (priv->rom_base) {
749	prom.base = priv->rom_base;
750	prom.size = dev->rom_size;
751    }
752
753    err = pci_device_solx_devfs_map_range(dev, &prom);
754    if (err == 0) {
755	(void) bcopy(prom.memory, buffer, dev->rom_size);
756
757	if (munmap(prom.memory, prom.size) == -1) {
758	    err = errno;
759	}
760    }
761    return err;
762}
763
764/*
765 * solaris version: Read the configurations space of the devices
766 */
767static int
768pci_device_solx_devfs_read( struct pci_device * dev, void * data,
769			     pciaddr_t offset, pciaddr_t size,
770			     pciaddr_t * bytes_read )
771{
772    pcitool_reg_t cfg_prg;
773    int err = 0;
774    unsigned int i = 0;
775    nexus_t *nexus;
776    int fd;
777
778    nexus = find_nexus_for_bus(dev->domain, dev->bus);
779
780    *bytes_read = 0;
781
782    if ( nexus == NULL ) {
783	return ENODEV;
784    }
785
786    cfg_prg.offset = offset;
787    cfg_prg.acc_attr = PCITOOL_ACC_ATTR_SIZE_1 + NATIVE_ENDIAN;
788    cfg_prg.bus_no = dev->bus;
789    cfg_prg.dev_no = dev->dev;
790    cfg_prg.func_no = dev->func;
791    cfg_prg.barnum = 0;
792    cfg_prg.user_version = PCITOOL_USER_VERSION;
793
794    if ((fd = open(nexus->path, O_RDWR | O_CLOEXEC)) < 0)
795	return ENOENT;
796
797    for (i = 0; i < size; i += PCITOOL_ACC_ATTR_SIZE(PCITOOL_ACC_ATTR_SIZE_1))
798    {
799	cfg_prg.offset = offset + i;
800
801	if ((err = ioctl(fd, PCITOOL_DEVICE_GET_REG, &cfg_prg)) != 0) {
802	    fprintf(stderr, "read bdf<%s,%x,%x,%x,%llx> config space failure\n",
803		    nexus->path,
804		    cfg_prg.bus_no,
805		    cfg_prg.dev_no,
806		    cfg_prg.func_no,
807		    (unsigned long long) cfg_prg.offset);
808	    fprintf(stderr, "Failure cause = %x\n", err);
809	    break;
810	}
811
812	((uint8_t *)data)[i] = (uint8_t)cfg_prg.data;
813	/*
814	 * DWORDS Offset or bytes Offset ??
815	 */
816    }
817    *bytes_read = i;
818
819    close(fd);
820
821    return (err);
822}
823
824/*
825 * Solaris version
826 */
827static int
828pci_device_solx_devfs_write( struct pci_device * dev, const void * data,
829			     pciaddr_t offset, pciaddr_t size,
830			     pciaddr_t * bytes_written )
831{
832    pcitool_reg_t cfg_prg;
833    int err = 0;
834    int cmd;
835    nexus_t *nexus;
836    int fd;
837
838    nexus = find_nexus_for_bus(dev->domain, dev->bus);
839
840    if ( bytes_written != NULL ) {
841	*bytes_written = 0;
842    }
843
844    if ( nexus == NULL ) {
845	return ENODEV;
846    }
847
848    cfg_prg.offset = offset;
849    switch (size) {
850        case 1:
851	    cfg_prg.acc_attr = PCITOOL_ACC_ATTR_SIZE_1 + NATIVE_ENDIAN;
852	    cfg_prg.data = *((const uint8_t *)data);
853	    break;
854        case 2:
855	    cfg_prg.acc_attr = PCITOOL_ACC_ATTR_SIZE_2 + NATIVE_ENDIAN;
856	    cfg_prg.data = *((const uint16_t *)data);
857	    break;
858        case 4:
859	    cfg_prg.acc_attr = PCITOOL_ACC_ATTR_SIZE_4 + NATIVE_ENDIAN;
860	    cfg_prg.data = *((const uint32_t *)data);
861	    break;
862        case 8:
863	    cfg_prg.acc_attr = PCITOOL_ACC_ATTR_SIZE_8 + NATIVE_ENDIAN;
864	    cfg_prg.data = *((const uint64_t *)data);
865	    break;
866        default:
867	    return EINVAL;
868    }
869    cfg_prg.bus_no = dev->bus;
870    cfg_prg.dev_no = dev->dev;
871    cfg_prg.func_no = dev->func;
872    cfg_prg.barnum = 0;
873    cfg_prg.user_version = PCITOOL_USER_VERSION;
874
875    /*
876     * Check if this device is bridge device.
877     * If it is, it is also a nexus node???
878     * It seems that there is no explicit
879     * PCI nexus device for X86, so not applicable
880     * from pcitool_bus_reg_ops in pci_tools.c
881     */
882    cmd = PCITOOL_DEVICE_SET_REG;
883
884    if ((fd = open(nexus->path, O_RDWR | O_CLOEXEC)) < 0)
885	return ENOENT;
886
887    if ((err = ioctl(fd, cmd, &cfg_prg)) != 0) {
888	close(fd);
889	return (err);
890    }
891    *bytes_written = size;
892
893    close(fd);
894
895    return (err);
896}
897
898static int pci_device_solx_devfs_boot_vga(struct pci_device *dev)
899{
900    struct pci_device_private *priv =
901	(struct pci_device_private *) dev;
902
903    return (priv->is_primary);
904
905}
906
907static struct pci_io_handle *
908pci_device_solx_devfs_open_legacy_io(struct pci_io_handle *ret,
909				     struct pci_device *dev,
910				     pciaddr_t base, pciaddr_t size)
911{
912#ifdef __x86
913    if (sysi86(SI86V86, V86SC_IOPL, PS_IOPL) == 0) {
914	ret->base = base;
915	ret->size = size;
916	ret->is_legacy = 1;
917	return ret;
918    }
919#endif
920    return NULL;
921}
922
923static uint32_t
924pci_device_solx_devfs_read32(struct pci_io_handle *handle, uint32_t reg)
925{
926#ifdef __x86
927    uint16_t port = (uint16_t) (handle->base + reg);
928    uint32_t ret;
929    __asm__ __volatile__("inl %1,%0":"=a"(ret):"d"(port));
930    return ret;
931#else
932    return *(uint32_t *)((uintptr_t)handle->memory + reg);
933#endif
934}
935
936static uint16_t
937pci_device_solx_devfs_read16(struct pci_io_handle *handle, uint32_t reg)
938{
939#ifdef __x86
940    uint16_t port = (uint16_t) (handle->base + reg);
941    uint16_t ret;
942    __asm__ __volatile__("inw %1,%0":"=a"(ret):"d"(port));
943    return ret;
944#else
945    return *(uint16_t *)((uintptr_t)handle->memory + reg);
946#endif
947}
948
949static uint8_t
950pci_device_solx_devfs_read8(struct pci_io_handle *handle, uint32_t reg)
951{
952#ifdef __x86
953    uint16_t port = (uint16_t) (handle->base + reg);
954    uint8_t ret;
955    __asm__ __volatile__("inb %1,%0":"=a"(ret):"d"(port));
956    return ret;
957#else
958    return *(uint8_t *)((uintptr_t)handle->memory + reg);
959#endif
960}
961
962static void
963pci_device_solx_devfs_write32(struct pci_io_handle *handle, uint32_t reg,
964    uint32_t data)
965{
966#ifdef __x86
967      uint16_t port = (uint16_t) (handle->base + reg);
968      __asm__ __volatile__("outl %0,%1"::"a"(data), "d"(port));
969#else
970      *(uint16_t *)((uintptr_t)handle->memory + reg) = data;
971#endif
972}
973
974static void
975pci_device_solx_devfs_write16(struct pci_io_handle *handle, uint32_t reg,
976    uint16_t data)
977{
978#ifdef __x86
979      uint16_t port = (uint16_t) (handle->base + reg);
980      __asm__ __volatile__("outw %0,%1"::"a"(data), "d"(port));
981#else
982    *(uint8_t *)((uintptr_t)handle->memory + reg) = data;
983#endif
984}
985
986static void
987pci_device_solx_devfs_write8(struct pci_io_handle *handle, uint32_t reg,
988    uint8_t data)
989{
990#ifdef __x86
991      uint16_t port = (uint16_t) (handle->base + reg);
992      __asm__ __volatile__("outb %0,%1"::"a"(data), "d"(port));
993#else
994      *(uint32_t *)((uintptr_t)handle->memory + reg) = data;
995#endif
996}
997
998static int
999pci_device_solx_devfs_map_legacy(struct pci_device *dev, pciaddr_t base,
1000				 pciaddr_t size, unsigned map_flags,
1001				 void **addr)
1002{
1003    int err;
1004    struct pci_device_mapping map = {
1005	.base = base,
1006	.size = size,
1007	.flags = map_flags,
1008    };
1009
1010    err = pci_device_solx_devfs_map_range(dev, &map);
1011    if (err == 0)
1012	*addr = map.memory;
1013    return err;
1014}
1015
1016static int
1017pci_device_solx_devfs_unmap_legacy(struct pci_device *dev,
1018				   void *addr, pciaddr_t size)
1019{
1020    struct pci_device_mapping map = {
1021	.memory = addr,
1022	.size = size,
1023    };
1024
1025    return pci_device_generic_unmap_range(dev, &map);
1026}
1027
1028static const struct pci_system_methods solx_devfs_methods = {
1029    .destroy = pci_system_solx_devfs_destroy,
1030#ifdef __sparc
1031    .destroy_device = pci_system_solx_devfs_destroy_device,
1032#else
1033    .destroy_device = NULL,
1034#endif
1035    .read_rom = pci_device_solx_devfs_read_rom,
1036    .probe = pci_device_solx_devfs_probe,
1037    .map_range = pci_device_solx_devfs_map_range,
1038    .unmap_range = pci_device_generic_unmap_range,
1039
1040    .read = pci_device_solx_devfs_read,
1041    .write = pci_device_solx_devfs_write,
1042
1043    .fill_capabilities = pci_fill_capabilities_generic,
1044    .boot_vga = pci_device_solx_devfs_boot_vga,
1045
1046    .open_legacy_io = pci_device_solx_devfs_open_legacy_io,
1047    .read32 = pci_device_solx_devfs_read32,
1048    .read16 = pci_device_solx_devfs_read16,
1049    .read8 = pci_device_solx_devfs_read8,
1050    .write32 = pci_device_solx_devfs_write32,
1051    .write16 = pci_device_solx_devfs_write16,
1052    .write8 = pci_device_solx_devfs_write8,
1053    .map_legacy = pci_device_solx_devfs_map_legacy,
1054    .unmap_legacy = pci_device_solx_devfs_unmap_legacy,
1055};
1056
1057/*
1058 * Attempt to access PCI subsystem using Solaris's devfs interface.
1059 * Solaris version
1060 */
1061_pci_hidden int
1062pci_system_solx_devfs_create( void )
1063{
1064    int err = 0;
1065    di_node_t di_node;
1066    probe_info_t pinfo;
1067    struct pci_device_private *devices;
1068
1069    if (nexus_list != NULL) {
1070	return 0;
1071    }
1072
1073    if ((di_node = di_init("/", DINFOCACHE)) == DI_NODE_NIL) {
1074	err = errno;
1075	(void) fprintf(stderr, "di_init() failed: %s\n",
1076		       strerror(errno));
1077	return (err);
1078    }
1079
1080    if ((devices = calloc(INITIAL_NUM_DEVICES,
1081			sizeof (struct pci_device_private))) == NULL) {
1082	err = errno;
1083	di_fini(di_node);
1084	return (err);
1085    }
1086
1087#ifdef __sparc
1088    if ((di_phdl = di_prom_init()) == DI_PROM_HANDLE_NIL)
1089	(void) fprintf(stderr, "di_prom_init failed: %s\n", strerror(errno));
1090#endif
1091
1092    pinfo.num_allocated_elems = INITIAL_NUM_DEVICES;
1093    pinfo.num_devices = 0;
1094    pinfo.devices = devices;
1095#ifdef __sparc
1096    nexus_count = 0;
1097#endif
1098    (void) di_walk_minor(di_node, DDI_NT_REGACC, 0, &pinfo, probe_nexus_node);
1099
1100    di_fini(di_node);
1101
1102    if ((pci_sys = calloc(1, sizeof (struct pci_system))) == NULL) {
1103	err = errno;
1104	free(devices);
1105	return (err);
1106    }
1107
1108    pci_sys->methods = &solx_devfs_methods;
1109    pci_sys->devices = pinfo.devices;
1110    pci_sys->num_devices = pinfo.num_devices;
1111
1112    return (err);
1113}
1114