/* * Copyright 1998 by Concurrent Computer Corporation * * Permission to use, copy, modify, distribute, and sell this software * and its documentation for any purpose is hereby granted without fee, * provided that the above copyright notice appear in all copies and that * both that copyright notice and this permission notice appear in * supporting documentation, and that the name of Concurrent Computer * Corporation not be used in advertising or publicity pertaining to * distribution of the software without specific, written prior * permission. Concurrent Computer Corporation makes no representations * about the suitability of this software for any purpose. It is * provided "as is" without express or implied warranty. * * CONCURRENT COMPUTER CORPORATION DISCLAIMS ALL WARRANTIES WITH REGARD * TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY * AND FITNESS, IN NO EVENT SHALL CONCURRENT COMPUTER CORPORATION BE * LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY * DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, * WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, * ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS * SOFTWARE. * * Copyright 1998 by Metro Link Incorporated * * Permission to use, copy, modify, distribute, and sell this software * and its documentation for any purpose is hereby granted without fee, * provided that the above copyright notice appear in all copies and that * both that copyright notice and this permission notice appear in * supporting documentation, and that the name of Metro Link * Incorporated not be used in advertising or publicity pertaining to * distribution of the software without specific, written prior * permission. Metro Link Incorporated makes no representations * about the suitability of this software for any purpose. It is * provided "as is" without express or implied warranty. * * METRO LINK INCORPORATED DISCLAIMS ALL WARRANTIES WITH REGARD * TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY * AND FITNESS, IN NO EVENT SHALL METRO LINK INCORPORATED BE * LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY * DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, * WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, * ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS * SOFTWARE. */ #ifdef HAVE_XORG_CONFIG_H #include #endif #include #include "compiler.h" #include "xf86.h" #include "xf86Priv.h" #include "xf86_OSlib.h" #include "Pci.h" #include /* * linux platform specific PCI access functions -- using /proc/bus/pci * needs kernel version 2.2.x */ static CARD32 linuxPciCfgRead(PCITAG tag, int off); static void linuxPciCfgWrite(PCITAG, int off, CARD32 val); static void linuxPciCfgSetBits(PCITAG tag, int off, CARD32 mask, CARD32 bits); static ADDRESS linuxTransAddrBusToHost(PCITAG tag, PciAddrType type, ADDRESS addr); #if defined(__powerpc__) static ADDRESS linuxPpcBusAddrToHostAddr(PCITAG, PciAddrType, ADDRESS); static ADDRESS linuxPpcHostAddrToBusAddr(PCITAG, PciAddrType, ADDRESS); #endif static CARD8 linuxPciCfgReadByte(PCITAG tag, int off); static void linuxPciCfgWriteByte(PCITAG tag, int off, CARD8 val); static CARD16 linuxPciCfgReadWord(PCITAG tag, int off); static void linuxPciCfgWriteWord(PCITAG tag, int off, CARD16 val); static int linuxPciHandleBIOS(PCITAG Tag, int basereg, unsigned char *buf, int len); static Bool linuxDomainSupport(void); static pciBusFuncs_t linuxFuncs0 = { /* pciReadLong */ linuxPciCfgRead, /* pciWriteLong */ linuxPciCfgWrite, /* pciSetBitsLong */ linuxPciCfgSetBits, #if defined(__powerpc__) /* pciAddrHostToBus */ linuxPpcHostAddrToBusAddr, /* pciAddrBusToHost */ linuxPpcBusAddrToHostAddr, #else /* pciAddrHostToBus */ pciAddrNOOP, /* linuxTransAddrBusToHost is busted on sparc64 but the PCI rework tree * makes it all moot, so we kludge it for now */ #if defined(__sparc__) /* pciAddrBusToHost */ pciAddrNOOP, #else /* pciAddrBusToHost */ linuxTransAddrBusToHost, #endif /* __sparc64__ */ #endif /* pciControlBridge */ NULL, /* pciGetBridgeBuses */ NULL, /* pciGetBridgeResources */ NULL, /* pciReadByte */ linuxPciCfgReadByte, /* pciWriteByte */ linuxPciCfgWriteByte, /* pciReadWord */ linuxPciCfgReadWord, /* pciWriteWord */ linuxPciCfgWriteWord, }; static pciBusInfo_t linuxPci0 = { /* configMech */ PCI_CFG_MECH_OTHER, /* numDevices */ 32, /* secondary */ FALSE, /* primary_bus */ 0, /* funcs */ &linuxFuncs0, /* pciBusPriv */ NULL, /* bridge */ NULL }; /* from lnx_pci.c. */ extern int lnxPciInit(void); static Bool domain_support = FALSE; void linuxPciInit() { struct stat st; if ((xf86Info.pciFlags == PCIForceNone) || (-1 == stat("/proc/bus/pci", &st))) { /* when using this as default for all linux architectures, we'll need a fallback for 2.0 kernels here */ return; } #ifndef INCLUDE_XF86_NO_DOMAIN domain_support = linuxDomainSupport(); #endif pciNumBuses = 1; pciBusInfo[0] = &linuxPci0; pciFindFirstFP = pciGenFindFirst; pciFindNextFP = pciGenFindNext; pciSetOSBIOSPtr(linuxPciHandleBIOS); xf86MaxPciDevs = lnxPciInit(); } static int linuxPciOpenFile(PCITAG tag, Bool write) { static int ldomain, lbus,ldev,lfunc,fd = -1,is_write = 0; int domain, bus, dev, func; char file[64]; struct stat ignored; static int is26 = -1; domain = PCI_DOM_FROM_TAG(tag); bus = PCI_BUS_NO_DOMAIN(PCI_BUS_FROM_TAG(tag)); dev = PCI_DEV_FROM_TAG(tag); func = PCI_FUNC_FROM_TAG(tag); if (is26 == -1) { if (stat("/sys/bus/pci",&ignored) < 0) is26 = 0; else is26 = 1; } if (!domain_support && domain > 0) return -1; if (fd == -1 || (write && (!is_write)) || domain != ldomain || bus != lbus || dev != ldev || func != lfunc) { if (fd != -1) { close(fd); fd = -1; } if (is26) sprintf(file,"/sys/bus/pci/devices/%04x:%02x:%02x.%01x/config", domain, bus, dev, func); else { if (bus < 256) { sprintf(file, "/proc/bus/pci/%04x:%02x", domain, bus); if (stat(file, &ignored) < 0) { if (domain == 0) sprintf(file, "/proc/bus/pci/%02x/%02x.%1x", bus, dev, func); else goto bail; } else sprintf(file, "/proc/bus/pci/%04x:%02x/%02x.%1x", domain, bus, dev, func); } else { sprintf(file, "/proc/bus/pci/%04x:%04x", domain, bus); if (stat(file, &ignored) < 0) { if (domain == 0) sprintf(file, "/proc/bus/pci/%04x/%02x.%1x", bus, dev, func); else goto bail; } else sprintf(file, "/proc/bus/pci/%04x:%04x/%02x.%1x", domain, bus, dev, func); } } if (write) { fd = open(file,O_RDWR); if (fd != -1) is_write = TRUE; } else switch (is_write) { case TRUE: fd = open(file,O_RDWR); if (fd > -1) break; default: fd = open(file,O_RDONLY); is_write = FALSE; } bail: ldomain = domain; lbus = bus; ldev = dev; lfunc = func; } return fd; } static CARD32 linuxPciCfgRead(PCITAG tag, int off) { int fd; CARD32 val = 0xffffffff; if (-1 != (fd = linuxPciOpenFile(tag,FALSE))) { lseek(fd,off,SEEK_SET); read(fd,&val,4); } return PCI_CPU(val); } static void linuxPciCfgWrite(PCITAG tag, int off, CARD32 val) { int fd; if (-1 != (fd = linuxPciOpenFile(tag,TRUE))) { lseek(fd,off,SEEK_SET); val = PCI_CPU(val); write(fd,&val,4); } } static void linuxPciCfgSetBits(PCITAG tag, int off, CARD32 mask, CARD32 bits) { int fd; CARD32 val = 0xffffffff; if (-1 != (fd = linuxPciOpenFile(tag,TRUE))) { lseek(fd,off,SEEK_SET); read(fd,&val,4); val = PCI_CPU(val); val = (val & ~mask) | (bits & mask); val = PCI_CPU(val); lseek(fd,off,SEEK_SET); write(fd,&val,4); } } /* * This function will convert a BAR address into a host address * suitable for passing into the mmap function of a /proc/bus * device. */ ADDRESS linuxTransAddrBusToHost(PCITAG tag, PciAddrType type, ADDRESS addr) { ADDRESS ret = xf86GetOSOffsetFromPCI(tag, PCI_MEM|PCI_IO, addr); if (ret) return ret; /* * if it is not a BAR address, it must be legacy, (or wrong) * return it as is.. */ return addr; } #if defined(__powerpc__) #ifndef __NR_pciconfig_iobase #define __NR_pciconfig_iobase 200 #endif static ADDRESS linuxPpcBusAddrToHostAddr(PCITAG tag, PciAddrType type, ADDRESS addr) { if (type == PCI_MEM) { ADDRESS membase = syscall(__NR_pciconfig_iobase, 1, PCI_BUS_FROM_TAG(tag), PCI_DFN_FROM_TAG(tag)); return (addr + membase); } else if (type == PCI_IO) { ADDRESS iobase = syscall(__NR_pciconfig_iobase, 2, PCI_BUS_FROM_TAG(tag), PCI_DFN_FROM_TAG(tag)); return (addr + iobase); } else return addr; } static ADDRESS linuxPpcHostAddrToBusAddr(PCITAG tag, PciAddrType type, ADDRESS addr) { if (type == PCI_MEM) { ADDRESS membase = syscall(__NR_pciconfig_iobase, 1, PCI_BUS_FROM_TAG(tag), PCI_DFN_FROM_TAG(tag)); return (addr - membase); } else if (type == PCI_IO) { ADDRESS iobase = syscall(__NR_pciconfig_iobase, 2, PCI_BUS_FROM_TAG(tag), PCI_DFN_FROM_TAG(tag)); return (addr - iobase); } else return addr; } #endif /* __powerpc__ */ static CARD8 linuxPciCfgReadByte(PCITAG tag, int off) { int fd; CARD8 val = 0xff; if (-1 != (fd = linuxPciOpenFile(tag,FALSE))) { lseek(fd,off,SEEK_SET); read(fd,&val,1); } return val; } static void linuxPciCfgWriteByte(PCITAG tag, int off, CARD8 val) { int fd; if (-1 != (fd = linuxPciOpenFile(tag,TRUE))) { lseek(fd,off,SEEK_SET); write(fd, &val, 1); } } static CARD16 linuxPciCfgReadWord(PCITAG tag, int off) { int fd; CARD16 val = 0xff; if (-1 != (fd = linuxPciOpenFile(tag,FALSE))) { lseek(fd, off, SEEK_SET); read(fd, &val, 2); } return PCI_CPU16(val); } static void linuxPciCfgWriteWord(PCITAG tag, int off, CARD16 val) { int fd; if (-1 != (fd = linuxPciOpenFile(tag,TRUE))) { lseek(fd, off, SEEK_SET); val = PCI_CPU16(val); write(fd, &val, 2); } } #ifndef INCLUDE_XF86_NO_DOMAIN /* * Compiling the following simply requires the presence of . * Actually running this is another matter altogether... * * This scheme requires that the kernel allow mmap()'ing of a host bridge's I/O * and memory spaces through its /proc/bus/pci/BUS/DFN entry. Which one is * determined by a prior ioctl(). * * For the sparc64 port, this means 2.4.12 or later. For ppc, this * functionality is almost, but not quite there yet. Alpha and other kernel * ports to multi-domain architectures still need to implement this. * * This scheme is also predicated on the use of an IOADDRESS compatible type to * designate I/O addresses. Although IOADDRESS is defined as an unsigned * integral type, it is actually the virtual address of, i.e. a pointer to, the * I/O port to access. And so, the inX/outX macros in "compiler.h" need to be * #define'd appropriately (as is done on SPARC's). * * Another requirement to port this scheme to another multi-domain architecture * is to add the appropriate entries in the pciControllerSizes array below. * * TO DO: Address the deleterious reaction some host bridges have to master * aborts. This is already done for secondary PCI buses, but not yet * for accesses to primary buses (except for the SPARC port, where * master aborts are avoided during PCI scans). */ #include #ifndef PCIIOC_BASE /* Ioctls for /proc/bus/pci/X/Y nodes. */ #define PCIIOC_BASE ('P' << 24 | 'C' << 16 | 'I' << 8) /* Get controller for PCI device. */ #define PCIIOC_CONTROLLER (PCIIOC_BASE | 0x00) /* Set mmap state to I/O space. */ #define PCIIOC_MMAP_IS_IO (PCIIOC_BASE | 0x01) /* Set mmap state to MEM space. */ #define PCIIOC_MMAP_IS_MEM (PCIIOC_BASE | 0x02) /* Enable/disable write-combining. */ #define PCIIOC_WRITE_COMBINE (PCIIOC_BASE | 0x03) #endif /* This probably shouldn't be Linux-specific */ static pciConfigPtr xf86GetPciHostConfigFromTag(PCITAG Tag) { int bus = PCI_BUS_FROM_TAG(Tag); pciBusInfo_t *pBusInfo; while ((bus < pciNumBuses) && (pBusInfo = pciBusInfo[bus])) { if (bus == pBusInfo->primary_bus) return pBusInfo->bridge; bus = pBusInfo->primary_bus; } return NULL; /* Bad data */ } /* * This is ugly, but until I can extract this information from the kernel, * it'll have to do. The default I/O space size is 64K, and 4G for memory. * Anything else needs to go in this table. (PowerPC folk take note.) * * Note that Linux/SPARC userland is 32-bit, so 4G overflows to zero here. * * Please keep this table in ascending vendor/device order. */ static const struct pciSizes { unsigned short vendor, device; unsigned long io_size, mem_size; } pciControllerSizes[] = { { PCI_VENDOR_SUN, PCI_CHIP_PSYCHO, 1U << 16, 1U << 31 }, { PCI_VENDOR_SUN, PCI_CHIP_SCHIZO, 1U << 24, 1U << 31 /* ??? */ }, { PCI_VENDOR_SUN, PCI_CHIP_SABRE, 1U << 24, (unsigned long)(1ULL << 32) }, { PCI_VENDOR_SUN, PCI_CHIP_HUMMINGBIRD, 1U << 24, (unsigned long)(1ULL << 32) } }; #define NUM_SIZES (sizeof(pciControllerSizes) / sizeof(pciControllerSizes[0])) static const struct pciSizes * linuxGetSizesStruct(PCITAG Tag) { static const struct pciSizes default_size = { 0, 0, 1U << 16, (unsigned long)(1ULL << 32) }; pciConfigPtr pPCI; int i; /* Find host bridge */ if ((pPCI = xf86GetPciHostConfigFromTag(Tag))) { /* Look up vendor/device */ for (i = 0; i < NUM_SIZES; i++) { if ((pPCI->pci_vendor == pciControllerSizes[i].vendor) && (pPCI->pci_device == pciControllerSizes[i].device)) { return & pciControllerSizes[i]; } } } /* Default to 64KB I/O and 4GB memory. */ return & default_size; } static __inline__ unsigned long linuxGetIOSize(PCITAG Tag) { const struct pciSizes * const sizes = linuxGetSizesStruct(Tag); return sizes->io_size; } static __inline__ void linuxGetSizes(PCITAG Tag, unsigned long *io_size, unsigned long *mem_size) { const struct pciSizes * const sizes = linuxGetSizesStruct(Tag); *io_size = sizes->io_size; *mem_size = sizes->mem_size; } static Bool linuxDomainSupport(void) { DIR *dir; struct dirent *dirent; char *end; if (!(dir = opendir("/proc/bus/pci"))) return FALSE; while (1) { if (!(dirent = readdir(dir))) return FALSE; strtol(dirent->d_name,&end,16); /* entry of the form xx or xxxx : x=[0..f] no domain */ if (*end == '\0') return FALSE; else if (*end == ':') { /* ':' found immediately after: verify for xxxx:xx or xxxx:xxxx */ strtol(end + 1,&end,16); if (*end == '\0') return TRUE; } } return FALSE; } _X_EXPORT int xf86GetPciDomain(PCITAG Tag) { pciConfigPtr pPCI; int fd, result; pPCI = xf86GetPciHostConfigFromTag(Tag); if (pPCI && (result = PCI_DOM_FROM_BUS(pPCI->busnum))) return result; if (!pPCI || pPCI->fakeDevice) return 1; /* Domain 0 is reserved */ if ((fd = linuxPciOpenFile(pPCI ? pPCI->tag : 0,FALSE)) < 0) return 0; if ((result = ioctl(fd, PCIIOC_CONTROLLER, 0)) < 0) return 0; return result + 1; /* Domain 0 is reserved */ } static pointer linuxMapPci(int ScreenNum, int Flags, PCITAG Tag, ADDRESS Base, unsigned long Size, int mmap_ioctl) { do { pciConfigPtr pPCI; unsigned char *result; ADDRESS realBase, Offset; int fd, mmapflags, prot; xf86InitVidMem(); if (((fd = linuxPciOpenFile(Tag ,FALSE)) < 0) || (ioctl(fd, mmap_ioctl, 0) < 0)) break; /* Note: IA-64 doesn't compile this and doesn't need to */ #ifdef __ia64__ # ifndef MAP_WRITECOMBINED # define MAP_WRITECOMBINED 0x00010000 # endif # ifndef MAP_NONCACHED # define MAP_NONCACHED 0x00020000 # endif if (Flags & VIDMEM_FRAMEBUFFER) mmapflags = MAP_SHARED | MAP_WRITECOMBINED; else mmapflags = MAP_SHARED | MAP_NONCACHED; #else /* !__ia64__ */ mmapflags = (Flags & VIDMEM_FRAMEBUFFER) / VIDMEM_FRAMEBUFFER; if (ioctl(fd, PCIIOC_WRITE_COMBINE, mmapflags) < 0) break; mmapflags = MAP_SHARED; #endif /* ?__ia64__ */ /* Align to page boundary */ realBase = Base & ~(getpagesize() - 1); Offset = Base - realBase; if (Flags & VIDMEM_READONLY) prot = PROT_READ; else prot = PROT_READ | PROT_WRITE; result = mmap(NULL, Size + Offset, prot, mmapflags, fd, realBase); if (!result || ((pointer)result == MAP_FAILED)) return NULL; xf86MakeNewMapping(ScreenNum, Flags, realBase, Size + Offset, result); return result + Offset; } while (0); if (mmap_ioctl == PCIIOC_MMAP_IS_MEM) return xf86MapVidMem(ScreenNum, Flags, Base, Size); return NULL; } #define MAX_DOMAINS 257 static pointer DomainMmappedIO[MAX_DOMAINS]; static int linuxOpenLegacy(PCITAG Tag, char *name) { #define PREFIX "/sys/class/pci_bus/%04x:%02x/%s" char *path; int domain, bus; pciBusInfo_t *pBusInfo; pciConfigPtr bridge = NULL; int fd; path = xalloc(strlen(PREFIX) + strlen(name)); if (!path) return -1; for (;;) { domain = xf86GetPciDomain(Tag); bus = PCI_BUS_NO_DOMAIN(PCI_BUS_FROM_TAG(Tag)); /* Domain 0 is reserved -- see xf86GetPciDomain() */ if ((domain <= 0) || (domain >= MAX_DOMAINS)) FatalError("linuxOpenLegacy(): domain out of range\n"); sprintf(path, PREFIX, domain - 1, bus, name); fd = open(path, O_RDWR); if (fd >= 0) { xfree(path); return fd; } pBusInfo = pciBusInfo[bus]; if (!pBusInfo || (bridge == pBusInfo->bridge) || !(bridge = pBusInfo->bridge)) { xfree(path); return -1; } Tag = bridge->tag; } xfree(path); return fd; } /* * xf86MapDomainMemory - memory map PCI domain memory * * This routine maps the memory region in the domain specified by Tag and * returns a pointer to it. The pointer is saved for future use if it's in * the legacy ISA memory space (memory in a domain between 0 and 1MB). */ _X_EXPORT pointer xf86MapDomainMemory(int ScreenNum, int Flags, PCITAG Tag, ADDRESS Base, unsigned long Size) { int domain = xf86GetPciDomain(Tag); int fd; pointer addr; /* * We use /proc/bus/pci on non-legacy addresses or if the Linux sysfs * legacy_mem interface is unavailable. */ if (Base > 1024*1024) return linuxMapPci(ScreenNum, Flags, Tag, Base, Size, PCIIOC_MMAP_IS_MEM); if ((fd = linuxOpenLegacy(Tag, "legacy_mem")) < 0) return linuxMapPci(ScreenNum, Flags, Tag, Base, Size, PCIIOC_MMAP_IS_MEM); addr = mmap(NULL, Size, PROT_READ|PROT_WRITE, MAP_SHARED, fd, Base); if (addr == MAP_FAILED) { close (fd); perror("mmap failure"); FatalError("xf86MapDomainMem(): mmap() failure\n"); } close(fd); return addr; } /* * xf86MapDomainIO - map I/O space in this domain * * Each domain has a legacy ISA I/O space. This routine will try to * map it using the Linux sysfs legacy_io interface. If that fails, * it'll fall back to using /proc/bus/pci. * * If the legacy_io interface *does* exist, the file descriptor (fd below) * will be saved in the DomainMmappedIO array in the upper bits of the * pointer. Callers will do I/O with small port numbers (<64k values), so * the platform I/O code can extract the port number and the fd, lseek to * the port number in the legacy_io file, and issue the read or write. * * This has no means of returning failure, so all errors are fatal */ _X_EXPORT IOADDRESS xf86MapDomainIO(int ScreenNum, int Flags, PCITAG Tag, IOADDRESS Base, unsigned long Size) { int domain = xf86GetPciDomain(Tag); int fd; if ((domain <= 0) || (domain >= MAX_DOMAINS)) FatalError("xf86MapDomainIO(): domain out of range\n"); if (DomainMmappedIO[domain]) return (IOADDRESS)DomainMmappedIO[domain] + Base; /* Permanently map all of I/O space */ if ((fd = linuxOpenLegacy(Tag, "legacy_io")) < 0) { DomainMmappedIO[domain] = linuxMapPci(ScreenNum, Flags, Tag, 0, linuxGetIOSize(Tag), PCIIOC_MMAP_IS_IO); /* ia64 can't mmap legacy IO port space */ if (!DomainMmappedIO[domain]) return Base; } else { /* legacy_io file exists, encode fd */ DomainMmappedIO[domain] = (pointer)(fd << 24); } return (IOADDRESS)DomainMmappedIO[domain] + Base; } /* * xf86ReadDomainMemory - copy from domain memory into a caller supplied buffer */ _X_EXPORT int xf86ReadDomainMemory(PCITAG Tag, ADDRESS Base, int Len, unsigned char *Buf) { unsigned char *ptr, *src; ADDRESS offset; unsigned long size; int len, pagemask = getpagesize() - 1; unsigned int i, dom, bus, dev, func; unsigned int fd; char file[256]; struct stat st; dom = PCI_DOM_FROM_TAG(Tag); bus = PCI_BUS_NO_DOMAIN(PCI_BUS_FROM_TAG(Tag)); dev = PCI_DEV_FROM_TAG(Tag); func = PCI_FUNC_FROM_TAG(Tag); sprintf(file, "/sys/devices/pci%04x:%02x/%04x:%02x:%02x.%1x/rom", dom, bus, dom, bus, dev, func); /* * If the caller wants the ROM and the sysfs rom interface exists, * try to use it instead of reading it from /proc/bus/pci. */ if (((Base & 0xfffff) == 0xC0000) && (stat(file, &st) == 0)) { if ((fd = open(file, O_RDWR))) Base = 0x0; /* enable the ROM first */ write(fd, "1", 2); lseek(fd, 0, SEEK_SET); /* copy the ROM until we hit Len, EOF or read error */ for (i = 0; i < Len && read(fd, Buf, 1) > 0; Buf++, i++) ; write(fd, "0", 2); close(fd); return Len; } /* Ensure page boundaries */ offset = Base & ~pagemask; size = ((Base + Len + pagemask) & ~pagemask) - offset; ptr = xf86MapDomainMemory(-1, VIDMEM_READONLY, Tag, offset, size); if (!ptr) return -1; /* Using memcpy() here can hang the system */ src = ptr + (Base - offset); for (len = Len; len-- > 0;) *Buf++ = *src++; xf86UnMapVidMem(-1, ptr, size); return Len; } resPtr xf86BusAccWindowsFromOS(void) { pciConfigPtr *ppPCI, pPCI; resPtr pRes = NULL; resRange range; unsigned long io_size, mem_size; int domain; if ((ppPCI = xf86scanpci(0))) { for (; (pPCI = *ppPCI); ppPCI++) { if ((pPCI->pci_base_class != PCI_CLASS_BRIDGE) || (pPCI->pci_sub_class != PCI_SUBCLASS_BRIDGE_HOST)) continue; domain = xf86GetPciDomain(pPCI->tag); linuxGetSizes(pPCI->tag, &io_size, &mem_size); RANGE(range, 0, (ADDRESS)(mem_size - 1), RANGE_TYPE(ResExcMemBlock, domain)); pRes = xf86AddResToList(pRes, &range, -1); RANGE(range, 0, (IOADDRESS)(io_size - 1), RANGE_TYPE(ResExcIoBlock, domain)); pRes = xf86AddResToList(pRes, &range, -1); if (domain <= 0) break; } } return pRes; } resPtr xf86PciBusAccWindowsFromOS(void) { pciConfigPtr *ppPCI, pPCI; resPtr pRes = NULL; resRange range; unsigned long io_size, mem_size; int domain; if ((ppPCI = xf86scanpci(0))) { for (; (pPCI = *ppPCI); ppPCI++) { if ((pPCI->pci_base_class != PCI_CLASS_BRIDGE) || (pPCI->pci_sub_class != PCI_SUBCLASS_BRIDGE_HOST)) continue; domain = xf86GetPciDomain(pPCI->tag); linuxGetSizes(pPCI->tag, &io_size, &mem_size); RANGE(range, 0, (ADDRESS)(mem_size - 1), RANGE_TYPE(ResExcMemBlock, domain)); pRes = xf86AddResToList(pRes, &range, -1); RANGE(range, 0, (IOADDRESS)(io_size - 1), RANGE_TYPE(ResExcIoBlock, domain)); pRes = xf86AddResToList(pRes, &range, -1); if (domain <= 0) break; } } return pRes; } resPtr xf86AccResFromOS(resPtr pRes) { pciConfigPtr *ppPCI, pPCI; resRange range; unsigned long io_size, mem_size; int domain; if ((ppPCI = xf86scanpci(0))) { for (; (pPCI = *ppPCI); ppPCI++) { if ((pPCI->pci_base_class != PCI_CLASS_BRIDGE) || (pPCI->pci_sub_class != PCI_SUBCLASS_BRIDGE_HOST)) continue; domain = xf86GetPciDomain(pPCI->tag); linuxGetSizes(pPCI->tag, &io_size, &mem_size); /* * At minimum, the top and bottom resources must be claimed, so * that resources that are (or appear to be) unallocated can be * relocated. */ RANGE(range, 0x00000000u, 0x0009ffffu, RANGE_TYPE(ResExcMemBlock, domain)); pRes = xf86AddResToList(pRes, &range, -1); RANGE(range, 0x000c0000u, 0x000effffu, RANGE_TYPE(ResExcMemBlock, domain)); pRes = xf86AddResToList(pRes, &range, -1); RANGE(range, 0x000f0000u, 0x000fffffu, RANGE_TYPE(ResExcMemBlock, domain)); pRes = xf86AddResToList(pRes, &range, -1); RANGE(range, (ADDRESS)(mem_size - 1), (ADDRESS)(mem_size - 1), RANGE_TYPE(ResExcMemBlock, domain)); pRes = xf86AddResToList(pRes, &range, -1); RANGE(range, 0x00000000u, 0x00000000u, RANGE_TYPE(ResExcIoBlock, domain)); pRes = xf86AddResToList(pRes, &range, -1); RANGE(range, (IOADDRESS)(io_size - 1), (IOADDRESS)(io_size - 1), RANGE_TYPE(ResExcIoBlock, domain)); pRes = xf86AddResToList(pRes, &range, -1); if (domain <= 0) break; } } return pRes; } #endif /* !INCLUDE_XF86_NO_DOMAIN */ int linuxPciHandleBIOS(PCITAG Tag, int basereg, unsigned char *buf, int len) { unsigned int dom, bus, dev, func; unsigned int fd; char file[256]; struct stat st; int ret; int sofar = 0; dom = PCI_DOM_FROM_TAG(Tag); bus = PCI_BUS_NO_DOMAIN(PCI_BUS_FROM_TAG(Tag)); dev = PCI_DEV_FROM_TAG(Tag); func = PCI_FUNC_FROM_TAG(Tag); sprintf(file, "/sys/bus/pci/devices/%04x:%02x:%02x.%1x/rom", dom, bus, dev, func); if (stat(file, &st) == 0) { if ((fd = open(file, O_RDWR))) basereg = 0x0; /* enable the ROM first */ write(fd, "1", 2); lseek(fd, 0, SEEK_SET); do { /* copy the ROM until we hit Len, EOF or read error */ ret = read(fd, buf+sofar, len-sofar); if (ret <= 0) break; sofar += ret; } while (sofar < len); write(fd, "0", 2); close(fd); if (sofar < len) xf86MsgVerb(X_INFO, 3, "Attempted to read BIOS %dKB from %s: got %dKB\n", len/1024, file, sofar/1024); return sofar; } return 0; } #ifdef __ia64__ static PCITAG ia64linuxPciFindFirst(void); static PCITAG ia64linuxPciFindNext(void); void ia64linuxPciInit() { struct stat st; linuxPciInit(); if (!stat("/proc/sgi_sn/licenseID", &st) && pciNumBuses) { /* Be a little paranoid here and only use this code for Altix systems. * It is generic, so it should work on any system, but depends on * /proc/bus/pci entries for each domain/bus combination. Altix is * guaranteed a recent enough kernel to have them. */ pciFindFirstFP = ia64linuxPciFindFirst; pciFindNextFP = ia64linuxPciFindNext; } } static DIR *busdomdir; static DIR *devdir; static PCITAG ia64linuxPciFindFirst(void) { busdomdir = opendir("/proc/bus/pci"); devdir = NULL; return ia64linuxPciFindNext(); } static struct dirent *getnextbus(int *domain, int *bus) { struct dirent *entry; int dombus; for (;;) { entry = readdir(busdomdir); if (entry == NULL) { *domain = 0; *bus = 0; closedir(busdomdir); return NULL; } if (sscanf(entry->d_name, "%04x:%02x", domain, bus) != 2) continue; dombus = PCI_MAKE_BUS(*domain, *bus); if (pciNumBuses <= dombus) pciNumBuses = dombus + 1; if (!pciBusInfo[dombus]) { pciBusInfo[dombus] = xnfalloc(sizeof(pciBusInfo_t)); *pciBusInfo[dombus] = *pciBusInfo[0]; } return entry; } } static PCITAG ia64linuxPciFindNext(void) { struct dirent *entry; char file[40]; static int bus, dev, func, domain; PCITAG pciDeviceTag; CARD32 devid; for (;;) { if (devdir == NULL) { entry = getnextbus(&domain, &bus); if (!entry) return PCI_NOT_FOUND; snprintf(file, 40, "/proc/bus/pci/%s", entry->d_name); devdir = opendir(file); if (!devdir) return PCI_NOT_FOUND; } entry = readdir(devdir); if (entry == NULL) { closedir(devdir); devdir = NULL; continue; } if (sscanf(entry->d_name, "%02x . %01x", &dev, &func) == 2) { pciDeviceTag = PCI_MAKE_TAG(PCI_MAKE_BUS(domain, bus), dev, func); devid = pciReadLong(pciDeviceTag, PCI_ID_REG); if ((devid & pciDevidMask) == pciDevid) /* Yes - Return it. Otherwise, next device */ return pciDeviceTag; } } } #endif