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    if (nexus->domain > 0xffff)
217	pci_base->domain_16 = 0xffff;
218    else
219	pci_base->domain_16 = nexus->domain;
220
221    /* Get property values */
222    for (i = 0; i < NUM_PROPERTIES; i++) {
223	len = di_prop_lookup_ints(DDI_DEV_T_ANY, node,
224		property_list[i].name, &retbuf);
225#ifdef __sparc
226	if ((len <= 0) && di_phdl)
227	    len = di_prom_prop_lookup_ints(di_phdl, node,
228		property_list[i].name, &retbuf);
229#endif
230
231	if (len > 0)
232	    property_list[i].value = retbuf[0];
233	else {
234	    /* a device must have property "class-code", "device-id", "vendor-id" */
235	    if (i < 3)
236		return DI_WALK_CONTINUE;
237#ifdef DEBUG
238	    fprintf(stderr, "cannot get property \"%s\" for nexus = %s :\n",
239		property_list[i].name, nexus->path);
240	    fprintf(stderr, "	domain = %x, busno = %x, devno = %x, funcno = %x\n",
241		pci_base->domain, pci_base->bus, pci_base->dev, pci_base->func);
242#endif
243	}
244    }
245
246    if ((property_list[1].value == 0) && (property_list[2].value == 0))
247	return DI_WALK_CONTINUE;
248
249    pci_base->device_class = property_list[0].value;
250    pci_base->device_id = property_list[1].value;
251    pci_base->vendor_id = property_list[2].value;
252    pci_base->revision = property_list[3].value;
253    pci_base->subvendor_id = property_list[4].value;
254    pci_base->subdevice_id = property_list[5].value;
255
256#ifdef DEBUG
257    fprintf(stderr,
258	    "nexus = %s, domain = %x, busno = %x, devno = %x, funcno = %x\n",
259	    nexus->path, pci_base->domain, pci_base->bus, pci_base->dev, pci_base->func);
260#endif
261
262    pinfo->num_devices++;
263    if (pinfo->num_devices == pinfo->num_allocated_elems) {
264	struct pci_device_private *new_devs;
265	size_t new_num_elems = pinfo->num_allocated_elems * 2;
266
267	new_devs = realloc(pinfo->devices,
268	new_num_elems * sizeof (struct pci_device_private));
269	if (new_devs == NULL) {
270	    (void) fprintf(stderr,
271	           "Error allocating memory for PCI devices:"
272		   " %s\n discarding additional devices\n",
273		   strerror(errno));
274	    ((probe_args_t *)arg)->ret = 1;
275	    return (DI_WALK_TERMINATE);
276	}
277	(void) memset(&new_devs[pinfo->num_devices], 0,
278		pinfo->num_allocated_elems *
279		sizeof (struct pci_device_private));
280	pinfo->num_allocated_elems = new_num_elems;
281	pinfo->devices = new_devs;
282    }
283
284    return (DI_WALK_CONTINUE);
285}
286/*
287 * This function is called from di_walk_minor() when any PROBE is processed
288 */
289static int
290probe_nexus_node(di_node_t di_node, di_minor_t minor, void *arg)
291{
292    probe_info_t *pinfo = (probe_info_t *)arg;
293    char *nexus_name, *nexus_dev_path;
294    nexus_t *nexus;
295    int fd;
296    char nexus_path[MAXPATHLEN];
297
298    di_prop_t prop;
299    char *strings;
300    int *ints;
301    int numval;
302    int pci_node = 0;
303    int first_bus = 0, last_bus = PCI_REG_BUS_G(PCI_REG_BUS_M);
304    int domain = 0;
305#ifdef __sparc
306    int bus_range_found = 0;
307    int device_type_found = 0;
308    di_prom_prop_t prom_prop;
309#endif
310
311#ifdef DEBUG
312    nexus_name = di_devfs_minor_path(minor);
313    fprintf(stderr, "-- device name: %s\n", nexus_name);
314    di_devfs_path_free(nexus_name);
315#endif
316
317    for (prop = di_prop_next(di_node, NULL); prop != NULL;
318	 prop = di_prop_next(di_node, prop)) {
319
320	const char *prop_name = di_prop_name(prop);
321
322#ifdef DEBUG
323	fprintf(stderr, "   property: %s\n", prop_name);
324#endif
325
326	if (strcmp(prop_name, "device_type") == 0) {
327	    numval = di_prop_strings(prop, &strings);
328	    if (numval == 1) {
329		if (strncmp(strings, "pci", 3) != 0)
330		    /* not a PCI node, bail */
331		    return (DI_WALK_CONTINUE);
332		else {
333		    pci_node = 1;
334#ifdef __sparc
335		    device_type_found =  1;
336#endif
337		}
338	    }
339	}
340	else if (strcmp(prop_name, "class-code") == 0) {
341	    /* not a root bus node, bail */
342	    return (DI_WALK_CONTINUE);
343	}
344	else if (strcmp(prop_name, "bus-range") == 0) {
345	    numval = di_prop_ints(prop, &ints);
346	    if (numval == 2) {
347		first_bus = ints[0];
348		last_bus = ints[1];
349#ifdef __sparc
350		bus_range_found = 1;
351#endif
352	    }
353	}
354#ifdef __sparc
355	domain = nexus_count;
356#else
357	else if (strcmp(prop_name, "pciseg") == 0) {
358	    numval = di_prop_ints(prop, &ints);
359	    if (numval == 1) {
360		domain = ints[0];
361	    }
362	}
363#endif
364    }
365
366#ifdef __sparc
367    if ((!device_type_found) && di_phdl) {
368	numval = di_prom_prop_lookup_strings(di_phdl, di_node,
369	    "device_type", &strings);
370	if (numval == 1) {
371	    if (strncmp(strings, "pci", 3) != 0)
372		return (DI_WALK_CONTINUE);
373	    else
374		pci_node = 1;
375	}
376    }
377
378    if ((!bus_range_found) && di_phdl) {
379	numval = di_prom_prop_lookup_ints(di_phdl, di_node,
380	    "bus-range", &ints);
381	if (numval == 2) {
382	    first_bus = ints[0];
383	    last_bus = ints[1];
384	}
385    }
386#endif
387
388    if (pci_node != 1)
389	return (DI_WALK_CONTINUE);
390
391    /* we have a PCI root bus node. */
392    nexus = calloc(1, sizeof(nexus_t));
393    if (nexus == NULL) {
394	(void) fprintf(stderr, "Error allocating memory for nexus: %s\n",
395		       strerror(errno));
396	return (DI_WALK_TERMINATE);
397    }
398    nexus->first_bus = first_bus;
399    nexus->last_bus = last_bus;
400    nexus->domain = domain;
401
402#ifdef __sparc
403    nexus_count++;
404#endif
405
406    nexus_name = di_devfs_minor_path(minor);
407    if (nexus_name == NULL) {
408	(void) fprintf(stderr, "Error getting nexus path: %s\n",
409		       strerror(errno));
410	free(nexus);
411	return (DI_WALK_CONTINUE);
412    }
413
414    snprintf(nexus_path, sizeof(nexus_path), "/devices%s", nexus_name);
415    di_devfs_path_free(nexus_name);
416
417#ifdef DEBUG
418    fprintf(stderr, "nexus = %s, bus-range = %d - %d\n",
419	    nexus_path, first_bus, last_bus);
420#endif
421
422    if ((fd = open(nexus_path, O_RDWR | O_CLOEXEC)) >= 0) {
423	probe_args_t args;
424
425	nexus->path = strdup(nexus_path);
426	nexus_dev_path = di_devfs_path(di_node);
427	nexus->dev_path = strdup(nexus_dev_path);
428	di_devfs_path_free(nexus_dev_path);
429
430	/* Walk through devices under the rnode */
431	args.pinfo = pinfo;
432	args.nexus = nexus;
433	args.ret = 0;
434
435	(void) di_walk_node(di_node, DI_WALK_CLDFIRST, (void *)&args, probe_device_node);
436
437	close(fd);
438
439	if (args.ret) {
440	    free(nexus->path);
441	    free(nexus->dev_path);
442	    free(nexus);
443	    return (DI_WALK_TERMINATE);
444	}
445
446	nexus->next = nexus_list;
447	nexus_list = nexus;
448    } else {
449	(void) fprintf(stderr, "Error opening %s: %s\n",
450		       nexus_path, strerror(errno));
451	free(nexus);
452    }
453
454    return DI_WALK_CONTINUE;
455}
456
457static int
458find_target_node(di_node_t node, void *arg)
459{
460    int *regbuf = NULL;
461    int len = 0;
462    uint32_t busno, funcno, devno;
463    i_devnode_t *devnode = (i_devnode_t *)arg;
464
465    /*
466     * Test the property functions, only for testing
467     */
468    /*
469    void *prop = DI_PROP_NIL;
470
471    (void) fprintf(stderr, "start of node 0x%x\n", node->nodeid);
472    while ((prop = di_prop_hw_next(node, prop)) != DI_PROP_NIL) {
473	int i;
474	(void) fprintf(stderr, "name=%s: ", di_prop_name(prop));
475	len = 0;
476	if (!strcmp(di_prop_name(prop), "reg")) {
477	    len = di_prop_ints(prop, &regbuf);
478	}
479	for (i = 0; i < len; i++) {
480	    fprintf(stderr, "0x%0x.", regbuf[i]);
481	}
482	fprintf(stderr, "\n");
483    }
484    (void) fprintf(stderr, "end of node 0x%x\n", node->nodeid);
485    */
486
487    len = di_prop_lookup_ints(DDI_DEV_T_ANY, node, "reg", &regbuf);
488
489#ifdef __sparc
490    if ((len <= 0) && di_phdl)
491	len = di_prom_prop_lookup_ints(di_phdl, node, "reg", &regbuf);
492#endif
493
494    if (len <= 0) {
495#ifdef DEBUG
496	fprintf(stderr, "error = %x\n", errno);
497	fprintf(stderr, "can not find assigned-address\n");
498#endif
499	return (DI_WALK_CONTINUE);
500    }
501
502    busno = PCI_REG_BUS_G(regbuf[0]);
503    devno = PCI_REG_DEV_G(regbuf[0]);
504    funcno = PCI_REG_FUNC_G(regbuf[0]);
505
506    if ((busno == devnode->bus) &&
507	(devno == devnode->dev) &&
508	(funcno == devnode->func)) {
509	devnode->node = node;
510
511	return (DI_WALK_TERMINATE);
512    }
513
514    return (DI_WALK_CONTINUE);
515}
516
517/*
518 * Solaris version
519 */
520static int
521pci_device_solx_devfs_probe( struct pci_device * dev )
522{
523    int err = 0;
524    di_node_t rnode = DI_NODE_NIL;
525    i_devnode_t args = { 0, 0, 0, DI_NODE_NIL };
526    int *regbuf;
527    pci_regspec_t *reg;
528    int i;
529    int len = 0;
530    uint ent = 0;
531    struct pci_device_private *priv =
532	(struct pci_device_private *) dev;
533    nexus_t *nexus;
534
535    if ( (nexus = find_nexus_for_bus(dev->domain, dev->bus)) == NULL )
536	return ENODEV;
537
538    pci_device_cfg_read_u8(dev, &priv->header_type, PCI_CONF_HEADER);
539
540    pci_device_cfg_read_u8(dev, (uint8_t *)&dev->irq, PCI_CONF_ILINE);
541
542    /*
543     * starting to find if it is MEM/MEM64/IO
544     * using libdevinfo
545     */
546    if ((rnode = di_init(nexus->dev_path, DINFOCACHE)) == DI_NODE_NIL) {
547	err = errno;
548	(void) fprintf(stderr, "di_init failed: %s\n", strerror(errno));
549    } else {
550	args.bus = dev->bus;
551	args.dev = dev->dev;
552	args.func = dev->func;
553	(void) di_walk_node(rnode, DI_WALK_CLDFIRST,
554		(void *)&args, find_target_node);
555    }
556
557    if (args.node != DI_NODE_NIL) {
558	int *prop;
559#ifdef __sparc
560	di_minor_t minor;
561#endif
562
563	priv->is_primary = 0;
564
565#ifdef __sparc
566	if (minor = di_minor_next(args.node, DI_MINOR_NIL))
567	    MAPPING_DEV_PATH(dev) = di_devfs_minor_path (minor);
568	else
569	    MAPPING_DEV_PATH(dev) = NULL;
570#endif
571
572	if (di_prop_lookup_ints(DDI_DEV_T_ANY, args.node,
573				"primary-controller", &prop) >= 1) {
574	    if (prop[0])
575		priv->is_primary = 1;
576	}
577
578	/*
579	 * It will succeed for sure, because it was
580	 * successfully called in find_target_node
581	 */
582	len = di_prop_lookup_ints(DDI_DEV_T_ANY, args.node,
583				  "assigned-addresses",
584				  &regbuf);
585
586#ifdef __sparc
587	if ((len <= 0) && di_phdl) {
588	    len = di_prom_prop_lookup_ints(di_phdl, args.node,
589				"assigned-addresses", &regbuf);
590	}
591#endif
592    }
593
594    if (len <= 0)
595	goto cleanup;
596
597    /*
598     * Each BAR address get its own region slot in sequence.
599     * 32 bit BAR:
600     * BAR 0x10 -> slot0, BAR 0x14 -> slot1...
601     * 64 bit BAR:
602     * BAR 0x10 -> slot0, BAR 0x18 -> slot2...,
603     * slot1 is part of BAR 0x10
604     * Linux give two region slot for 64 bit address.
605     */
606    for (i = 0; i < len; i = i + (int)CELL_NUMS_1275) {
607
608	reg = (pci_regspec_t *)&regbuf[i];
609	ent = reg->pci_phys_hi & 0xff;
610
611	if (ent > PCI_CONF_ROM) {
612	    fprintf(stderr, "error ent = %d\n", ent);
613	    break;
614	}
615	/*
616	 * G35 broken in BAR0
617	 */
618	if (ent < PCI_CONF_BASE0) {
619	    /*
620	     * VGA resource here and ignore it
621	     */
622	    break;
623	} else if (ent == PCI_CONF_ROM) {
624	    priv->rom_base = reg->pci_phys_low |
625		((uint64_t)reg->pci_phys_mid << 32);
626	    dev->rom_size = reg->pci_size_low;
627	} else {
628	    ent = (ent - PCI_CONF_BASE0) >> 2;
629	    /*
630	     * non relocatable resource is excluded
631	     * such like 0xa0000, 0x3b0. If it is met,
632	     * the loop is broken;
633	     */
634	    if (!PCI_REG_REG_G(reg->pci_phys_hi))
635		break;
636
637	    if (reg->pci_phys_hi & PCI_PREFETCH_B) {
638		dev->regions[ent].is_prefetchable = 1;
639	    }
640
641
642	    dev->regions[ent].base_addr = reg->pci_phys_low |
643		((uint64_t)reg->pci_phys_mid << 32);
644	    dev->regions[ent].size = reg->pci_size_low |
645		((uint64_t)reg->pci_size_hi << 32);
646
647	    switch (reg->pci_phys_hi & PCI_REG_ADDR_M) {
648		case PCI_ADDR_IO:
649		    dev->regions[ent].is_IO = 1;
650		    break;
651		case PCI_ADDR_MEM32:
652		    break;
653		case PCI_ADDR_MEM64:
654		    dev->regions[ent].is_64 = 1;
655		    /*
656		     * Skip one slot for 64 bit address
657		     */
658		    break;
659	    }
660	}
661    }
662
663  cleanup:
664    if (rnode != DI_NODE_NIL) {
665	di_fini(rnode);
666    }
667    return (err);
668}
669
670/**
671 * Map a memory region for a device using /dev/xsvc (x86) or fb device (sparc)
672 *
673 * \param dev   Device whose memory region is to be mapped.
674 * \param map   Parameters of the mapping that is to be created.
675 *
676 * \return
677 * Zero on success or an \c errno value on failure.
678 */
679static int
680pci_device_solx_devfs_map_range(struct pci_device *dev,
681				struct pci_device_mapping *map)
682{
683    const int prot = ((map->flags & PCI_DEV_MAP_FLAG_WRITABLE) != 0)
684			? (PROT_READ | PROT_WRITE) : PROT_READ;
685    int err = 0;
686
687    const char *map_dev;
688    int		map_fd;
689
690#ifdef __sparc
691    char	map_dev_buf[128];
692
693    if (MAPPING_DEV_PATH(dev)) {
694	snprintf(map_dev_buf, sizeof (map_dev_buf), "%s%s",
695		 "/devices", MAPPING_DEV_PATH(dev));
696	map_dev = map_dev_buf;
697    }
698    else
699	map_dev = "/dev/fb0";
700
701    map_fd = -1;
702#else
703    /*
704     * Still uses xsvc to do the user space mapping on x86/x64,
705     * caches open fd across multiple calls.
706     */
707    map_dev = "/dev/xsvc";
708    map_fd = xsvc_fd;
709#endif
710
711    if (map_fd < 0) {
712	if ((map_fd = open(map_dev, O_RDWR | O_CLOEXEC)) < 0) {
713	    err = errno;
714	    (void) fprintf(stderr, "can not open %s: %s\n", map_dev,
715			   strerror(errno));
716	    return err;
717	}
718#ifndef __sparc
719        xsvc_fd = map_fd;
720#endif
721    }
722
723    map->memory = mmap(NULL, map->size, prot, MAP_SHARED, map_fd, map->base);
724    if (map->memory == MAP_FAILED) {
725	err = errno;
726
727	(void) fprintf(stderr, "map rom region =%llx failed: %s\n",
728		       (unsigned long long) map->base, strerror(errno));
729    }
730
731#ifdef __sparc
732    close (map_fd);
733#endif
734
735    return err;
736}
737
738/*
739 * Solaris version: read the VGA ROM data
740 */
741static int
742pci_device_solx_devfs_read_rom( struct pci_device * dev, void * buffer )
743{
744    int err;
745    struct pci_device_mapping prom = {
746	.base = 0xC0000,
747	.size = 0x10000,
748	.flags = 0
749    };
750    struct pci_device_private *priv =
751	(struct pci_device_private *) dev;
752
753    if (priv->rom_base) {
754	prom.base = priv->rom_base;
755	prom.size = dev->rom_size;
756    }
757
758    err = pci_device_solx_devfs_map_range(dev, &prom);
759    if (err == 0) {
760	(void) bcopy(prom.memory, buffer, dev->rom_size);
761
762	if (munmap(prom.memory, prom.size) == -1) {
763	    err = errno;
764	}
765    }
766    return err;
767}
768
769/*
770 * solaris version: Read the configurations space of the devices
771 */
772static int
773pci_device_solx_devfs_read( struct pci_device * dev, void * data,
774			     pciaddr_t offset, pciaddr_t size,
775			     pciaddr_t * bytes_read )
776{
777    pcitool_reg_t cfg_prg;
778    int err = 0;
779    unsigned int i = 0;
780    nexus_t *nexus;
781    int fd;
782
783    nexus = find_nexus_for_bus(dev->domain, dev->bus);
784
785    *bytes_read = 0;
786
787    if ( nexus == NULL ) {
788	return ENODEV;
789    }
790
791    cfg_prg.offset = offset;
792    cfg_prg.acc_attr = PCITOOL_ACC_ATTR_SIZE_1 + NATIVE_ENDIAN;
793    cfg_prg.bus_no = dev->bus;
794    cfg_prg.dev_no = dev->dev;
795    cfg_prg.func_no = dev->func;
796    cfg_prg.barnum = 0;
797    cfg_prg.user_version = PCITOOL_USER_VERSION;
798
799    if ((fd = open(nexus->path, O_RDWR | O_CLOEXEC)) < 0)
800	return ENOENT;
801
802    for (i = 0; i < size; i += PCITOOL_ACC_ATTR_SIZE(PCITOOL_ACC_ATTR_SIZE_1))
803    {
804	cfg_prg.offset = offset + i;
805
806	if ((err = ioctl(fd, PCITOOL_DEVICE_GET_REG, &cfg_prg)) != 0) {
807	    fprintf(stderr, "read bdf<%s,%x,%x,%x,%llx> config space failure\n",
808		    nexus->path,
809		    cfg_prg.bus_no,
810		    cfg_prg.dev_no,
811		    cfg_prg.func_no,
812		    (unsigned long long) cfg_prg.offset);
813	    fprintf(stderr, "Failure cause = %x\n", err);
814	    break;
815	}
816
817	((uint8_t *)data)[i] = (uint8_t)cfg_prg.data;
818	/*
819	 * DWORDS Offset or bytes Offset ??
820	 */
821    }
822    *bytes_read = i;
823
824    close(fd);
825
826    return (err);
827}
828
829/*
830 * Solaris version
831 */
832static int
833pci_device_solx_devfs_write( struct pci_device * dev, const void * data,
834			     pciaddr_t offset, pciaddr_t size,
835			     pciaddr_t * bytes_written )
836{
837    pcitool_reg_t cfg_prg;
838    int err = 0;
839    int cmd;
840    nexus_t *nexus;
841    int fd;
842
843    nexus = find_nexus_for_bus(dev->domain, dev->bus);
844
845    if ( bytes_written != NULL ) {
846	*bytes_written = 0;
847    }
848
849    if ( nexus == NULL ) {
850	return ENODEV;
851    }
852
853    cfg_prg.offset = offset;
854    switch (size) {
855        case 1:
856	    cfg_prg.acc_attr = PCITOOL_ACC_ATTR_SIZE_1 + NATIVE_ENDIAN;
857	    cfg_prg.data = *((const uint8_t *)data);
858	    break;
859        case 2:
860	    cfg_prg.acc_attr = PCITOOL_ACC_ATTR_SIZE_2 + NATIVE_ENDIAN;
861	    cfg_prg.data = *((const uint16_t *)data);
862	    break;
863        case 4:
864	    cfg_prg.acc_attr = PCITOOL_ACC_ATTR_SIZE_4 + NATIVE_ENDIAN;
865	    cfg_prg.data = *((const uint32_t *)data);
866	    break;
867        case 8:
868	    cfg_prg.acc_attr = PCITOOL_ACC_ATTR_SIZE_8 + NATIVE_ENDIAN;
869	    cfg_prg.data = *((const uint64_t *)data);
870	    break;
871        default:
872	    return EINVAL;
873    }
874    cfg_prg.bus_no = dev->bus;
875    cfg_prg.dev_no = dev->dev;
876    cfg_prg.func_no = dev->func;
877    cfg_prg.barnum = 0;
878    cfg_prg.user_version = PCITOOL_USER_VERSION;
879
880    /*
881     * Check if this device is bridge device.
882     * If it is, it is also a nexus node???
883     * It seems that there is no explicit
884     * PCI nexus device for X86, so not applicable
885     * from pcitool_bus_reg_ops in pci_tools.c
886     */
887    cmd = PCITOOL_DEVICE_SET_REG;
888
889    if ((fd = open(nexus->path, O_RDWR | O_CLOEXEC)) < 0)
890	return ENOENT;
891
892    if ((err = ioctl(fd, cmd, &cfg_prg)) != 0) {
893	close(fd);
894	return (err);
895    }
896    *bytes_written = size;
897
898    close(fd);
899
900    return (err);
901}
902
903static int pci_device_solx_devfs_boot_vga(struct pci_device *dev)
904{
905    struct pci_device_private *priv =
906	(struct pci_device_private *) dev;
907
908    return (priv->is_primary);
909
910}
911
912static struct pci_io_handle *
913pci_device_solx_devfs_open_legacy_io(struct pci_io_handle *ret,
914				     struct pci_device *dev,
915				     pciaddr_t base, pciaddr_t size)
916{
917#ifdef __x86
918    if (sysi86(SI86V86, V86SC_IOPL, PS_IOPL) == 0) {
919	ret->base = base;
920	ret->size = size;
921	ret->is_legacy = 1;
922	return ret;
923    }
924#endif
925    return NULL;
926}
927
928static uint32_t
929pci_device_solx_devfs_read32(struct pci_io_handle *handle, uint32_t reg)
930{
931#ifdef __x86
932    uint16_t port = (uint16_t) (handle->base + reg);
933    uint32_t ret;
934    __asm__ __volatile__("inl %1,%0":"=a"(ret):"d"(port));
935    return ret;
936#else
937    return *(uint32_t *)((uintptr_t)handle->memory + reg);
938#endif
939}
940
941static uint16_t
942pci_device_solx_devfs_read16(struct pci_io_handle *handle, uint32_t reg)
943{
944#ifdef __x86
945    uint16_t port = (uint16_t) (handle->base + reg);
946    uint16_t ret;
947    __asm__ __volatile__("inw %1,%0":"=a"(ret):"d"(port));
948    return ret;
949#else
950    return *(uint16_t *)((uintptr_t)handle->memory + reg);
951#endif
952}
953
954static uint8_t
955pci_device_solx_devfs_read8(struct pci_io_handle *handle, uint32_t reg)
956{
957#ifdef __x86
958    uint16_t port = (uint16_t) (handle->base + reg);
959    uint8_t ret;
960    __asm__ __volatile__("inb %1,%0":"=a"(ret):"d"(port));
961    return ret;
962#else
963    return *(uint8_t *)((uintptr_t)handle->memory + reg);
964#endif
965}
966
967static void
968pci_device_solx_devfs_write32(struct pci_io_handle *handle, uint32_t reg,
969    uint32_t data)
970{
971#ifdef __x86
972      uint16_t port = (uint16_t) (handle->base + reg);
973      __asm__ __volatile__("outl %0,%1"::"a"(data), "d"(port));
974#else
975      *(uint16_t *)((uintptr_t)handle->memory + reg) = data;
976#endif
977}
978
979static void
980pci_device_solx_devfs_write16(struct pci_io_handle *handle, uint32_t reg,
981    uint16_t data)
982{
983#ifdef __x86
984      uint16_t port = (uint16_t) (handle->base + reg);
985      __asm__ __volatile__("outw %0,%1"::"a"(data), "d"(port));
986#else
987    *(uint8_t *)((uintptr_t)handle->memory + reg) = data;
988#endif
989}
990
991static void
992pci_device_solx_devfs_write8(struct pci_io_handle *handle, uint32_t reg,
993    uint8_t data)
994{
995#ifdef __x86
996      uint16_t port = (uint16_t) (handle->base + reg);
997      __asm__ __volatile__("outb %0,%1"::"a"(data), "d"(port));
998#else
999      *(uint32_t *)((uintptr_t)handle->memory + reg) = data;
1000#endif
1001}
1002
1003static int
1004pci_device_solx_devfs_map_legacy(struct pci_device *dev, pciaddr_t base,
1005				 pciaddr_t size, unsigned map_flags,
1006				 void **addr)
1007{
1008    int err;
1009    struct pci_device_mapping map = {
1010	.base = base,
1011	.size = size,
1012	.flags = map_flags,
1013    };
1014
1015    err = pci_device_solx_devfs_map_range(dev, &map);
1016    if (err == 0)
1017	*addr = map.memory;
1018    return err;
1019}
1020
1021static int
1022pci_device_solx_devfs_unmap_legacy(struct pci_device *dev,
1023				   void *addr, pciaddr_t size)
1024{
1025    struct pci_device_mapping map = {
1026	.memory = addr,
1027	.size = size,
1028    };
1029
1030    return pci_device_generic_unmap_range(dev, &map);
1031}
1032
1033static const struct pci_system_methods solx_devfs_methods = {
1034    .destroy = pci_system_solx_devfs_destroy,
1035#ifdef __sparc
1036    .destroy_device = pci_system_solx_devfs_destroy_device,
1037#else
1038    .destroy_device = NULL,
1039#endif
1040    .read_rom = pci_device_solx_devfs_read_rom,
1041    .probe = pci_device_solx_devfs_probe,
1042    .map_range = pci_device_solx_devfs_map_range,
1043    .unmap_range = pci_device_generic_unmap_range,
1044
1045    .read = pci_device_solx_devfs_read,
1046    .write = pci_device_solx_devfs_write,
1047
1048    .fill_capabilities = pci_fill_capabilities_generic,
1049    .boot_vga = pci_device_solx_devfs_boot_vga,
1050
1051    .open_legacy_io = pci_device_solx_devfs_open_legacy_io,
1052    .read32 = pci_device_solx_devfs_read32,
1053    .read16 = pci_device_solx_devfs_read16,
1054    .read8 = pci_device_solx_devfs_read8,
1055    .write32 = pci_device_solx_devfs_write32,
1056    .write16 = pci_device_solx_devfs_write16,
1057    .write8 = pci_device_solx_devfs_write8,
1058    .map_legacy = pci_device_solx_devfs_map_legacy,
1059    .unmap_legacy = pci_device_solx_devfs_unmap_legacy,
1060};
1061
1062/*
1063 * Attempt to access PCI subsystem using Solaris's devfs interface.
1064 * Solaris version
1065 */
1066_pci_hidden int
1067pci_system_solx_devfs_create( void )
1068{
1069    int err = 0;
1070    di_node_t di_node;
1071    probe_info_t pinfo;
1072    struct pci_device_private *devices;
1073
1074    if (nexus_list != NULL) {
1075	return 0;
1076    }
1077
1078    if ((di_node = di_init("/", DINFOCACHE)) == DI_NODE_NIL) {
1079	err = errno;
1080	(void) fprintf(stderr, "di_init() failed: %s\n",
1081		       strerror(errno));
1082	return (err);
1083    }
1084
1085    if ((devices = calloc(INITIAL_NUM_DEVICES,
1086			sizeof (struct pci_device_private))) == NULL) {
1087	err = errno;
1088	di_fini(di_node);
1089	return (err);
1090    }
1091
1092#ifdef __sparc
1093    if ((di_phdl = di_prom_init()) == DI_PROM_HANDLE_NIL)
1094	(void) fprintf(stderr, "di_prom_init failed: %s\n", strerror(errno));
1095#endif
1096
1097    pinfo.num_allocated_elems = INITIAL_NUM_DEVICES;
1098    pinfo.num_devices = 0;
1099    pinfo.devices = devices;
1100#ifdef __sparc
1101    nexus_count = 0;
1102#endif
1103    (void) di_walk_minor(di_node, DDI_NT_REGACC, 0, &pinfo, probe_nexus_node);
1104
1105    di_fini(di_node);
1106
1107    if ((pci_sys = calloc(1, sizeof (struct pci_system))) == NULL) {
1108	err = errno;
1109	free(devices);
1110	return (err);
1111    }
1112
1113    pci_sys->methods = &solx_devfs_methods;
1114    pci_sys->devices = pinfo.devices;
1115    pci_sys->num_devices = pinfo.num_devices;
1116
1117    return (err);
1118}
1119