diff --git a/ChangeLog b/ChangeLog index b865ec109..3972c19c6 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,15 @@ +2006-05-18 Adam Jackson + + * hw/xfree86/common/xf86Priv.h: + * hw/xfree86/common/xf86pciBus.c: + * hw/xfree86/dummylib/Makefile.am: + * hw/xfree86/dummylib/getemptypci.c: + * hw/xfree86/os-support/bus/Pci.c: + * hw/xfree86/os-support/bus/xf86Pci.h: + * hw/xfree86/os-support/linux/lnx_pci.c: + Bug #4139: Fix a BAR remapping bug that could lead to IERR and + system hang. (Egbert Eich) + 2006-05-18 Adam Jackson * hw/xfree86/os-support/linux/lnx_pci.c: diff --git a/hw/xfree86/common/xf86Priv.h b/hw/xfree86/common/xf86Priv.h index 4d578b8ff..0794de777 100644 --- a/hw/xfree86/common/xf86Priv.h +++ b/hw/xfree86/common/xf86Priv.h @@ -155,6 +155,7 @@ void xf86AddDevToEntity(int entityIndex, GDevPtr dev); extern void xf86PostPreInit(void); extern void xf86PostScreenInit(void); extern memType getValidBIOSBase(PCITAG tag, int num); +extern memType getEmptyPciRange(PCITAG tag, int base_reg); extern int pciTestMultiDeviceCard(int bus, int dev, int func, PCITAG** pTag); /* xf86Config.c */ diff --git a/hw/xfree86/common/xf86pciBus.c b/hw/xfree86/common/xf86pciBus.c index af19b37eb..4d587a13f 100644 --- a/hw/xfree86/common/xf86pciBus.c +++ b/hw/xfree86/common/xf86pciBus.c @@ -1498,29 +1498,117 @@ xf86ReallocatePciResources(int entityIndex, resPtr pRes) /* * BIOS releated */ -memType -getValidBIOSBase(PCITAG tag, int num) +static resPtr +getOwnResources(pciVideoPtr pvp, resPtr mem) { - pciVideoPtr pvp = NULL; - PciBusPtr pbp; - resPtr m = NULL; - resPtr tmp, avoid, mem = NULL; resRange range; - memType ret; - int n = 0; int i; - CARD32 biosSize, alignment; + /* Make sure we don't conflict with our own mem resources */ + for (i = 0; i < 6; i++) { + if (!pvp->memBase[i]) + continue; + P_M_RANGE(range,TAG(pvp),pvp->memBase[i],pvp->size[i], + ResExcMemBlock); + mem = xf86AddResToList(mem,&range,-1); + } + return mem; +} +static void +getPciRangesForMapping(pciVideoPtr pvp, resPtr *map, resPtr *avoid) +{ + PciBusPtr pbp; + resPtr tmp; + + *avoid = xf86DupResList(pciAvoidRes); + + pbp = xf86PciBus; + while (pbp) { + if (pbp->secondary == pvp->bus) { + if (pbp->preferred_pmem) + tmp = xf86DupResList(pbp->preferred_pmem); + else + tmp = xf86DupResList(pbp->pmem); + *map = xf86JoinResLists(*map,tmp); + if (pbp->preferred_mem) + tmp = xf86DupResList(pbp->preferred_mem); + else + tmp = xf86DupResList(pbp->mem); + *map = xf86JoinResLists(*map,tmp); + tmp = *map; + while (tmp) { + tmp->block_end = min(tmp->block_end,PCI_MEM32_LENGTH_MAX); + tmp = tmp->next; + } + } else if ((pbp->primary == pvp->bus) && + (pbp->secondary >= 0) && + (pbp->primary != pbp->secondary)) { + tmp = xf86DupResList(pbp->preferred_pmem); + *avoid = xf86JoinResLists(*avoid, tmp); + tmp = xf86DupResList(pbp->pmem); + *avoid = xf86JoinResLists(*avoid, tmp); + tmp = xf86DupResList(pbp->preferred_mem); + *avoid = xf86JoinResLists(*avoid, tmp); + tmp = xf86DupResList(pbp->mem); + *avoid = xf86JoinResLists(*avoid, tmp); + } + pbp = pbp->next; + } + pciConvertListToHost(pvp->bus,pvp->device,pvp->func, *avoid); + pciConvertListToHost(pvp->bus,pvp->device,pvp->func, *map); +} + +static memType +findPciRange(PCITAG tag, resPtr m, resPtr avoid, CARD32 size) +{ + resRange range; + CARD32 alignment = (1 << size) - 1; + + while (m) { + range = xf86GetBlock(RANGE_TYPE(ResExcMemBlock, xf86GetPciDomain(tag)), + PCI_SIZE(ResMem, tag, 1 << size), + m->block_begin, m->block_end, + PCI_SIZE(ResMem, tag, alignment), + avoid); + if (range.type != ResEnd) { + return M2B(tag, range.rBase); + } + m = m->next; + } + return 0; +} + +pciVideoPtr +getPciVideoPtr(tag) +{ + int n = 0; + + pciVideoPtr pvp = NULL; if (!xf86PciVideoInfo) return 0; while ((pvp = xf86PciVideoInfo[n++])) { if (pciTag(pvp->bus,pvp->device,pvp->func) == tag) - break; + return pvp; } + return NULL; +} + +memType +getValidBIOSBase(PCITAG tag, int num) +{ + pciVideoPtr pvp = NULL; + memType ret; + CARD32 biosSize; + resPtr mem = NULL; + resPtr avoid = NULL, m = NULL; + resRange range; + + pvp = getPciVideoPtr(tag); + if (!pvp) return 0; biosSize = pvp->biosSize; - alignment = (1 << biosSize) - 1; + if (biosSize > 24) biosSize = 24; @@ -1531,15 +1619,8 @@ getValidBIOSBase(PCITAG tag, int num) /* In some cases the BIOS base register contains the size mask */ if ((memType)(-1 << biosSize) == PCIGETROM(pvp->biosBase)) return 0; - /* Make sure we don't conflict with our own mem resources */ - for (i = 0; i < 6; i++) { - if (!pvp->memBase[i]) - continue; - P_M_RANGE(range,TAG(pvp),pvp->memBase[i],pvp->size[i], - ResExcMemBlock); - mem = xf86AddResToList(mem,&range,-1); - } - P_M_RANGE(range, TAG(pvp),pvp->biosBase,biosSize,ResExcMemBlock); + mem = getOwnResources(pvp,mem); + P_M_RANGE(range, tag, pvp->biosBase,biosSize,ResExcMemBlock); ret = pvp->biosBase; break; case ROM_BASE_MEM0: @@ -1550,7 +1631,7 @@ getValidBIOSBase(PCITAG tag, int num) case ROM_BASE_MEM5: if (!pvp->memBase[num] || (pvp->size[num] < biosSize)) return 0; - P_M_RANGE(range, TAG(pvp),pvp->memBase[num],biosSize, + P_M_RANGE(range, tag ,pvp->memBase[num],biosSize, ResExcMemBlock); ret = pvp->memBase[num]; break; @@ -1562,59 +1643,15 @@ getValidBIOSBase(PCITAG tag, int num) } /* Now find the ranges for validation */ - avoid = xf86DupResList(pciAvoidRes); - pbp = xf86PciBus; - while (pbp) { - if (pbp->secondary == pvp->bus) { - if (pbp->preferred_pmem) - tmp = xf86DupResList(pbp->preferred_pmem); - else - tmp = xf86DupResList(pbp->pmem); - m = xf86JoinResLists(m,tmp); - if (pbp->preferred_mem) - tmp = xf86DupResList(pbp->preferred_mem); - else - tmp = xf86DupResList(pbp->mem); - m = xf86JoinResLists(m,tmp); - tmp = m; - while (tmp) { - tmp->block_end = min(tmp->block_end,PCI_MEM32_LENGTH_MAX); - tmp = tmp->next; - } - } else if ((pbp->primary == pvp->bus) && - (pbp->secondary >= 0) && - (pbp->primary != pbp->secondary)) { - tmp = xf86DupResList(pbp->preferred_pmem); - avoid = xf86JoinResLists(avoid, tmp); - tmp = xf86DupResList(pbp->pmem); - avoid = xf86JoinResLists(avoid, tmp); - tmp = xf86DupResList(pbp->preferred_mem); - avoid = xf86JoinResLists(avoid, tmp); - tmp = xf86DupResList(pbp->mem); - avoid = xf86JoinResLists(avoid, tmp); - } - pbp = pbp->next; - } - pciConvertListToHost(pvp->bus,pvp->device,pvp->func, avoid); - if (mem) - pciConvertListToHost(pvp->bus,pvp->device,pvp->func, mem); - + getPciRangesForMapping(pvp,&m,&avoid); + if (!ret) { /* Return a possible window */ - while (m) { - range = xf86GetBlock(RANGE_TYPE(ResExcMemBlock, xf86GetPciDomain(tag)), - PCI_SIZE(ResMem, TAG(pvp), 1 << biosSize), - m->block_begin, m->block_end, - PCI_SIZE(ResMem, TAG(pvp), alignment), - avoid); - if (range.type != ResEnd) { - ret = M2B(TAG(pvp), range.rBase); - break; - } - m = m->next; - } + ret = findPciRange(tag,m,avoid,biosSize); } else { #if !defined(__ia64__) /* on ia64, trust the kernel, don't look for overlaps */ + if (mem) + pciConvertListToHost(pvp->bus,pvp->device,pvp->func, mem); if (!xf86IsSubsetOf(range, m) || ChkConflict(&range, avoid, SETUP) || (mem && ChkConflict(&range, mem, SETUP))) @@ -1627,6 +1664,22 @@ getValidBIOSBase(PCITAG tag, int num) return ret; } +memType +getEmptyPciRange(PCITAG tag, int base_reg) +{ + resPtr avoid = NULL, m = NULL; + memType ret; + + pciVideoPtr pvp = getPciVideoPtr(tag); + if (!pvp) return 0; + getPciRangesForMapping(pvp,&m,&avoid); + ret = findPciRange(tag,m,avoid,pvp->size[base_reg]); + xf86FreeResList(avoid); + xf86FreeResList(m); + + return ret; +} + /* * xf86Bus.c interface */ diff --git a/hw/xfree86/dummylib/Makefile.am b/hw/xfree86/dummylib/Makefile.am index c2cb8ef78..ab62a6b54 100644 --- a/hw/xfree86/dummylib/Makefile.am +++ b/hw/xfree86/dummylib/Makefile.am @@ -12,7 +12,7 @@ if NEED_STRLCAT STRL_SRCS = $(top_srcdir)/os/strlcat.c $(top_srcdir)/os/strlcpy.c endif -libdummy_a_SOURCES = getvalidbios.c \ +libdummy_a_SOURCES = getvalidbios.c getemptypci.c \ pcitestmulti.c xf86allocscripi.c \ xf86addrestolist.c xf86drvmsg.c xf86drvmsgverb.c \ xf86getverb.c \ @@ -24,6 +24,7 @@ libdummy_a_SOURCES = getvalidbios.c \ libdummy_nonserver_a_SOURCES = \ fatalerror.c \ getvalidbios.c \ + getemptypci.c \ logvwrite.c \ pcitestmulti.c \ $(STRL_SRCS) \ diff --git a/hw/xfree86/dummylib/getemptypci.c b/hw/xfree86/dummylib/getemptypci.c new file mode 100644 index 000000000..03119d3d9 --- /dev/null +++ b/hw/xfree86/dummylib/getemptypci.c @@ -0,0 +1,19 @@ + +#ifdef HAVE_XORG_CONFIG_H +#include +#endif + +#include +#include "os.h" +#include "xf86.h" +#include "xf86Priv.h" + +/* + * Utility functions required by libxf86_os. + */ + +memType +getEmptyPciRange(PCITAG tag, int base_reg) +{ + return 0; +} diff --git a/hw/xfree86/os-support/bus/Pci.c b/hw/xfree86/os-support/bus/Pci.c index 007c07a34..eeed8b00f 100644 --- a/hw/xfree86/os-support/bus/Pci.c +++ b/hw/xfree86/os-support/bus/Pci.c @@ -1068,6 +1068,20 @@ xf86scanpci(int flags) return pci_devp; } +pciConfigPtr +xf86GetPciConfigFromTag(PCITAG Tag) +{ + pciConfigPtr pDev; + int i = 0; + + for (i = 0 ; (pDev = pci_devp[i]) && i <= MAX_PCI_DEVICES; i++) { + if (Tag == pDev->tag) + return pDev; + } + + return NULL; /* Bad data */ +} + CARD32 pciCheckForBrokenBase(PCITAG Tag,int basereg) { @@ -1158,13 +1172,18 @@ handlePciBIOS( PCITAG Tag, int basereg, unsigned char * buf, int len ) /* if we use a mem base save it and move it out of the way */ if (b_reg >= 0 && b_reg <= 5) { + memType emptybase; savebase = pciReadLong(Tag, PCI_MAP_REG_START+(b_reg<<2)); xf86MsgVerb(X_INFO,5,"xf86ReadPciBios: modifying membase[%i]" " for device %i:%i:%i\n", basereg, (int)PCI_BUS_FROM_TAG(Tag), (int)PCI_DEV_FROM_TAG(Tag), (int)PCI_FUNC_FROM_TAG(Tag)); + if (!(emptybase = getEmptyPciRange(Tag,b_reg))) { + xf86Msg(X_ERROR,"Cannot find empty range to map base to\n"); + return 0; + } pciWriteLong(Tag, PCI_MAP_REG_START + (b_reg << 2), - (CARD32)~0); + emptybase); } /* Set ROM base address and enable ROM address decoding */ pciWriteLong(Tag, PCI_MAP_ROM_REG, romaddr diff --git a/hw/xfree86/os-support/bus/xf86Pci.h b/hw/xfree86/os-support/bus/xf86Pci.h index 9c67e85f9..69254ce8c 100644 --- a/hw/xfree86/os-support/bus/xf86Pci.h +++ b/hw/xfree86/os-support/bus/xf86Pci.h @@ -774,6 +774,7 @@ pointer xf86MapPciMem(int ScreenNum, int Flags, PCITAG Tag, int xf86ReadPciBIOS(unsigned long Offset, PCITAG Tag, int basereg, unsigned char *Buf, int Len); pciConfigPtr *xf86scanpci(int flags); +pciConfigPtr xf86GetPciConfigFromTag(PCITAG Tag); extern int pciNumBuses; diff --git a/hw/xfree86/os-support/linux/lnx_pci.c b/hw/xfree86/os-support/linux/lnx_pci.c index 3505887bc..4d883146a 100644 --- a/hw/xfree86/os-support/linux/lnx_pci.c +++ b/hw/xfree86/os-support/linux/lnx_pci.c @@ -201,12 +201,14 @@ xf86GetOSOffsetFromPCI(PCITAG tag, int space, unsigned long base) fn = devfn & 0x7; if (tag == pciTag(bus,dev,fn)) { /* ok now look through all the BAR values of this device */ + pciConfigPtr pDev = xf86GetPciConfigFromTag(tag); + for (ndx=0; ndx<7; ndx++) { unsigned long savePtr, flagMask; if (ndx == 6) - savePtr = pciReadLong(tag, PCI_CMD_BIOS_REG); + savePtr = pDev->pci_baserom; else /* this the ROM bar */ - savePtr = pciReadLong(tag, PCI_CMD_BASE_REG + (0x4 * ndx)); + savePtr = (&pDev->pci_base0)[ndx]; /* Ignore unset base addresses. The kernel may * have reported non-zero size and address even * if they are disabled (e.g. disabled ROM BAR).