OSDN Git Service

Solaris: Add domain support for sparc platform
[android-x86/external-libpciaccess.git] / src / solx_devfs.c
index 5e91a14..108fd1f 100644 (file)
@@ -1,6 +1,6 @@
 /*
  * (C) Copyright IBM Corporation 2006
- * Copyright (c) 2007, 2009, 2011, Oracle and/or its affiliates.
+ * Copyright (c) 2007, 2009, 2011, 2012, Oracle and/or its affiliates.
  * All Rights Reserved.
  *
  * Permission is hereby granted, free of charge, to any person obtaining a
 #include <libdevinfo.h>
 #include "pci_tools.h"
 
+#ifdef __x86
+# include <sys/sysi86.h>
+# include <sys/psw.h>
+#endif
+
 #include "pciaccess.h"
 #include "pciaccess_private.h"
 
@@ -66,11 +71,6 @@ typedef struct nexus {
     char *path;                        /* for errors/debugging; fd is all we need */
     char *dev_path;
     struct nexus *next;
-#ifdef __sparc
-    struct pci_device **devlist;
-    volatile size_t num_allocated_elems;
-    volatile size_t num_devices;
-#endif
 } nexus_t;
 
 typedef struct probe_info {
@@ -86,6 +86,7 @@ static int xsvc_fd = -1;
 
 #ifdef __sparc
 static di_prom_handle_t di_phdl;
+static size_t  nexus_count = 0;
 #endif
 
 /*
@@ -126,22 +127,6 @@ static di_prom_handle_t di_phdl;
 # define U45_SB_CLASS_RID      0x06040000
 #endif
 
-#ifdef __sparc
-static nexus_t *
-find_nexus_for_dev(struct pci_device *dev)
-{
-    nexus_t *nexus;
-    int i;
-
-    for (nexus = nexus_list ; nexus != NULL ; nexus = nexus->next) {
-       for (i = 0; i < nexus->num_devices; i++) {
-           if (nexus->devlist[i] == dev)
-               return nexus;
-       }
-    }
-    return NULL;
-}
-#else
 static nexus_t *
 find_nexus_for_bus( int domain, int bus )
 {
@@ -155,7 +140,6 @@ find_nexus_for_bus( int domain, int bus )
     }
     return NULL;
 }
-#endif
 
 #define GET_CONFIG_VAL_8(offset) (config_hdr.bytes[offset])
 #define GET_CONFIG_VAL_16(offset) \
@@ -185,19 +169,6 @@ pci_system_solx_devfs_destroy( void )
        close(nexus->fd);
        free(nexus->path);
        free(nexus->dev_path);
-#ifdef __sparc
-       {
-           struct pci_device *dev;
-           int i;
-
-           for (i = 0; i < nexus->num_devices; i++) {
-               dev = nexus->devlist[i];
-               if (MAPPING_DEV_PATH(dev))
-                   di_devfs_path_free((char *) MAPPING_DEV_PATH(dev));
-           }
-       }
-       free(nexus->devlist);
-#endif
        free(nexus);
     }
     nexus_list = NULL;
@@ -213,6 +184,20 @@ pci_system_solx_devfs_destroy( void )
 #endif
 }
 
+
+#ifdef __sparc
+/*
+ * Release resources per device
+ */
+static void
+pci_system_solx_devfs_destroy_device( struct pci_device *dev )
+{
+   if (MAPPING_DEV_PATH(dev))
+       di_devfs_path_free((char *) MAPPING_DEV_PATH(dev));
+}
+#endif
+
+
 /*
  * Retrieve first 16 dwords of device's config header, except for the first
  * dword.  First 16 dwords are defined by the PCI specification.
@@ -431,25 +416,6 @@ probe_dev(nexus_t *nexus, pcitool_reg_t *prg_p, probe_info_t *pinfo)
                pinfo->devices = new_devs;
            }
 
-#ifdef __sparc
-           nexus->devlist[nexus->num_devices++] = pci_base;
-
-           if (nexus->num_devices == nexus->num_allocated_elems) {
-               struct pci_device **new_devs;
-               size_t new_num_elems = nexus->num_allocated_elems * 2;
-
-               new_devs = realloc(nexus->devlist,
-                       new_num_elems * sizeof (struct pci_device *));
-               if (new_devs == NULL)
-                   return (rval);
-               (void) memset(&new_devs[nexus->num_devices], 0,
-                       nexus->num_allocated_elems *
-                       sizeof (struct pci_device *));
-               nexus->num_allocated_elems = new_num_elems;
-               nexus->devlist = new_devs;
-           }
-#endif
-
            /*
             * Accommodate devices which state their
             * multi-functionality only in their function 0 config
@@ -591,12 +557,16 @@ probe_nexus_node(di_node_t di_node, di_minor_t minor, void *arg)
 #endif
            }
        }
+#ifdef __sparc
+       domain = nexus_count;
+#else
        else if (strcmp(prop_name, "pciseg") == 0) {
            numval = di_prop_ints(prop, &ints);
            if (numval == 1) {
                domain = ints[0];
            }
        }
+#endif
     }
 
 #ifdef __sparc
@@ -636,15 +606,7 @@ probe_nexus_node(di_node_t di_node, di_minor_t minor, void *arg)
     nexus->domain = domain;
 
 #ifdef __sparc
-    if ((nexus->devlist = calloc(INITIAL_NUM_DEVICES,
-                       sizeof (struct pci_device *))) == NULL) {
-       (void) fprintf(stderr, "Error allocating memory for nexus devlist: %s\n",
-                       strerror(errno));
-       free (nexus);
-       return (DI_WALK_TERMINATE);
-    }
-    nexus->num_allocated_elems = INITIAL_NUM_DEVICES;
-    nexus->num_devices = 0;
+    nexus_count++;
 #endif
 
     nexus_name = di_devfs_minor_path(minor);
@@ -663,7 +625,7 @@ probe_nexus_node(di_node_t di_node, di_minor_t minor, void *arg)
            nexus_path, first_bus, last_bus);
 #endif
 
-    if ((fd = open(nexus_path, O_RDWR)) >= 0) {
+    if ((fd = open(nexus_path, O_RDWR | O_CLOEXEC)) >= 0) {
        nexus->fd = fd;
        nexus->path = strdup(nexus_path);
        nexus_dev_path = di_devfs_path(di_node);
@@ -763,13 +725,11 @@ pci_device_solx_devfs_probe( struct pci_device * dev )
     int i;
     int len = 0;
     uint ent = 0;
+    struct pci_device_private *priv =
+       (struct pci_device_private *) dev;
     nexus_t *nexus;
 
-#ifdef __sparc
-    if ( (nexus = find_nexus_for_dev(dev)) == NULL )
-#else
     if ( (nexus = find_nexus_for_bus(dev->domain, dev->bus)) == NULL )
-#endif
        return ENODEV;
 
     /*
@@ -788,10 +748,13 @@ pci_device_solx_devfs_probe( struct pci_device * dev )
     }
 
     if (args.node != DI_NODE_NIL) {
+       int *prop;
 #ifdef __sparc
        di_minor_t minor;
 #endif
 
+       priv->is_primary = 0;
+
 #ifdef __sparc
        if (minor = di_minor_next(args.node, DI_MINOR_NIL))
            MAPPING_DEV_PATH(dev) = di_devfs_minor_path (minor);
@@ -799,6 +762,12 @@ pci_device_solx_devfs_probe( struct pci_device * dev )
            MAPPING_DEV_PATH(dev) = NULL;
 #endif
 
+       if (di_prop_lookup_ints(DDI_DEV_T_ANY, args.node,
+                               "primary-controller", &prop) >= 1) {
+           if (prop[0])
+               priv->is_primary = 1;
+       }
+
        /*
         * It will succeed for sure, because it was
         * successfully called in find_target_node
@@ -818,83 +787,69 @@ pci_device_solx_devfs_probe( struct pci_device * dev )
     if (len <= 0)
        goto cleanup;
 
-
-    /*
-     * how to find the size of rom???
-     * if the device has expansion rom,
-     * it must be listed in the last
-     * cells because solaris find probe
-     * the base address from offset 0x10
-     * to 0x30h. So only check the last
-     * item.
-     */
-    reg = (pci_regspec_t *)&regbuf[len - CELL_NUMS_1275];
-    if (PCI_REG_REG_G(reg->pci_phys_hi) == PCI_CONF_ROM) {
-       /*
-        * rom can only be 32 bits
-        */
-       dev->rom_size = reg->pci_size_low;
-       len = len - CELL_NUMS_1275;
-    }
-    else {
-       /*
-        * size default to 64K and base address
-        * default to 0xC0000
-        */
-       dev->rom_size = 0x10000;
-    }
-
     /*
-     * Solaris has its own BAR index.
+     * Each BAR address get its own region slot in sequence.
+     * 32 bit BAR:
+     * BAR 0x10 -> slot0, BAR 0x14 -> slot1...
+     * 64 bit BAR:
+     * BAR 0x10 -> slot0, BAR 0x18 -> slot2...,
+     * slot1 is part of BAR 0x10
      * Linux give two region slot for 64 bit address.
      */
     for (i = 0; i < len; i = i + CELL_NUMS_1275) {
 
        reg = (pci_regspec_t *)&regbuf[i];
        ent = reg->pci_phys_hi & 0xff;
-       /*
-        * G35 broken in BAR0
-        */
-       ent = (ent - PCI_CONF_BASE0) >> 2;
-       if (ent >= 6) {
+
+       if (ent > PCI_CONF_ROM) {
            fprintf(stderr, "error ent = %d\n", ent);
            break;
        }
-
        /*
-        * non relocatable resource is excluded
-        * such like 0xa0000, 0x3b0. If it is met,
-        * the loop is broken;
+        * G35 broken in BAR0
         */
-       if (!PCI_REG_REG_G(reg->pci_phys_hi))
+       if (ent < PCI_CONF_BASE0) {
+           /*
+            * VGA resource here and ignore it
+            */
            break;
+       } else if (ent == PCI_CONF_ROM) {
+           priv->rom_base = reg->pci_phys_low |
+               ((uint64_t)reg->pci_phys_mid << 32);
+           dev->rom_size = reg->pci_size_low;
+       } else {
+           ent = (ent - PCI_CONF_BASE0) >> 2;
+           /*
+            * non relocatable resource is excluded
+            * such like 0xa0000, 0x3b0. If it is met,
+            * the loop is broken;
+            */
+           if (!PCI_REG_REG_G(reg->pci_phys_hi))
+               break;
 
-       if (reg->pci_phys_hi & PCI_PREFETCH_B) {
-           dev->regions[ent].is_prefetchable = 1;
-       }
+           if (reg->pci_phys_hi & PCI_PREFETCH_B) {
+               dev->regions[ent].is_prefetchable = 1;
+           }
 
 
-       /*
-        * We split the shift count 32 into two 16 to
-        * avoid the complaining of the compiler
-        */
-       dev->regions[ent].base_addr = reg->pci_phys_low +
-           ((reg->pci_phys_mid << 16) << 16);
-       dev->regions[ent].size = reg->pci_size_low +
-           ((reg->pci_size_hi << 16) << 16);
-
-       switch (reg->pci_phys_hi & PCI_REG_ADDR_M) {
-           case PCI_ADDR_IO:
-               dev->regions[ent].is_IO = 1;
-               break;
-           case PCI_ADDR_MEM32:
-               break;
-           case PCI_ADDR_MEM64:
-               dev->regions[ent].is_64 = 1;
-               /*
-                * Skip one slot for 64 bit address
-                */
-               break;
+           dev->regions[ent].base_addr = reg->pci_phys_low |
+               ((uint64_t)reg->pci_phys_mid << 32);
+           dev->regions[ent].size = reg->pci_size_low |
+               ((uint64_t)reg->pci_size_hi << 32);
+
+           switch (reg->pci_phys_hi & PCI_REG_ADDR_M) {
+               case PCI_ADDR_IO:
+                   dev->regions[ent].is_IO = 1;
+                   break;
+               case PCI_ADDR_MEM32:
+                   break;
+               case PCI_ADDR_MEM64:
+                   dev->regions[ent].is_64 = 1;
+                   /*
+                    * Skip one slot for 64 bit address
+                    */
+                   break;
+           }
        }
     }
 
@@ -906,7 +861,7 @@ pci_device_solx_devfs_probe( struct pci_device * dev )
 }
 
 /**
- * Map a memory region for a device using /dev/xsvc.
+ * Map a memory region for a device using /dev/xsvc (x86) or fb device (sparc)
  *
  * \param dev   Device whose memory region is to be mapped.
  * \param map   Parameters of the mapping that is to be created.
@@ -922,39 +877,40 @@ pci_device_solx_devfs_map_range(struct pci_device *dev,
                        ? (PROT_READ | PROT_WRITE) : PROT_READ;
     int err = 0;
 
-#ifdef __sparc
-    char       map_dev[128];
+    const char *map_dev;
     int                map_fd;
 
-    if (MAPPING_DEV_PATH(dev))
-       snprintf(map_dev, sizeof (map_dev), "%s%s", "/devices", MAPPING_DEV_PATH(dev));
-    else
-       strcpy (map_dev, "/dev/fb0");
+#ifdef __sparc
+    char       map_dev_buf[128];
 
-    if ((map_fd = open(map_dev, O_RDWR)) < 0) {
-       err = errno;
-       (void) fprintf(stderr, "can not open %s: %s\n", map_dev,
-                          strerror(errno));
-       return err;
+    if (MAPPING_DEV_PATH(dev)) {
+       snprintf(map_dev_buf, sizeof (map_dev_buf), "%s%s",
+                "/devices", MAPPING_DEV_PATH(dev));
+       map_dev = map_dev_buf;
     }
+    else
+       map_dev = "/dev/fb0";
 
-    map->memory = mmap(NULL, map->size, prot, MAP_SHARED, map_fd, map->base);
+    map_fd = -1;
 #else
     /*
-     * Still used xsvc to do the user space mapping
+     * Still uses xsvc to do the user space mapping on x86/x64,
+     * caches open fd across multiple calls.
      */
-    if (xsvc_fd < 0) {
-       if ((xsvc_fd = open("/dev/xsvc", O_RDWR)) < 0) {
+    map_dev = "/dev/xsvc";
+    map_fd = xsvc_fd;
+#endif
+
+    if (map_fd < 0) {
+       if ((map_fd = open(map_dev, O_RDWR | O_CLOEXEC)) < 0) {
            err = errno;
-           (void) fprintf(stderr, "can not open /dev/xsvc: %s\n",
+           (void) fprintf(stderr, "can not open %s: %s\n", map_dev,
                           strerror(errno));
            return err;
        }
     }
 
-    map->memory = mmap(NULL, map->size, prot, MAP_SHARED, xsvc_fd, map->base);
-#endif
-
+    map->memory = mmap(NULL, map->size, prot, MAP_SHARED, map_fd, map->base);
     if (map->memory == MAP_FAILED) {
        err = errno;
 
@@ -978,15 +934,22 @@ pci_device_solx_devfs_read_rom( struct pci_device * dev, void * buffer )
     int err;
     struct pci_device_mapping prom = {
        .base = 0xC0000,
-       .size = dev->rom_size,
+       .size = 0x10000,
        .flags = 0
     };
+    struct pci_device_private *priv =
+       (struct pci_device_private *) dev;
+
+    if (priv->rom_base) {
+       prom.base = priv->rom_base;
+       prom.size = dev->rom_size;
+    }
 
     err = pci_device_solx_devfs_map_range(dev, &prom);
     if (err == 0) {
        (void) bcopy(prom.memory, buffer, dev->rom_size);
 
-       if (munmap(prom.memory, dev->rom_size) == -1) {
+       if (munmap(prom.memory, prom.size) == -1) {
            err = errno;
        }
     }
@@ -1006,11 +969,7 @@ pci_device_solx_devfs_read( struct pci_device * dev, void * data,
     int i = 0;
     nexus_t *nexus;
 
-#ifdef __sparc
-    nexus = find_nexus_for_dev(dev);
-#else
     nexus = find_nexus_for_bus(dev->domain, dev->bus);
-#endif
 
     *bytes_read = 0;
 
@@ -1064,11 +1023,7 @@ pci_device_solx_devfs_write( struct pci_device * dev, const void * data,
     int cmd;
     nexus_t *nexus;
 
-#ifdef __sparc
-    nexus = find_nexus_for_dev(dev);
-#else
     nexus = find_nexus_for_bus(dev->domain, dev->bus);
-#endif
 
     if ( bytes_written != NULL ) {
        *bytes_written = 0;
@@ -1122,11 +1077,142 @@ pci_device_solx_devfs_write( struct pci_device * dev, const void * data,
     return (err);
 }
 
+static int pci_device_solx_devfs_boot_vga(struct pci_device *dev)
+{
+    struct pci_device_private *priv =
+       (struct pci_device_private *) dev;
 
+    return (priv->is_primary);
+
+}
+
+static struct pci_io_handle *
+pci_device_solx_devfs_open_legacy_io(struct pci_io_handle *ret,
+                                    struct pci_device *dev,
+                                    pciaddr_t base, pciaddr_t size)
+{
+#ifdef __x86
+    if (sysi86(SI86V86, V86SC_IOPL, PS_IOPL) == 0) {
+       ret->base = base;
+       ret->size = size;
+       return ret;
+    }
+#endif
+    return NULL;
+}
+
+static uint32_t
+pci_device_solx_devfs_read32(struct pci_io_handle *handle, uint32_t reg)
+{
+#ifdef __x86
+    uint16_t port = (uint16_t) (handle->base + reg);
+    uint32_t ret;
+    __asm__ __volatile__("inl %1,%0":"=a"(ret):"d"(port));
+    return ret;
+#else
+    return *(uint32_t *)((uintptr_t)handle->memory + reg);
+#endif
+}
+
+static uint16_t
+pci_device_solx_devfs_read16(struct pci_io_handle *handle, uint32_t reg)
+{
+#ifdef __x86
+    uint16_t port = (uint16_t) (handle->base + reg);
+    uint16_t ret;
+    __asm__ __volatile__("inw %1,%0":"=a"(ret):"d"(port));
+    return ret;
+#else
+    return *(uint16_t *)((uintptr_t)handle->memory + reg);
+#endif
+}
+
+static uint8_t
+pci_device_solx_devfs_read8(struct pci_io_handle *handle, uint32_t reg)
+{
+#ifdef __x86
+    uint16_t port = (uint16_t) (handle->base + reg);
+    uint8_t ret;
+    __asm__ __volatile__("inb %1,%0":"=a"(ret):"d"(port));
+    return ret;
+#else
+    return *(uint8_t *)((uintptr_t)handle->memory + reg);
+#endif
+}
+
+static void
+pci_device_solx_devfs_write32(struct pci_io_handle *handle, uint32_t reg,
+    uint32_t data)
+{
+#ifdef __x86
+      uint16_t port = (uint16_t) (handle->base + reg);
+      __asm__ __volatile__("outl %0,%1"::"a"(data), "d"(port));
+#else
+      *(uint16_t *)((uintptr_t)handle->memory + reg) = data;
+#endif
+}
+
+static void
+pci_device_solx_devfs_write16(struct pci_io_handle *handle, uint32_t reg,
+    uint16_t data)
+{
+#ifdef __x86
+      uint16_t port = (uint16_t) (handle->base + reg);
+      __asm__ __volatile__("outw %0,%1"::"a"(data), "d"(port));
+#else
+    *(uint8_t *)((uintptr_t)handle->memory + reg) = data;
+#endif
+}
+
+static void
+pci_device_solx_devfs_write8(struct pci_io_handle *handle, uint32_t reg,
+    uint8_t data)
+{
+#ifdef __x86
+      uint16_t port = (uint16_t) (handle->base + reg);
+      __asm__ __volatile__("outb %0,%1"::"a"(data), "d"(port));
+#else
+      *(uint32_t *)((uintptr_t)handle->memory + reg) = data;
+#endif
+}
+
+static int
+pci_device_solx_devfs_map_legacy(struct pci_device *dev, pciaddr_t base,
+                                pciaddr_t size, unsigned map_flags,
+                                void **addr)
+{
+    int err;
+    struct pci_device_mapping map = {
+       .base = base,
+       .size = size,
+       .flags = map_flags,
+    };
+
+    err = pci_device_solx_devfs_map_range(dev, &map);
+    if (err == 0)
+       *addr = map.memory;
+    return err;
+}
+
+static int
+pci_device_solx_devfs_unmap_legacy(struct pci_device *dev,
+                                  void *addr, pciaddr_t size)
+{
+    struct pci_device_mapping map = {
+       .memory = addr,
+       .size = size,
+    };
+
+    return pci_device_generic_unmap_range(dev, &map);
+}
 
 static const struct pci_system_methods solx_devfs_methods = {
     .destroy = pci_system_solx_devfs_destroy,
+#ifdef __sparc
+    .destroy_device = pci_system_solx_devfs_destroy_device,
+#else
     .destroy_device = NULL,
+#endif
     .read_rom = pci_device_solx_devfs_read_rom,
     .probe = pci_device_solx_devfs_probe,
     .map_range = pci_device_solx_devfs_map_range,
@@ -1135,7 +1221,18 @@ static const struct pci_system_methods solx_devfs_methods = {
     .read = pci_device_solx_devfs_read,
     .write = pci_device_solx_devfs_write,
 
-    .fill_capabilities = pci_fill_capabilities_generic
+    .fill_capabilities = pci_fill_capabilities_generic,
+    .boot_vga = pci_device_solx_devfs_boot_vga,
+
+    .open_legacy_io = pci_device_solx_devfs_open_legacy_io,
+    .read32 = pci_device_solx_devfs_read32,
+    .read16 = pci_device_solx_devfs_read16,
+    .read8 = pci_device_solx_devfs_read8,
+    .write32 = pci_device_solx_devfs_write32,
+    .write16 = pci_device_solx_devfs_write16,
+    .write8 = pci_device_solx_devfs_write8,
+    .map_legacy = pci_device_solx_devfs_map_legacy,
+    .unmap_legacy = pci_device_solx_devfs_unmap_legacy,
 };
 
 /*
@@ -1176,6 +1273,9 @@ pci_system_solx_devfs_create( void )
     pinfo.num_allocated_elems = INITIAL_NUM_DEVICES;
     pinfo.num_devices = 0;
     pinfo.devices = devices;
+#ifdef __sparc
+    nexus_count = 0;
+#endif
     (void) di_walk_minor(di_node, DDI_NT_REGACC, 0, &pinfo, probe_nexus_node);
 
     di_fini(di_node);