solx_devfs.c revision 4f5e7dd7
14f5e7dd7Smrg/* 24f5e7dd7Smrg * (C) Copyright IBM Corporation 2006 34f5e7dd7Smrg * All Rights Reserved. 44f5e7dd7Smrg * 54f5e7dd7Smrg * Permission is hereby granted, free of charge, to any person obtaining a 64f5e7dd7Smrg * copy of this software and associated documentation files (the "Software"), 74f5e7dd7Smrg * to deal in the Software without restriction, including without limitation 84f5e7dd7Smrg * on the rights to use, copy, modify, merge, publish, distribute, sub 94f5e7dd7Smrg * license, and/or sell copies of the Software, and to permit persons to whom 104f5e7dd7Smrg * the Software is furnished to do so, subject to the following conditions: 114f5e7dd7Smrg * 124f5e7dd7Smrg * The above copyright notice and this permission notice (including the next 134f5e7dd7Smrg * paragraph) shall be included in all copies or substantial portions of the 144f5e7dd7Smrg * Software. 154f5e7dd7Smrg * 164f5e7dd7Smrg * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 174f5e7dd7Smrg * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 184f5e7dd7Smrg * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL 194f5e7dd7Smrg * IBM AND/OR THEIR SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 204f5e7dd7Smrg * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 214f5e7dd7Smrg * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 224f5e7dd7Smrg * DEALINGS IN THE SOFTWARE. 234f5e7dd7Smrg */ 244f5e7dd7Smrg/* 254f5e7dd7Smrg * Copyright 2007, 2009 Sun Microsystems, Inc. All rights reserved. 264f5e7dd7Smrg * 274f5e7dd7Smrg * Permission is hereby granted, free of charge, to any person obtaining a 284f5e7dd7Smrg * copy of this software and associated documentation files (the 294f5e7dd7Smrg * "Software"), to deal in the Software without restriction, including 304f5e7dd7Smrg * without limitation the rights to use, copy, modify, merge, publish, 314f5e7dd7Smrg * distribute, and/or sell copies of the Software, and to permit persons 324f5e7dd7Smrg * to whom the Software is furnished to do so, provided that the above 334f5e7dd7Smrg * copyright notice(s) and this permission notice appear in all copies of 344f5e7dd7Smrg * the Software and that both the above copyright notice(s) and this 354f5e7dd7Smrg * permission notice appear in supporting documentation. 364f5e7dd7Smrg * 374f5e7dd7Smrg * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 384f5e7dd7Smrg * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 394f5e7dd7Smrg * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT 404f5e7dd7Smrg * OF THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR 414f5e7dd7Smrg * HOLDERS INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL 424f5e7dd7Smrg * INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING 434f5e7dd7Smrg * FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, 444f5e7dd7Smrg * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION 454f5e7dd7Smrg * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 464f5e7dd7Smrg * 474f5e7dd7Smrg * Except as contained in this notice, the name of a copyright holder 484f5e7dd7Smrg * shall not be used in advertising or otherwise to promote the sale, use 494f5e7dd7Smrg * or other dealings in this Software without prior written authorization 504f5e7dd7Smrg * of the copyright holder. 514f5e7dd7Smrg */ 524f5e7dd7Smrg/* 534f5e7dd7Smrg * Solaris devfs interfaces 544f5e7dd7Smrg */ 554f5e7dd7Smrg 564f5e7dd7Smrg#include <stdlib.h> 574f5e7dd7Smrg#include <strings.h> 584f5e7dd7Smrg#include <stdio.h> 594f5e7dd7Smrg#include <unistd.h> 604f5e7dd7Smrg#include <sys/types.h> 614f5e7dd7Smrg#include <fcntl.h> 624f5e7dd7Smrg#include <sys/mman.h> 634f5e7dd7Smrg#include <errno.h> 644f5e7dd7Smrg#include <sys/pci.h> 654f5e7dd7Smrg#include <libdevinfo.h> 664f5e7dd7Smrg#include "pci_tools.h" 674f5e7dd7Smrg 684f5e7dd7Smrg#include "pciaccess.h" 694f5e7dd7Smrg#include "pciaccess_private.h" 704f5e7dd7Smrg 714f5e7dd7Smrg/* #define DEBUG */ 724f5e7dd7Smrg 734f5e7dd7Smrg#define MAX_DEVICES 256 744f5e7dd7Smrg#define CELL_NUMS_1275 (sizeof(pci_regspec_t) / sizeof(uint_t)) 754f5e7dd7Smrg 764f5e7dd7Smrgtypedef union { 774f5e7dd7Smrg uint8_t bytes[16 * sizeof (uint32_t)]; 784f5e7dd7Smrg uint32_t dwords[16]; 794f5e7dd7Smrg} pci_conf_hdr_t; 804f5e7dd7Smrg 814f5e7dd7Smrgtypedef struct i_devnode { 824f5e7dd7Smrg uint8_t bus; 834f5e7dd7Smrg uint8_t dev; 844f5e7dd7Smrg uint8_t func; 854f5e7dd7Smrg di_node_t node; 864f5e7dd7Smrg} i_devnode_t; 874f5e7dd7Smrg 884f5e7dd7Smrgtypedef struct nexus { 894f5e7dd7Smrg int fd; 904f5e7dd7Smrg int first_bus; 914f5e7dd7Smrg int last_bus; 924f5e7dd7Smrg char *path; /* for errors/debugging; fd is all we need */ 934f5e7dd7Smrg struct nexus *next; 944f5e7dd7Smrg} nexus_t; 954f5e7dd7Smrg 964f5e7dd7Smrgstatic nexus_t *nexus_list = NULL; 974f5e7dd7Smrgstatic int xsvc_fd = -1; 984f5e7dd7Smrg 994f5e7dd7Smrg/* 1004f5e7dd7Smrg * Read config space in native processor endianness. Endian-neutral 1014f5e7dd7Smrg * processing can then take place. On big endian machines, MSB and LSB 1024f5e7dd7Smrg * of little endian data end up switched if read as little endian. 1034f5e7dd7Smrg * They are in correct order if read as big endian. 1044f5e7dd7Smrg */ 1054f5e7dd7Smrg#if defined(__sparc) 1064f5e7dd7Smrg# define NATIVE_ENDIAN PCITOOL_ACC_ATTR_ENDN_BIG 1074f5e7dd7Smrg#elif defined(__x86) 1084f5e7dd7Smrg# define NATIVE_ENDIAN PCITOOL_ACC_ATTR_ENDN_LTL 1094f5e7dd7Smrg#else 1104f5e7dd7Smrg# error "ISA is neither __sparc nor __x86" 1114f5e7dd7Smrg#endif 1124f5e7dd7Smrg 1134f5e7dd7Smrg/* 1144f5e7dd7Smrg * Identify problematic southbridges. These have device id 0x5249 and 1154f5e7dd7Smrg * vendor id 0x10b9. Check for revision ID 0 and class code 060400 as well. 1164f5e7dd7Smrg * Values are little endian, so they are reversed for SPARC. 1174f5e7dd7Smrg * 1184f5e7dd7Smrg * Check for these southbridges on all architectures, as the issue is a 1194f5e7dd7Smrg * southbridge issue, independent of processor. 1204f5e7dd7Smrg * 1214f5e7dd7Smrg * If one of these is found during probing, skip probing other devs/funcs on 1224f5e7dd7Smrg * the rest of the bus, since the southbridge and all devs underneath will 1234f5e7dd7Smrg * otherwise disappear. 1244f5e7dd7Smrg */ 1254f5e7dd7Smrg#if (NATIVE_ENDIAN == PCITOOL_ACC_ATTR_ENDN_BIG) 1264f5e7dd7Smrg# define U45_SB_DEVID_VID 0xb9104952 1274f5e7dd7Smrg# define U45_SB_CLASS_RID 0x00000406 1284f5e7dd7Smrg#else 1294f5e7dd7Smrg# define U45_SB_DEVID_VID 0x524910b9 1304f5e7dd7Smrg# define U45_SB_CLASS_RID 0x06040000 1314f5e7dd7Smrg#endif 1324f5e7dd7Smrg 1334f5e7dd7Smrgstatic int pci_device_solx_devfs_map_range(struct pci_device *dev, 1344f5e7dd7Smrg struct pci_device_mapping *map); 1354f5e7dd7Smrg 1364f5e7dd7Smrgstatic int pci_device_solx_devfs_read_rom( struct pci_device * dev, 1374f5e7dd7Smrg void * buffer ); 1384f5e7dd7Smrg 1394f5e7dd7Smrgstatic int pci_device_solx_devfs_probe( struct pci_device * dev ); 1404f5e7dd7Smrg 1414f5e7dd7Smrgstatic int pci_device_solx_devfs_read( struct pci_device * dev, void * data, 1424f5e7dd7Smrg pciaddr_t offset, pciaddr_t size, pciaddr_t * bytes_read ); 1434f5e7dd7Smrg 1444f5e7dd7Smrgstatic int pci_device_solx_devfs_write( struct pci_device * dev, 1454f5e7dd7Smrg const void * data, pciaddr_t offset, pciaddr_t size, 1464f5e7dd7Smrg pciaddr_t * bytes_written ); 1474f5e7dd7Smrg 1484f5e7dd7Smrgstatic int probe_dev(nexus_t *nexus, pcitool_reg_t *prg_p, 1494f5e7dd7Smrg struct pci_system *pci_sys); 1504f5e7dd7Smrg 1514f5e7dd7Smrgstatic int do_probe(nexus_t *nexus, struct pci_system *pci_sys); 1524f5e7dd7Smrg 1534f5e7dd7Smrgstatic int probe_nexus_node(di_node_t di_node, di_minor_t minor, void *arg); 1544f5e7dd7Smrg 1554f5e7dd7Smrgstatic void pci_system_solx_devfs_destroy( void ); 1564f5e7dd7Smrg 1574f5e7dd7Smrgstatic int get_config_header(int fd, uint8_t bus_no, uint8_t dev_no, 1584f5e7dd7Smrg uint8_t func_no, pci_conf_hdr_t *config_hdr_p); 1594f5e7dd7Smrg 1604f5e7dd7Smrgint pci_system_solx_devfs_create( void ); 1614f5e7dd7Smrg 1624f5e7dd7Smrgstatic const struct pci_system_methods solx_devfs_methods = { 1634f5e7dd7Smrg .destroy = pci_system_solx_devfs_destroy, 1644f5e7dd7Smrg .destroy_device = NULL, 1654f5e7dd7Smrg .read_rom = pci_device_solx_devfs_read_rom, 1664f5e7dd7Smrg .probe = pci_device_solx_devfs_probe, 1674f5e7dd7Smrg .map_range = pci_device_solx_devfs_map_range, 1684f5e7dd7Smrg .unmap_range = pci_device_generic_unmap_range, 1694f5e7dd7Smrg 1704f5e7dd7Smrg .read = pci_device_solx_devfs_read, 1714f5e7dd7Smrg .write = pci_device_solx_devfs_write, 1724f5e7dd7Smrg 1734f5e7dd7Smrg .fill_capabilities = pci_fill_capabilities_generic 1744f5e7dd7Smrg}; 1754f5e7dd7Smrg 1764f5e7dd7Smrgstatic nexus_t * 1774f5e7dd7Smrgfind_nexus_for_bus( int bus ) 1784f5e7dd7Smrg{ 1794f5e7dd7Smrg nexus_t *nexus; 1804f5e7dd7Smrg 1814f5e7dd7Smrg for (nexus = nexus_list ; nexus != NULL ; nexus = nexus->next) { 1824f5e7dd7Smrg if ((bus >= nexus->first_bus) && (bus <= nexus->last_bus)) { 1834f5e7dd7Smrg return nexus; 1844f5e7dd7Smrg } 1854f5e7dd7Smrg } 1864f5e7dd7Smrg return NULL; 1874f5e7dd7Smrg} 1884f5e7dd7Smrg 1894f5e7dd7Smrg#define GET_CONFIG_VAL_8(offset) (config_hdr.bytes[offset]) 1904f5e7dd7Smrg#define GET_CONFIG_VAL_16(offset) \ 1914f5e7dd7Smrg (uint16_t) (GET_CONFIG_VAL_8(offset) + (GET_CONFIG_VAL_8(offset+1) << 8)) 1924f5e7dd7Smrg#define GET_CONFIG_VAL_32(offset) \ 1934f5e7dd7Smrg (uint32_t) (GET_CONFIG_VAL_8(offset) + \ 1944f5e7dd7Smrg (GET_CONFIG_VAL_8(offset+1) << 8) + \ 1954f5e7dd7Smrg (GET_CONFIG_VAL_8(offset+2) << 16) + \ 1964f5e7dd7Smrg (GET_CONFIG_VAL_8(offset+3) << 24)) 1974f5e7dd7Smrg 1984f5e7dd7Smrg/* 1994f5e7dd7Smrg * Release all the resources 2004f5e7dd7Smrg * Solaris version 2014f5e7dd7Smrg */ 2024f5e7dd7Smrgstatic void 2034f5e7dd7Smrgpci_system_solx_devfs_destroy( void ) 2044f5e7dd7Smrg{ 2054f5e7dd7Smrg /* 2064f5e7dd7Smrg * The memory allocated for pci_sys & devices in create routines 2074f5e7dd7Smrg * will be freed in pci_system_cleanup. 2084f5e7dd7Smrg * Need to free system-specific allocations here. 2094f5e7dd7Smrg */ 2104f5e7dd7Smrg nexus_t *nexus, *next; 2114f5e7dd7Smrg 2124f5e7dd7Smrg for (nexus = nexus_list ; nexus != NULL ; nexus = next) { 2134f5e7dd7Smrg next = nexus->next; 2144f5e7dd7Smrg close(nexus->fd); 2154f5e7dd7Smrg free(nexus->path); 2164f5e7dd7Smrg free(nexus); 2174f5e7dd7Smrg } 2184f5e7dd7Smrg nexus_list = NULL; 2194f5e7dd7Smrg 2204f5e7dd7Smrg if (xsvc_fd >= 0) { 2214f5e7dd7Smrg close(xsvc_fd); 2224f5e7dd7Smrg xsvc_fd = -1; 2234f5e7dd7Smrg } 2244f5e7dd7Smrg} 2254f5e7dd7Smrg 2264f5e7dd7Smrg/* 2274f5e7dd7Smrg * Attempt to access PCI subsystem using Solaris's devfs interface. 2284f5e7dd7Smrg * Solaris version 2294f5e7dd7Smrg */ 2304f5e7dd7Smrg_pci_hidden int 2314f5e7dd7Smrgpci_system_solx_devfs_create( void ) 2324f5e7dd7Smrg{ 2334f5e7dd7Smrg int err = 0; 2344f5e7dd7Smrg di_node_t di_node; 2354f5e7dd7Smrg 2364f5e7dd7Smrg 2374f5e7dd7Smrg if (nexus_list != NULL) { 2384f5e7dd7Smrg return 0; 2394f5e7dd7Smrg } 2404f5e7dd7Smrg 2414f5e7dd7Smrg /* 2424f5e7dd7Smrg * Only allow MAX_DEVICES exists 2434f5e7dd7Smrg * I will fix it later to get 2444f5e7dd7Smrg * the total devices first 2454f5e7dd7Smrg */ 2464f5e7dd7Smrg if ((pci_sys = calloc(1, sizeof (struct pci_system))) != NULL) { 2474f5e7dd7Smrg pci_sys->methods = &solx_devfs_methods; 2484f5e7dd7Smrg 2494f5e7dd7Smrg if ((pci_sys->devices = 2504f5e7dd7Smrg calloc(MAX_DEVICES, sizeof (struct pci_device_private))) 2514f5e7dd7Smrg != NULL) { 2524f5e7dd7Smrg 2534f5e7dd7Smrg if ((di_node = di_init("/", DINFOCPYALL)) == DI_NODE_NIL) { 2544f5e7dd7Smrg err = errno; 2554f5e7dd7Smrg (void) fprintf(stderr, "di_init() failed: %s\n", 2564f5e7dd7Smrg strerror(errno)); 2574f5e7dd7Smrg } else { 2584f5e7dd7Smrg (void) di_walk_minor(di_node, DDI_NT_REGACC, 0, pci_sys, 2594f5e7dd7Smrg probe_nexus_node); 2604f5e7dd7Smrg di_fini(di_node); 2614f5e7dd7Smrg } 2624f5e7dd7Smrg } 2634f5e7dd7Smrg else { 2644f5e7dd7Smrg err = errno; 2654f5e7dd7Smrg } 2664f5e7dd7Smrg } else { 2674f5e7dd7Smrg err = errno; 2684f5e7dd7Smrg } 2694f5e7dd7Smrg 2704f5e7dd7Smrg if (err != 0) { 2714f5e7dd7Smrg if (pci_sys != NULL) { 2724f5e7dd7Smrg free(pci_sys->devices); 2734f5e7dd7Smrg free(pci_sys); 2744f5e7dd7Smrg pci_sys = NULL; 2754f5e7dd7Smrg } 2764f5e7dd7Smrg } 2774f5e7dd7Smrg 2784f5e7dd7Smrg return (err); 2794f5e7dd7Smrg} 2804f5e7dd7Smrg 2814f5e7dd7Smrg/* 2824f5e7dd7Smrg * Retrieve first 16 dwords of device's config header, except for the first 2834f5e7dd7Smrg * dword. First 16 dwords are defined by the PCI specification. 2844f5e7dd7Smrg */ 2854f5e7dd7Smrgstatic int 2864f5e7dd7Smrgget_config_header(int fd, uint8_t bus_no, uint8_t dev_no, uint8_t func_no, 2874f5e7dd7Smrg pci_conf_hdr_t *config_hdr_p) 2884f5e7dd7Smrg{ 2894f5e7dd7Smrg pcitool_reg_t cfg_prg; 2904f5e7dd7Smrg int i; 2914f5e7dd7Smrg int rval = 0; 2924f5e7dd7Smrg 2934f5e7dd7Smrg /* Prepare a local pcitool_reg_t so as to not disturb the caller's. */ 2944f5e7dd7Smrg cfg_prg.offset = 0; 2954f5e7dd7Smrg cfg_prg.acc_attr = PCITOOL_ACC_ATTR_SIZE_4 + NATIVE_ENDIAN; 2964f5e7dd7Smrg cfg_prg.bus_no = bus_no; 2974f5e7dd7Smrg cfg_prg.dev_no = dev_no; 2984f5e7dd7Smrg cfg_prg.func_no = func_no; 2994f5e7dd7Smrg cfg_prg.barnum = 0; 3004f5e7dd7Smrg cfg_prg.user_version = PCITOOL_USER_VERSION; 3014f5e7dd7Smrg 3024f5e7dd7Smrg /* Get dwords 1-15 of config space. They must be read as uint32_t. */ 3034f5e7dd7Smrg for (i = 1; i < (sizeof (pci_conf_hdr_t) / sizeof (uint32_t)); i++) { 3044f5e7dd7Smrg cfg_prg.offset += sizeof (uint32_t); 3054f5e7dd7Smrg if ((rval = ioctl(fd, PCITOOL_DEVICE_GET_REG, &cfg_prg)) != 0) { 3064f5e7dd7Smrg break; 3074f5e7dd7Smrg } 3084f5e7dd7Smrg config_hdr_p->dwords[i] = (uint32_t)cfg_prg.data; 3094f5e7dd7Smrg } 3104f5e7dd7Smrg 3114f5e7dd7Smrg return (rval); 3124f5e7dd7Smrg} 3134f5e7dd7Smrg 3144f5e7dd7Smrg 3154f5e7dd7Smrg/* 3164f5e7dd7Smrg * Probe device's functions. Modifies many fields in the prg_p. 3174f5e7dd7Smrg */ 3184f5e7dd7Smrgstatic int 3194f5e7dd7Smrgprobe_dev(nexus_t *nexus, pcitool_reg_t *prg_p, struct pci_system *pci_sys) 3204f5e7dd7Smrg{ 3214f5e7dd7Smrg pci_conf_hdr_t config_hdr; 3224f5e7dd7Smrg boolean_t multi_function_device; 3234f5e7dd7Smrg int8_t func; 3244f5e7dd7Smrg int8_t first_func = 0; 3254f5e7dd7Smrg int8_t last_func = PCI_REG_FUNC_M >> PCI_REG_FUNC_SHIFT; 3264f5e7dd7Smrg int rval = 0; 3274f5e7dd7Smrg struct pci_device * pci_base; 3284f5e7dd7Smrg 3294f5e7dd7Smrg /* 3304f5e7dd7Smrg * Loop through at least func=first_func. Continue looping through 3314f5e7dd7Smrg * functions if there are no errors and the device is a multi-function 3324f5e7dd7Smrg * device. 3334f5e7dd7Smrg * 3344f5e7dd7Smrg * (Note, if first_func == 0, header will show whether multifunction 3354f5e7dd7Smrg * device and set multi_function_device. If first_func != 0, then we 3364f5e7dd7Smrg * will force the loop as the user wants a specific function to be 3374f5e7dd7Smrg * checked. 3384f5e7dd7Smrg */ 3394f5e7dd7Smrg for (func = first_func, multi_function_device = B_FALSE; 3404f5e7dd7Smrg ((func <= last_func) && 3414f5e7dd7Smrg ((func == first_func) || (multi_function_device))); 3424f5e7dd7Smrg func++) { 3434f5e7dd7Smrg prg_p->func_no = func; 3444f5e7dd7Smrg 3454f5e7dd7Smrg /* 3464f5e7dd7Smrg * Four things can happen here: 3474f5e7dd7Smrg * 3484f5e7dd7Smrg * 1) ioctl comes back as EFAULT and prg_p->status is 3494f5e7dd7Smrg * PCITOOL_INVALID_ADDRESS. There is no device at this location. 3504f5e7dd7Smrg * 3514f5e7dd7Smrg * 2) ioctl comes back successful and the data comes back as 3524f5e7dd7Smrg * zero. Config space is mapped but no device responded. 3534f5e7dd7Smrg * 3544f5e7dd7Smrg * 3) ioctl comes back successful and the data comes back as 3554f5e7dd7Smrg * non-zero. We've found a device. 3564f5e7dd7Smrg * 3574f5e7dd7Smrg * 4) Some other error occurs in an ioctl. 3584f5e7dd7Smrg */ 3594f5e7dd7Smrg 3604f5e7dd7Smrg prg_p->status = PCITOOL_SUCCESS; 3614f5e7dd7Smrg prg_p->offset = 0; 3624f5e7dd7Smrg prg_p->data = 0; 3634f5e7dd7Smrg prg_p->user_version = PCITOOL_USER_VERSION; 3644f5e7dd7Smrg 3654f5e7dd7Smrg errno = 0; 3664f5e7dd7Smrg if (((rval = ioctl(nexus->fd, PCITOOL_DEVICE_GET_REG, prg_p)) != 0) || 3674f5e7dd7Smrg (prg_p->data == 0xffffffff)) { 3684f5e7dd7Smrg 3694f5e7dd7Smrg /* 3704f5e7dd7Smrg * Accept errno == EINVAL along with status of 3714f5e7dd7Smrg * PCITOOL_OUT_OF_RANGE because some systems 3724f5e7dd7Smrg * don't implement the full range of config space. 3734f5e7dd7Smrg * Leave the loop quietly in this case. 3744f5e7dd7Smrg */ 3754f5e7dd7Smrg if ((errno == EINVAL) || 3764f5e7dd7Smrg (prg_p->status == PCITOOL_OUT_OF_RANGE)) { 3774f5e7dd7Smrg break; 3784f5e7dd7Smrg } 3794f5e7dd7Smrg 3804f5e7dd7Smrg /* 3814f5e7dd7Smrg * Exit silently with ENXIO as this means that there are 3824f5e7dd7Smrg * no devices under the pci root nexus. 3834f5e7dd7Smrg */ 3844f5e7dd7Smrg else if ((errno == ENXIO) && 3854f5e7dd7Smrg (prg_p->status == PCITOOL_IO_ERROR)) { 3864f5e7dd7Smrg break; 3874f5e7dd7Smrg } 3884f5e7dd7Smrg 3894f5e7dd7Smrg /* 3904f5e7dd7Smrg * Expect errno == EFAULT along with status of 3914f5e7dd7Smrg * PCITOOL_INVALID_ADDRESS because there won't be 3924f5e7dd7Smrg * devices at each stop. Quit on any other error. 3934f5e7dd7Smrg */ 3944f5e7dd7Smrg else if (((errno != EFAULT) || 3954f5e7dd7Smrg (prg_p->status != PCITOOL_INVALID_ADDRESS)) && 3964f5e7dd7Smrg (prg_p->data != 0xffffffff)) { 3974f5e7dd7Smrg break; 3984f5e7dd7Smrg } 3994f5e7dd7Smrg 4004f5e7dd7Smrg /* 4014f5e7dd7Smrg * If no function at this location, 4024f5e7dd7Smrg * just advance to the next function. 4034f5e7dd7Smrg */ 4044f5e7dd7Smrg else { 4054f5e7dd7Smrg rval = 0; 4064f5e7dd7Smrg } 4074f5e7dd7Smrg 4084f5e7dd7Smrg /* 4094f5e7dd7Smrg * Data came back as 0. 4104f5e7dd7Smrg * Treat as unresponsive device and check next device. 4114f5e7dd7Smrg */ 4124f5e7dd7Smrg } else if (prg_p->data == 0) { 4134f5e7dd7Smrg rval = 0; 4144f5e7dd7Smrg break; /* Func loop. */ 4154f5e7dd7Smrg 4164f5e7dd7Smrg /* Found something. */ 4174f5e7dd7Smrg } else { 4184f5e7dd7Smrg config_hdr.dwords[0] = (uint32_t)prg_p->data; 4194f5e7dd7Smrg 4204f5e7dd7Smrg /* Get the rest of the PCI header. */ 4214f5e7dd7Smrg if ((rval = get_config_header(nexus->fd, prg_p->bus_no, 4224f5e7dd7Smrg prg_p->dev_no, prg_p->func_no, 4234f5e7dd7Smrg &config_hdr)) != 0) { 4244f5e7dd7Smrg break; 4254f5e7dd7Smrg } 4264f5e7dd7Smrg 4274f5e7dd7Smrg /* 4284f5e7dd7Smrg * Special case for the type of Southbridge found on 4294f5e7dd7Smrg * Ultra-45 and other sun4u fire workstations. 4304f5e7dd7Smrg */ 4314f5e7dd7Smrg if ((config_hdr.dwords[0] == U45_SB_DEVID_VID) && 4324f5e7dd7Smrg (config_hdr.dwords[2] == U45_SB_CLASS_RID)) { 4334f5e7dd7Smrg rval = ECANCELED; 4344f5e7dd7Smrg break; 4354f5e7dd7Smrg } 4364f5e7dd7Smrg 4374f5e7dd7Smrg /* 4384f5e7dd7Smrg * Found one device with bus number, device number and 4394f5e7dd7Smrg * function number. 4404f5e7dd7Smrg */ 4414f5e7dd7Smrg 4424f5e7dd7Smrg pci_base = &pci_sys->devices[pci_sys->num_devices].base; 4434f5e7dd7Smrg 4444f5e7dd7Smrg /* 4454f5e7dd7Smrg * Domain is peer bus?? 4464f5e7dd7Smrg */ 4474f5e7dd7Smrg pci_base->domain = 0; 4484f5e7dd7Smrg pci_base->bus = prg_p->bus_no; 4494f5e7dd7Smrg pci_base->dev = prg_p->dev_no; 4504f5e7dd7Smrg pci_base->func = func; 4514f5e7dd7Smrg 4524f5e7dd7Smrg /* 4534f5e7dd7Smrg * for the format of device_class, see struct pci_device; 4544f5e7dd7Smrg */ 4554f5e7dd7Smrg 4564f5e7dd7Smrg pci_base->device_class = 4574f5e7dd7Smrg (GET_CONFIG_VAL_8(PCI_CONF_BASCLASS) << 16) | 4584f5e7dd7Smrg (GET_CONFIG_VAL_8(PCI_CONF_SUBCLASS) << 8) | 4594f5e7dd7Smrg GET_CONFIG_VAL_8(PCI_CONF_PROGCLASS); 4604f5e7dd7Smrg 4614f5e7dd7Smrg pci_base->revision = GET_CONFIG_VAL_8(PCI_CONF_REVID); 4624f5e7dd7Smrg pci_base->vendor_id = GET_CONFIG_VAL_16(PCI_CONF_VENID); 4634f5e7dd7Smrg pci_base->device_id = GET_CONFIG_VAL_16(PCI_CONF_DEVID); 4644f5e7dd7Smrg pci_base->subvendor_id = GET_CONFIG_VAL_16(PCI_CONF_SUBVENID); 4654f5e7dd7Smrg pci_base->subdevice_id = GET_CONFIG_VAL_16(PCI_CONF_SUBSYSID); 4664f5e7dd7Smrg 4674f5e7dd7Smrg pci_sys->devices[pci_sys->num_devices].header_type 4684f5e7dd7Smrg = GET_CONFIG_VAL_8(PCI_CONF_HEADER); 4694f5e7dd7Smrg 4704f5e7dd7Smrg#ifdef DEBUG 4714f5e7dd7Smrg fprintf(stderr, 4724f5e7dd7Smrg "nexus = %s, busno = %x, devno = %x, funcno = %x\n", 4734f5e7dd7Smrg nexus->path, prg_p->bus_no, prg_p->dev_no, func); 4744f5e7dd7Smrg#endif 4754f5e7dd7Smrg 4764f5e7dd7Smrg if (pci_sys->num_devices < (MAX_DEVICES - 1)) { 4774f5e7dd7Smrg pci_sys->num_devices++; 4784f5e7dd7Smrg } else { 4794f5e7dd7Smrg (void) fprintf(stderr, 4804f5e7dd7Smrg "Maximum number of PCI devices found," 4814f5e7dd7Smrg " discarding additional devices\n"); 4824f5e7dd7Smrg } 4834f5e7dd7Smrg 4844f5e7dd7Smrg 4854f5e7dd7Smrg /* 4864f5e7dd7Smrg * Accommodate devices which state their 4874f5e7dd7Smrg * multi-functionality only in their function 0 config 4884f5e7dd7Smrg * space. Note multi-functionality throughout probing 4894f5e7dd7Smrg * of all of this device's functions. 4904f5e7dd7Smrg */ 4914f5e7dd7Smrg if (config_hdr.bytes[PCI_CONF_HEADER] & PCI_HEADER_MULTI) { 4924f5e7dd7Smrg multi_function_device = B_TRUE; 4934f5e7dd7Smrg } 4944f5e7dd7Smrg } 4954f5e7dd7Smrg } 4964f5e7dd7Smrg 4974f5e7dd7Smrg return (rval); 4984f5e7dd7Smrg} 4994f5e7dd7Smrg 5004f5e7dd7Smrg/* 5014f5e7dd7Smrg * This function is called from di_walk_minor() when any PROBE is processed 5024f5e7dd7Smrg */ 5034f5e7dd7Smrgstatic int 5044f5e7dd7Smrgprobe_nexus_node(di_node_t di_node, di_minor_t minor, void *arg) 5054f5e7dd7Smrg{ 5064f5e7dd7Smrg struct pci_system *pci_sys = (struct pci_system *) arg; 5074f5e7dd7Smrg char *nexus_name; 5084f5e7dd7Smrg nexus_t *nexus; 5094f5e7dd7Smrg int fd; 5104f5e7dd7Smrg char nexus_path[MAXPATHLEN]; 5114f5e7dd7Smrg 5124f5e7dd7Smrg di_prop_t prop; 5134f5e7dd7Smrg char *strings; 5144f5e7dd7Smrg int *ints; 5154f5e7dd7Smrg int numval; 5164f5e7dd7Smrg int pci_node = 0; 5174f5e7dd7Smrg int first_bus = 0, last_bus = PCI_REG_BUS_G(PCI_REG_BUS_M); 5184f5e7dd7Smrg 5194f5e7dd7Smrg#ifdef DEBUG 5204f5e7dd7Smrg nexus_name = di_devfs_minor_path(minor); 5214f5e7dd7Smrg fprintf(stderr, "-- device name: %s\n", nexus_name); 5224f5e7dd7Smrg#endif 5234f5e7dd7Smrg 5244f5e7dd7Smrg for (prop = di_prop_next(di_node, NULL); prop != NULL; 5254f5e7dd7Smrg prop = di_prop_next(di_node, prop)) { 5264f5e7dd7Smrg 5274f5e7dd7Smrg const char *prop_name = di_prop_name(prop); 5284f5e7dd7Smrg 5294f5e7dd7Smrg#ifdef DEBUG 5304f5e7dd7Smrg fprintf(stderr, " property: %s\n", prop_name); 5314f5e7dd7Smrg#endif 5324f5e7dd7Smrg 5334f5e7dd7Smrg if (strcmp(prop_name, "device_type") == 0) { 5344f5e7dd7Smrg numval = di_prop_strings(prop, &strings); 5354f5e7dd7Smrg if (numval != 1 || strncmp(strings, "pci", 3) != 0) { 5364f5e7dd7Smrg /* not a PCI node, bail */ 5374f5e7dd7Smrg return (DI_WALK_CONTINUE); 5384f5e7dd7Smrg } 5394f5e7dd7Smrg pci_node = 1; 5404f5e7dd7Smrg } 5414f5e7dd7Smrg else if (strcmp(prop_name, "class-code") == 0) { 5424f5e7dd7Smrg /* not a root bus node, bail */ 5434f5e7dd7Smrg return (DI_WALK_CONTINUE); 5444f5e7dd7Smrg } 5454f5e7dd7Smrg else if (strcmp(prop_name, "bus-range") == 0) { 5464f5e7dd7Smrg numval = di_prop_ints(prop, &ints); 5474f5e7dd7Smrg if (numval == 2) { 5484f5e7dd7Smrg first_bus = ints[0]; 5494f5e7dd7Smrg last_bus = ints[1]; 5504f5e7dd7Smrg } 5514f5e7dd7Smrg } 5524f5e7dd7Smrg } 5534f5e7dd7Smrg 5544f5e7dd7Smrg#ifdef __x86 /* sparc pci nodes don't have the device_type set */ 5554f5e7dd7Smrg if (pci_node != 1) 5564f5e7dd7Smrg return (DI_WALK_CONTINUE); 5574f5e7dd7Smrg#endif 5584f5e7dd7Smrg 5594f5e7dd7Smrg /* we have a PCI root bus node. */ 5604f5e7dd7Smrg nexus = calloc(1, sizeof(nexus_t)); 5614f5e7dd7Smrg if (nexus == NULL) { 5624f5e7dd7Smrg (void) fprintf(stderr, "Error allocating memory for nexus: %s\n", 5634f5e7dd7Smrg strerror(errno)); 5644f5e7dd7Smrg return (DI_WALK_TERMINATE); 5654f5e7dd7Smrg } 5664f5e7dd7Smrg nexus->first_bus = first_bus; 5674f5e7dd7Smrg nexus->last_bus = last_bus; 5684f5e7dd7Smrg 5694f5e7dd7Smrg nexus_name = di_devfs_minor_path(minor); 5704f5e7dd7Smrg if (nexus_name == NULL) { 5714f5e7dd7Smrg (void) fprintf(stderr, "Error getting nexus path: %s\n", 5724f5e7dd7Smrg strerror(errno)); 5734f5e7dd7Smrg free(nexus); 5744f5e7dd7Smrg return (DI_WALK_CONTINUE); 5754f5e7dd7Smrg } 5764f5e7dd7Smrg 5774f5e7dd7Smrg snprintf(nexus_path, sizeof(nexus_path), "/devices%s", nexus_name); 5784f5e7dd7Smrg di_devfs_path_free(nexus_name); 5794f5e7dd7Smrg 5804f5e7dd7Smrg#ifdef DEBUG 5814f5e7dd7Smrg fprintf(stderr, "nexus = %s, bus-range = %d - %d\n", 5824f5e7dd7Smrg nexus_path, first_bus, last_bus); 5834f5e7dd7Smrg#endif 5844f5e7dd7Smrg 5854f5e7dd7Smrg if ((fd = open(nexus_path, O_RDWR)) >= 0) { 5864f5e7dd7Smrg nexus->fd = fd; 5874f5e7dd7Smrg nexus->path = strdup(nexus_path); 5884f5e7dd7Smrg if ((do_probe(nexus, pci_sys) != 0) && (errno != ENXIO)) { 5894f5e7dd7Smrg (void) fprintf(stderr, "Error probing node %s: %s\n", 5904f5e7dd7Smrg nexus_path, strerror(errno)); 5914f5e7dd7Smrg (void) close(fd); 5924f5e7dd7Smrg free(nexus->path); 5934f5e7dd7Smrg free(nexus); 5944f5e7dd7Smrg } else { 5954f5e7dd7Smrg nexus->next = nexus_list; 5964f5e7dd7Smrg nexus_list = nexus; 5974f5e7dd7Smrg } 5984f5e7dd7Smrg } else { 5994f5e7dd7Smrg (void) fprintf(stderr, "Error opening %s: %s\n", 6004f5e7dd7Smrg nexus_path, strerror(errno)); 6014f5e7dd7Smrg free(nexus); 6024f5e7dd7Smrg } 6034f5e7dd7Smrg 6044f5e7dd7Smrg return DI_WALK_CONTINUE; 6054f5e7dd7Smrg} 6064f5e7dd7Smrg 6074f5e7dd7Smrg 6084f5e7dd7Smrg/* 6094f5e7dd7Smrg * Solaris version 6104f5e7dd7Smrg * Probe a given nexus config space for devices. 6114f5e7dd7Smrg * 6124f5e7dd7Smrg * fd is the file descriptor of the nexus. 6134f5e7dd7Smrg * input_args contains commandline options as specified by the user. 6144f5e7dd7Smrg */ 6154f5e7dd7Smrgstatic int 6164f5e7dd7Smrgdo_probe(nexus_t *nexus, struct pci_system *pci_sys) 6174f5e7dd7Smrg{ 6184f5e7dd7Smrg pcitool_reg_t prg; 6194f5e7dd7Smrg uint32_t bus; 6204f5e7dd7Smrg uint8_t dev; 6214f5e7dd7Smrg uint32_t last_bus = nexus->last_bus; 6224f5e7dd7Smrg uint8_t last_dev = PCI_REG_DEV_M >> PCI_REG_DEV_SHIFT; 6234f5e7dd7Smrg uint8_t first_bus = nexus->first_bus; 6244f5e7dd7Smrg uint8_t first_dev = 0; 6254f5e7dd7Smrg int rval = 0; 6264f5e7dd7Smrg 6274f5e7dd7Smrg prg.barnum = 0; /* Config space. */ 6284f5e7dd7Smrg 6294f5e7dd7Smrg /* Must read in 4-byte quantities. */ 6304f5e7dd7Smrg prg.acc_attr = PCITOOL_ACC_ATTR_SIZE_4 + NATIVE_ENDIAN; 6314f5e7dd7Smrg 6324f5e7dd7Smrg prg.data = 0; 6334f5e7dd7Smrg 6344f5e7dd7Smrg /* 6354f5e7dd7Smrg * Loop through all valid bus / dev / func combinations to check for 6364f5e7dd7Smrg * all devices, with the following exceptions: 6374f5e7dd7Smrg * 6384f5e7dd7Smrg * When nothing is found at function 0 of a bus / dev combination, skip 6394f5e7dd7Smrg * the other functions of that bus / dev combination. 6404f5e7dd7Smrg * 6414f5e7dd7Smrg * When a found device's function 0 is probed and it is determined that 6424f5e7dd7Smrg * it is not a multifunction device, skip probing of that device's 6434f5e7dd7Smrg * other functions. 6444f5e7dd7Smrg */ 6454f5e7dd7Smrg for (bus = first_bus; ((bus <= last_bus) && (rval == 0)); bus++) { 6464f5e7dd7Smrg prg.bus_no = (uint8_t)bus; 6474f5e7dd7Smrg 6484f5e7dd7Smrg for (dev = first_dev; ((dev <= last_dev) && (rval == 0)); dev++) { 6494f5e7dd7Smrg prg.dev_no = dev; 6504f5e7dd7Smrg rval = probe_dev(nexus, &prg, pci_sys); 6514f5e7dd7Smrg } 6524f5e7dd7Smrg 6534f5e7dd7Smrg /* 6544f5e7dd7Smrg * Ultra-45 southbridge workaround: 6554f5e7dd7Smrg * ECANCELED tells to skip to the next bus. 6564f5e7dd7Smrg */ 6574f5e7dd7Smrg if (rval == ECANCELED) { 6584f5e7dd7Smrg rval = 0; 6594f5e7dd7Smrg } 6604f5e7dd7Smrg } 6614f5e7dd7Smrg 6624f5e7dd7Smrg return (rval); 6634f5e7dd7Smrg} 6644f5e7dd7Smrg 6654f5e7dd7Smrgstatic int 6664f5e7dd7Smrgfind_target_node(di_node_t node, void *arg) 6674f5e7dd7Smrg{ 6684f5e7dd7Smrg int *regbuf = NULL; 6694f5e7dd7Smrg int len = 0; 6704f5e7dd7Smrg uint32_t busno, funcno, devno; 6714f5e7dd7Smrg i_devnode_t *devnode = (i_devnode_t *)arg; 6724f5e7dd7Smrg 6734f5e7dd7Smrg /* 6744f5e7dd7Smrg * Test the property functions, only for testing 6754f5e7dd7Smrg */ 6764f5e7dd7Smrg /* 6774f5e7dd7Smrg void *prop = DI_PROP_NIL; 6784f5e7dd7Smrg 6794f5e7dd7Smrg (void) fprintf(stderr, "start of node 0x%x\n", node->nodeid); 6804f5e7dd7Smrg while ((prop = di_prop_hw_next(node, prop)) != DI_PROP_NIL) { 6814f5e7dd7Smrg int i; 6824f5e7dd7Smrg (void) fprintf(stderr, "name=%s: ", di_prop_name(prop)); 6834f5e7dd7Smrg len = 0; 6844f5e7dd7Smrg if (!strcmp(di_prop_name(prop), "reg")) { 6854f5e7dd7Smrg len = di_prop_ints(prop, ®buf); 6864f5e7dd7Smrg } 6874f5e7dd7Smrg for (i = 0; i < len; i++) { 6884f5e7dd7Smrg fprintf(stderr, "0x%0x.", regbuf[i]); 6894f5e7dd7Smrg } 6904f5e7dd7Smrg fprintf(stderr, "\n"); 6914f5e7dd7Smrg } 6924f5e7dd7Smrg (void) fprintf(stderr, "end of node 0x%x\n", node->nodeid); 6934f5e7dd7Smrg */ 6944f5e7dd7Smrg 6954f5e7dd7Smrg len = di_prop_lookup_ints(DDI_DEV_T_ANY, node, "reg", ®buf); 6964f5e7dd7Smrg 6974f5e7dd7Smrg if (len <= 0) { 6984f5e7dd7Smrg#ifdef DEBUG 6994f5e7dd7Smrg fprintf(stderr, "error = %x\n", errno); 7004f5e7dd7Smrg fprintf(stderr, "can not find assigned-address\n"); 7014f5e7dd7Smrg#endif 7024f5e7dd7Smrg return (DI_WALK_CONTINUE); 7034f5e7dd7Smrg } 7044f5e7dd7Smrg 7054f5e7dd7Smrg busno = PCI_REG_BUS_G(regbuf[0]); 7064f5e7dd7Smrg devno = PCI_REG_DEV_G(regbuf[0]); 7074f5e7dd7Smrg funcno = PCI_REG_FUNC_G(regbuf[0]); 7084f5e7dd7Smrg 7094f5e7dd7Smrg if ((busno == devnode->bus) && 7104f5e7dd7Smrg (devno == devnode->dev) && 7114f5e7dd7Smrg (funcno == devnode->func)) { 7124f5e7dd7Smrg devnode->node = node; 7134f5e7dd7Smrg 7144f5e7dd7Smrg return (DI_WALK_TERMINATE); 7154f5e7dd7Smrg } 7164f5e7dd7Smrg 7174f5e7dd7Smrg return (DI_WALK_CONTINUE); 7184f5e7dd7Smrg} 7194f5e7dd7Smrg 7204f5e7dd7Smrg/* 7214f5e7dd7Smrg * Solaris version 7224f5e7dd7Smrg */ 7234f5e7dd7Smrgstatic int 7244f5e7dd7Smrgpci_device_solx_devfs_probe( struct pci_device * dev ) 7254f5e7dd7Smrg{ 7264f5e7dd7Smrg uint8_t config[256]; 7274f5e7dd7Smrg int err; 7284f5e7dd7Smrg di_node_t rnode = DI_NODE_NIL; 7294f5e7dd7Smrg i_devnode_t args = { 0, 0, 0, DI_NODE_NIL }; 7304f5e7dd7Smrg int *regbuf; 7314f5e7dd7Smrg pci_regspec_t *reg; 7324f5e7dd7Smrg int i; 7334f5e7dd7Smrg pciaddr_t bytes; 7344f5e7dd7Smrg int len = 0; 7354f5e7dd7Smrg uint ent = 0; 7364f5e7dd7Smrg 7374f5e7dd7Smrg err = pci_device_solx_devfs_read( dev, config, 0, 256, & bytes ); 7384f5e7dd7Smrg 7394f5e7dd7Smrg if ( bytes >= 64 ) { 7404f5e7dd7Smrg struct pci_device_private *priv = 7414f5e7dd7Smrg (struct pci_device_private *) dev; 7424f5e7dd7Smrg 7434f5e7dd7Smrg dev->vendor_id = (uint16_t)config[0] + ((uint16_t)config[1] << 8); 7444f5e7dd7Smrg dev->device_id = (uint16_t)config[2] + ((uint16_t)config[3] << 8); 7454f5e7dd7Smrg dev->device_class = (uint32_t)config[9] + 7464f5e7dd7Smrg ((uint32_t)config[10] << 8) + 7474f5e7dd7Smrg ((uint16_t)config[11] << 16); 7484f5e7dd7Smrg 7494f5e7dd7Smrg /* 7504f5e7dd7Smrg * device class code is already there. 7514f5e7dd7Smrg * see probe_dev function. 7524f5e7dd7Smrg */ 7534f5e7dd7Smrg dev->revision = config[8]; 7544f5e7dd7Smrg dev->subvendor_id = (uint16_t)config[44] + ((uint16_t)config[45] << 8); 7554f5e7dd7Smrg dev->subdevice_id = (uint16_t)config[46] + ((uint16_t)config[47] << 8); 7564f5e7dd7Smrg dev->irq = config[60]; 7574f5e7dd7Smrg 7584f5e7dd7Smrg priv->header_type = config[14]; 7594f5e7dd7Smrg /* 7604f5e7dd7Smrg * starting to find if it is MEM/MEM64/IO 7614f5e7dd7Smrg * using libdevinfo 7624f5e7dd7Smrg */ 7634f5e7dd7Smrg if ((rnode = di_init("/", DINFOCPYALL)) == DI_NODE_NIL) { 7644f5e7dd7Smrg err = errno; 7654f5e7dd7Smrg (void) fprintf(stderr, "di_init failed: %s\n", strerror(errno)); 7664f5e7dd7Smrg } else { 7674f5e7dd7Smrg args.bus = dev->bus; 7684f5e7dd7Smrg args.dev = dev->dev; 7694f5e7dd7Smrg args.func = dev->func; 7704f5e7dd7Smrg (void) di_walk_node(rnode, DI_WALK_CLDFIRST, 7714f5e7dd7Smrg (void *)&args, find_target_node); 7724f5e7dd7Smrg } 7734f5e7dd7Smrg } 7744f5e7dd7Smrg if (args.node != DI_NODE_NIL) { 7754f5e7dd7Smrg /* 7764f5e7dd7Smrg * It will succeed for sure, because it was 7774f5e7dd7Smrg * successfully called in find_target_node 7784f5e7dd7Smrg */ 7794f5e7dd7Smrg len = di_prop_lookup_ints(DDI_DEV_T_ANY, args.node, 7804f5e7dd7Smrg "assigned-addresses", 7814f5e7dd7Smrg ®buf); 7824f5e7dd7Smrg 7834f5e7dd7Smrg } 7844f5e7dd7Smrg 7854f5e7dd7Smrg if (len <= 0) 7864f5e7dd7Smrg goto cleanup; 7874f5e7dd7Smrg 7884f5e7dd7Smrg 7894f5e7dd7Smrg /* 7904f5e7dd7Smrg * how to find the size of rom??? 7914f5e7dd7Smrg * if the device has expansion rom, 7924f5e7dd7Smrg * it must be listed in the last 7934f5e7dd7Smrg * cells because solaris find probe 7944f5e7dd7Smrg * the base address from offset 0x10 7954f5e7dd7Smrg * to 0x30h. So only check the last 7964f5e7dd7Smrg * item. 7974f5e7dd7Smrg */ 7984f5e7dd7Smrg reg = (pci_regspec_t *)®buf[len - CELL_NUMS_1275]; 7994f5e7dd7Smrg if (PCI_REG_REG_G(reg->pci_phys_hi) == PCI_CONF_ROM) { 8004f5e7dd7Smrg /* 8014f5e7dd7Smrg * rom can only be 32 bits 8024f5e7dd7Smrg */ 8034f5e7dd7Smrg dev->rom_size = reg->pci_size_low; 8044f5e7dd7Smrg len = len - CELL_NUMS_1275; 8054f5e7dd7Smrg } 8064f5e7dd7Smrg else { 8074f5e7dd7Smrg /* 8084f5e7dd7Smrg * size default to 64K and base address 8094f5e7dd7Smrg * default to 0xC0000 8104f5e7dd7Smrg */ 8114f5e7dd7Smrg dev->rom_size = 0x10000; 8124f5e7dd7Smrg } 8134f5e7dd7Smrg 8144f5e7dd7Smrg /* 8154f5e7dd7Smrg * Solaris has its own BAR index. 8164f5e7dd7Smrg * Linux give two region slot for 64 bit address. 8174f5e7dd7Smrg */ 8184f5e7dd7Smrg for (i = 0; i < len; i = i + CELL_NUMS_1275) { 8194f5e7dd7Smrg 8204f5e7dd7Smrg reg = (pci_regspec_t *)®buf[i]; 8214f5e7dd7Smrg ent = reg->pci_phys_hi & 0xff; 8224f5e7dd7Smrg /* 8234f5e7dd7Smrg * G35 broken in BAR0 8244f5e7dd7Smrg */ 8254f5e7dd7Smrg ent = (ent - PCI_CONF_BASE0) >> 2; 8264f5e7dd7Smrg if (ent >= 6) { 8274f5e7dd7Smrg fprintf(stderr, "error ent = %d\n", ent); 8284f5e7dd7Smrg break; 8294f5e7dd7Smrg } 8304f5e7dd7Smrg 8314f5e7dd7Smrg /* 8324f5e7dd7Smrg * non relocatable resource is excluded 8334f5e7dd7Smrg * such like 0xa0000, 0x3b0. If it is met, 8344f5e7dd7Smrg * the loop is broken; 8354f5e7dd7Smrg */ 8364f5e7dd7Smrg if (!PCI_REG_REG_G(reg->pci_phys_hi)) 8374f5e7dd7Smrg break; 8384f5e7dd7Smrg 8394f5e7dd7Smrg if (reg->pci_phys_hi & PCI_PREFETCH_B) { 8404f5e7dd7Smrg dev->regions[ent].is_prefetchable = 1; 8414f5e7dd7Smrg } 8424f5e7dd7Smrg 8434f5e7dd7Smrg 8444f5e7dd7Smrg /* 8454f5e7dd7Smrg * We split the shift count 32 into two 16 to 8464f5e7dd7Smrg * avoid the complaining of the compiler 8474f5e7dd7Smrg */ 8484f5e7dd7Smrg dev->regions[ent].base_addr = reg->pci_phys_low + 8494f5e7dd7Smrg ((reg->pci_phys_mid << 16) << 16); 8504f5e7dd7Smrg dev->regions[ent].size = reg->pci_size_low + 8514f5e7dd7Smrg ((reg->pci_size_hi << 16) << 16); 8524f5e7dd7Smrg 8534f5e7dd7Smrg switch (reg->pci_phys_hi & PCI_REG_ADDR_M) { 8544f5e7dd7Smrg case PCI_ADDR_IO: 8554f5e7dd7Smrg dev->regions[ent].is_IO = 1; 8564f5e7dd7Smrg break; 8574f5e7dd7Smrg case PCI_ADDR_MEM32: 8584f5e7dd7Smrg break; 8594f5e7dd7Smrg case PCI_ADDR_MEM64: 8604f5e7dd7Smrg dev->regions[ent].is_64 = 1; 8614f5e7dd7Smrg /* 8624f5e7dd7Smrg * Skip one slot for 64 bit address 8634f5e7dd7Smrg */ 8644f5e7dd7Smrg break; 8654f5e7dd7Smrg } 8664f5e7dd7Smrg } 8674f5e7dd7Smrg 8684f5e7dd7Smrg cleanup: 8694f5e7dd7Smrg if (rnode != DI_NODE_NIL) { 8704f5e7dd7Smrg di_fini(rnode); 8714f5e7dd7Smrg } 8724f5e7dd7Smrg return (err); 8734f5e7dd7Smrg} 8744f5e7dd7Smrg 8754f5e7dd7Smrg/* 8764f5e7dd7Smrg * Solaris version: read the VGA ROM data 8774f5e7dd7Smrg */ 8784f5e7dd7Smrgstatic int 8794f5e7dd7Smrgpci_device_solx_devfs_read_rom( struct pci_device * dev, void * buffer ) 8804f5e7dd7Smrg{ 8814f5e7dd7Smrg int err; 8824f5e7dd7Smrg struct pci_device_mapping prom = { 8834f5e7dd7Smrg .base = 0xC0000, 8844f5e7dd7Smrg .size = dev->rom_size, 8854f5e7dd7Smrg .flags = 0 8864f5e7dd7Smrg }; 8874f5e7dd7Smrg 8884f5e7dd7Smrg err = pci_device_solx_devfs_map_range(dev, &prom); 8894f5e7dd7Smrg if (err == 0) { 8904f5e7dd7Smrg (void) bcopy(prom.memory, buffer, dev->rom_size); 8914f5e7dd7Smrg 8924f5e7dd7Smrg if (munmap(prom.memory, dev->rom_size) == -1) { 8934f5e7dd7Smrg err = errno; 8944f5e7dd7Smrg } 8954f5e7dd7Smrg } 8964f5e7dd7Smrg return err; 8974f5e7dd7Smrg} 8984f5e7dd7Smrg 8994f5e7dd7Smrg/* 9004f5e7dd7Smrg * solaris version: Read the configurations space of the devices 9014f5e7dd7Smrg */ 9024f5e7dd7Smrgstatic int 9034f5e7dd7Smrgpci_device_solx_devfs_read( struct pci_device * dev, void * data, 9044f5e7dd7Smrg pciaddr_t offset, pciaddr_t size, 9054f5e7dd7Smrg pciaddr_t * bytes_read ) 9064f5e7dd7Smrg{ 9074f5e7dd7Smrg pcitool_reg_t cfg_prg; 9084f5e7dd7Smrg int err = 0; 9094f5e7dd7Smrg int i = 0; 9104f5e7dd7Smrg nexus_t *nexus = find_nexus_for_bus(dev->bus); 9114f5e7dd7Smrg 9124f5e7dd7Smrg *bytes_read = 0; 9134f5e7dd7Smrg 9144f5e7dd7Smrg if ( nexus == NULL ) { 9154f5e7dd7Smrg return ENODEV; 9164f5e7dd7Smrg } 9174f5e7dd7Smrg 9184f5e7dd7Smrg cfg_prg.offset = offset; 9194f5e7dd7Smrg cfg_prg.acc_attr = PCITOOL_ACC_ATTR_SIZE_1 + NATIVE_ENDIAN; 9204f5e7dd7Smrg cfg_prg.bus_no = dev->bus; 9214f5e7dd7Smrg cfg_prg.dev_no = dev->dev; 9224f5e7dd7Smrg cfg_prg.func_no = dev->func; 9234f5e7dd7Smrg cfg_prg.barnum = 0; 9244f5e7dd7Smrg cfg_prg.user_version = PCITOOL_USER_VERSION; 9254f5e7dd7Smrg 9264f5e7dd7Smrg for (i = 0; i < size; i += PCITOOL_ACC_ATTR_SIZE(PCITOOL_ACC_ATTR_SIZE_1)) 9274f5e7dd7Smrg { 9284f5e7dd7Smrg cfg_prg.offset = offset + i; 9294f5e7dd7Smrg 9304f5e7dd7Smrg if ((err = ioctl(nexus->fd, PCITOOL_DEVICE_GET_REG, &cfg_prg)) != 0) { 9314f5e7dd7Smrg fprintf(stderr, "read bdf<%s,%x,%x,%x,%llx> config space failure\n", 9324f5e7dd7Smrg nexus->path, 9334f5e7dd7Smrg cfg_prg.bus_no, 9344f5e7dd7Smrg cfg_prg.dev_no, 9354f5e7dd7Smrg cfg_prg.func_no, 9364f5e7dd7Smrg cfg_prg.offset); 9374f5e7dd7Smrg fprintf(stderr, "Failure cause = %x\n", err); 9384f5e7dd7Smrg break; 9394f5e7dd7Smrg } 9404f5e7dd7Smrg 9414f5e7dd7Smrg ((uint8_t *)data)[i] = (uint8_t)cfg_prg.data; 9424f5e7dd7Smrg /* 9434f5e7dd7Smrg * DWORDS Offset or bytes Offset ?? 9444f5e7dd7Smrg */ 9454f5e7dd7Smrg } 9464f5e7dd7Smrg *bytes_read = i; 9474f5e7dd7Smrg 9484f5e7dd7Smrg return (err); 9494f5e7dd7Smrg} 9504f5e7dd7Smrg 9514f5e7dd7Smrg/* 9524f5e7dd7Smrg * Solaris version 9534f5e7dd7Smrg */ 9544f5e7dd7Smrgstatic int 9554f5e7dd7Smrgpci_device_solx_devfs_write( struct pci_device * dev, const void * data, 9564f5e7dd7Smrg pciaddr_t offset, pciaddr_t size, 9574f5e7dd7Smrg pciaddr_t * bytes_written ) 9584f5e7dd7Smrg{ 9594f5e7dd7Smrg pcitool_reg_t cfg_prg; 9604f5e7dd7Smrg int err = 0; 9614f5e7dd7Smrg int cmd; 9624f5e7dd7Smrg nexus_t *nexus = find_nexus_for_bus(dev->bus); 9634f5e7dd7Smrg 9644f5e7dd7Smrg if ( bytes_written != NULL ) { 9654f5e7dd7Smrg *bytes_written = 0; 9664f5e7dd7Smrg } 9674f5e7dd7Smrg 9684f5e7dd7Smrg if ( nexus == NULL ) { 9694f5e7dd7Smrg return ENODEV; 9704f5e7dd7Smrg } 9714f5e7dd7Smrg 9724f5e7dd7Smrg cfg_prg.offset = offset; 9734f5e7dd7Smrg switch (size) { 9744f5e7dd7Smrg case 1: 9754f5e7dd7Smrg cfg_prg.acc_attr = PCITOOL_ACC_ATTR_SIZE_1 + NATIVE_ENDIAN; 9764f5e7dd7Smrg break; 9774f5e7dd7Smrg case 2: 9784f5e7dd7Smrg cfg_prg.acc_attr = PCITOOL_ACC_ATTR_SIZE_2 + NATIVE_ENDIAN; 9794f5e7dd7Smrg break; 9804f5e7dd7Smrg case 4: 9814f5e7dd7Smrg cfg_prg.acc_attr = PCITOOL_ACC_ATTR_SIZE_4 + NATIVE_ENDIAN; 9824f5e7dd7Smrg break; 9834f5e7dd7Smrg case 8: 9844f5e7dd7Smrg cfg_prg.acc_attr = PCITOOL_ACC_ATTR_SIZE_8 + NATIVE_ENDIAN; 9854f5e7dd7Smrg break; 9864f5e7dd7Smrg default: 9874f5e7dd7Smrg return EINVAL; 9884f5e7dd7Smrg } 9894f5e7dd7Smrg cfg_prg.bus_no = dev->bus; 9904f5e7dd7Smrg cfg_prg.dev_no = dev->dev; 9914f5e7dd7Smrg cfg_prg.func_no = dev->func; 9924f5e7dd7Smrg cfg_prg.barnum = 0; 9934f5e7dd7Smrg cfg_prg.user_version = PCITOOL_USER_VERSION; 9944f5e7dd7Smrg cfg_prg.data = *((uint64_t *)data); 9954f5e7dd7Smrg 9964f5e7dd7Smrg /* 9974f5e7dd7Smrg * Check if this device is bridge device. 9984f5e7dd7Smrg * If it is, it is also a nexus node??? 9994f5e7dd7Smrg * It seems that there is no explicit 10004f5e7dd7Smrg * PCI nexus device for X86, so not applicable 10014f5e7dd7Smrg * from pcitool_bus_reg_ops in pci_tools.c 10024f5e7dd7Smrg */ 10034f5e7dd7Smrg cmd = PCITOOL_DEVICE_SET_REG; 10044f5e7dd7Smrg 10054f5e7dd7Smrg if ((err = ioctl(nexus->fd, cmd, &cfg_prg)) != 0) { 10064f5e7dd7Smrg return (err); 10074f5e7dd7Smrg } 10084f5e7dd7Smrg *bytes_written = size; 10094f5e7dd7Smrg 10104f5e7dd7Smrg return (err); 10114f5e7dd7Smrg} 10124f5e7dd7Smrg 10134f5e7dd7Smrg 10144f5e7dd7Smrg/** 10154f5e7dd7Smrg * Map a memory region for a device using /dev/xsvc. 10164f5e7dd7Smrg * 10174f5e7dd7Smrg * \param dev Device whose memory region is to be mapped. 10184f5e7dd7Smrg * \param map Parameters of the mapping that is to be created. 10194f5e7dd7Smrg * 10204f5e7dd7Smrg * \return 10214f5e7dd7Smrg * Zero on success or an \c errno value on failure. 10224f5e7dd7Smrg */ 10234f5e7dd7Smrgstatic int 10244f5e7dd7Smrgpci_device_solx_devfs_map_range(struct pci_device *dev, 10254f5e7dd7Smrg struct pci_device_mapping *map) 10264f5e7dd7Smrg{ 10274f5e7dd7Smrg const int prot = ((map->flags & PCI_DEV_MAP_FLAG_WRITABLE) != 0) 10284f5e7dd7Smrg ? (PROT_READ | PROT_WRITE) : PROT_READ; 10294f5e7dd7Smrg int err = 0; 10304f5e7dd7Smrg 10314f5e7dd7Smrg /* 10324f5e7dd7Smrg * Still used xsvc to do the user space mapping 10334f5e7dd7Smrg */ 10344f5e7dd7Smrg if (xsvc_fd < 0) { 10354f5e7dd7Smrg if ((xsvc_fd = open("/dev/xsvc", O_RDWR)) < 0) { 10364f5e7dd7Smrg err = errno; 10374f5e7dd7Smrg (void) fprintf(stderr, "can not open /dev/xsvc: %s\n", 10384f5e7dd7Smrg strerror(errno)); 10394f5e7dd7Smrg return err; 10404f5e7dd7Smrg } 10414f5e7dd7Smrg } 10424f5e7dd7Smrg 10434f5e7dd7Smrg map->memory = mmap(NULL, map->size, prot, MAP_SHARED, xsvc_fd, map->base); 10444f5e7dd7Smrg if (map->memory == MAP_FAILED) { 10454f5e7dd7Smrg err = errno; 10464f5e7dd7Smrg 10474f5e7dd7Smrg (void) fprintf(stderr, "map rom region =%llx failed: %s\n", 10484f5e7dd7Smrg map->base, strerror(errno)); 10494f5e7dd7Smrg } 10504f5e7dd7Smrg 10514f5e7dd7Smrg return err; 10524f5e7dd7Smrg} 1053