/*
* (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"
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 {
#ifdef __sparc
static di_prom_handle_t di_phdl;
+static size_t nexus_count = 0;
#endif
/*
# 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 )
{
}
return NULL;
}
-#endif
#define GET_CONFIG_VAL_8(offset) (config_hdr.bytes[offset])
#define GET_CONFIG_VAL_16(offset) \
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;
#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.
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
#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
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);
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);
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;
/*
}
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);
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
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 *)®buf[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 *)®buf[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;
+ }
}
}
}
/**
- * 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.
? (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;
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;
}
}
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;
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;
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,
.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,
};
/*
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);