Bug #4139: Fix a BAR remapping bug that could lead to IERR and system hang.
(Egbert Eich)
This commit is contained in:
parent
91239d83f4
commit
deebf6bd51
12
ChangeLog
12
ChangeLog
|
@ -1,3 +1,15 @@
|
||||||
|
2006-05-18 Adam Jackson <ajax@freedesktop.org>
|
||||||
|
|
||||||
|
* 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 <ajax@freedesktop.org>
|
2006-05-18 Adam Jackson <ajax@freedesktop.org>
|
||||||
|
|
||||||
* hw/xfree86/os-support/linux/lnx_pci.c:
|
* hw/xfree86/os-support/linux/lnx_pci.c:
|
||||||
|
|
|
@ -155,6 +155,7 @@ void xf86AddDevToEntity(int entityIndex, GDevPtr dev);
|
||||||
extern void xf86PostPreInit(void);
|
extern void xf86PostPreInit(void);
|
||||||
extern void xf86PostScreenInit(void);
|
extern void xf86PostScreenInit(void);
|
||||||
extern memType getValidBIOSBase(PCITAG tag, int num);
|
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);
|
extern int pciTestMultiDeviceCard(int bus, int dev, int func, PCITAG** pTag);
|
||||||
|
|
||||||
/* xf86Config.c */
|
/* xf86Config.c */
|
||||||
|
|
|
@ -1498,29 +1498,117 @@ xf86ReallocatePciResources(int entityIndex, resPtr pRes)
|
||||||
/*
|
/*
|
||||||
* BIOS releated
|
* BIOS releated
|
||||||
*/
|
*/
|
||||||
memType
|
static resPtr
|
||||||
getValidBIOSBase(PCITAG tag, int num)
|
getOwnResources(pciVideoPtr pvp, resPtr mem)
|
||||||
{
|
{
|
||||||
pciVideoPtr pvp = NULL;
|
|
||||||
PciBusPtr pbp;
|
|
||||||
resPtr m = NULL;
|
|
||||||
resPtr tmp, avoid, mem = NULL;
|
|
||||||
resRange range;
|
resRange range;
|
||||||
memType ret;
|
|
||||||
int n = 0;
|
|
||||||
int i;
|
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;
|
if (!xf86PciVideoInfo) return 0;
|
||||||
|
|
||||||
while ((pvp = xf86PciVideoInfo[n++])) {
|
while ((pvp = xf86PciVideoInfo[n++])) {
|
||||||
if (pciTag(pvp->bus,pvp->device,pvp->func) == tag)
|
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;
|
if (!pvp) return 0;
|
||||||
|
|
||||||
biosSize = pvp->biosSize;
|
biosSize = pvp->biosSize;
|
||||||
alignment = (1 << biosSize) - 1;
|
|
||||||
if (biosSize > 24)
|
if (biosSize > 24)
|
||||||
biosSize = 24;
|
biosSize = 24;
|
||||||
|
|
||||||
|
@ -1531,15 +1619,8 @@ getValidBIOSBase(PCITAG tag, int num)
|
||||||
/* In some cases the BIOS base register contains the size mask */
|
/* In some cases the BIOS base register contains the size mask */
|
||||||
if ((memType)(-1 << biosSize) == PCIGETROM(pvp->biosBase))
|
if ((memType)(-1 << biosSize) == PCIGETROM(pvp->biosBase))
|
||||||
return 0;
|
return 0;
|
||||||
/* Make sure we don't conflict with our own mem resources */
|
mem = getOwnResources(pvp,mem);
|
||||||
for (i = 0; i < 6; i++) {
|
P_M_RANGE(range, tag, pvp->biosBase,biosSize,ResExcMemBlock);
|
||||||
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);
|
|
||||||
ret = pvp->biosBase;
|
ret = pvp->biosBase;
|
||||||
break;
|
break;
|
||||||
case ROM_BASE_MEM0:
|
case ROM_BASE_MEM0:
|
||||||
|
@ -1550,7 +1631,7 @@ getValidBIOSBase(PCITAG tag, int num)
|
||||||
case ROM_BASE_MEM5:
|
case ROM_BASE_MEM5:
|
||||||
if (!pvp->memBase[num] || (pvp->size[num] < biosSize))
|
if (!pvp->memBase[num] || (pvp->size[num] < biosSize))
|
||||||
return 0;
|
return 0;
|
||||||
P_M_RANGE(range, TAG(pvp),pvp->memBase[num],biosSize,
|
P_M_RANGE(range, tag ,pvp->memBase[num],biosSize,
|
||||||
ResExcMemBlock);
|
ResExcMemBlock);
|
||||||
ret = pvp->memBase[num];
|
ret = pvp->memBase[num];
|
||||||
break;
|
break;
|
||||||
|
@ -1562,59 +1643,15 @@ getValidBIOSBase(PCITAG tag, int num)
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Now find the ranges for validation */
|
/* Now find the ranges for validation */
|
||||||
avoid = xf86DupResList(pciAvoidRes);
|
getPciRangesForMapping(pvp,&m,&avoid);
|
||||||
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);
|
|
||||||
|
|
||||||
if (!ret) {
|
if (!ret) {
|
||||||
/* Return a possible window */
|
/* Return a possible window */
|
||||||
while (m) {
|
ret = findPciRange(tag,m,avoid,biosSize);
|
||||||
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;
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
#if !defined(__ia64__) /* on ia64, trust the kernel, don't look for overlaps */
|
#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) ||
|
if (!xf86IsSubsetOf(range, m) ||
|
||||||
ChkConflict(&range, avoid, SETUP)
|
ChkConflict(&range, avoid, SETUP)
|
||||||
|| (mem && ChkConflict(&range, mem, SETUP)))
|
|| (mem && ChkConflict(&range, mem, SETUP)))
|
||||||
|
@ -1627,6 +1664,22 @@ getValidBIOSBase(PCITAG tag, int num)
|
||||||
return ret;
|
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
|
* xf86Bus.c interface
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -12,7 +12,7 @@ if NEED_STRLCAT
|
||||||
STRL_SRCS = $(top_srcdir)/os/strlcat.c $(top_srcdir)/os/strlcpy.c
|
STRL_SRCS = $(top_srcdir)/os/strlcat.c $(top_srcdir)/os/strlcpy.c
|
||||||
endif
|
endif
|
||||||
|
|
||||||
libdummy_a_SOURCES = getvalidbios.c \
|
libdummy_a_SOURCES = getvalidbios.c getemptypci.c \
|
||||||
pcitestmulti.c xf86allocscripi.c \
|
pcitestmulti.c xf86allocscripi.c \
|
||||||
xf86addrestolist.c xf86drvmsg.c xf86drvmsgverb.c \
|
xf86addrestolist.c xf86drvmsg.c xf86drvmsgverb.c \
|
||||||
xf86getverb.c \
|
xf86getverb.c \
|
||||||
|
@ -24,6 +24,7 @@ libdummy_a_SOURCES = getvalidbios.c \
|
||||||
libdummy_nonserver_a_SOURCES = \
|
libdummy_nonserver_a_SOURCES = \
|
||||||
fatalerror.c \
|
fatalerror.c \
|
||||||
getvalidbios.c \
|
getvalidbios.c \
|
||||||
|
getemptypci.c \
|
||||||
logvwrite.c \
|
logvwrite.c \
|
||||||
pcitestmulti.c \
|
pcitestmulti.c \
|
||||||
$(STRL_SRCS) \
|
$(STRL_SRCS) \
|
||||||
|
|
|
@ -0,0 +1,19 @@
|
||||||
|
|
||||||
|
#ifdef HAVE_XORG_CONFIG_H
|
||||||
|
#include <xorg-config.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include <X11/X.h>
|
||||||
|
#include "os.h"
|
||||||
|
#include "xf86.h"
|
||||||
|
#include "xf86Priv.h"
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Utility functions required by libxf86_os.
|
||||||
|
*/
|
||||||
|
|
||||||
|
memType
|
||||||
|
getEmptyPciRange(PCITAG tag, int base_reg)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
|
@ -1068,6 +1068,20 @@ xf86scanpci(int flags)
|
||||||
return pci_devp;
|
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
|
CARD32
|
||||||
pciCheckForBrokenBase(PCITAG Tag,int basereg)
|
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 we use a mem base save it and move it out of the way */
|
||||||
if (b_reg >= 0 && b_reg <= 5) {
|
if (b_reg >= 0 && b_reg <= 5) {
|
||||||
|
memType emptybase;
|
||||||
savebase = pciReadLong(Tag, PCI_MAP_REG_START+(b_reg<<2));
|
savebase = pciReadLong(Tag, PCI_MAP_REG_START+(b_reg<<2));
|
||||||
xf86MsgVerb(X_INFO,5,"xf86ReadPciBios: modifying membase[%i]"
|
xf86MsgVerb(X_INFO,5,"xf86ReadPciBios: modifying membase[%i]"
|
||||||
" for device %i:%i:%i\n", basereg,
|
" for device %i:%i:%i\n", basereg,
|
||||||
(int)PCI_BUS_FROM_TAG(Tag), (int)PCI_DEV_FROM_TAG(Tag),
|
(int)PCI_BUS_FROM_TAG(Tag), (int)PCI_DEV_FROM_TAG(Tag),
|
||||||
(int)PCI_FUNC_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),
|
pciWriteLong(Tag, PCI_MAP_REG_START + (b_reg << 2),
|
||||||
(CARD32)~0);
|
emptybase);
|
||||||
}
|
}
|
||||||
/* Set ROM base address and enable ROM address decoding */
|
/* Set ROM base address and enable ROM address decoding */
|
||||||
pciWriteLong(Tag, PCI_MAP_ROM_REG, romaddr
|
pciWriteLong(Tag, PCI_MAP_ROM_REG, romaddr
|
||||||
|
|
|
@ -774,6 +774,7 @@ pointer xf86MapPciMem(int ScreenNum, int Flags, PCITAG Tag,
|
||||||
int xf86ReadPciBIOS(unsigned long Offset, PCITAG Tag, int basereg,
|
int xf86ReadPciBIOS(unsigned long Offset, PCITAG Tag, int basereg,
|
||||||
unsigned char *Buf, int Len);
|
unsigned char *Buf, int Len);
|
||||||
pciConfigPtr *xf86scanpci(int flags);
|
pciConfigPtr *xf86scanpci(int flags);
|
||||||
|
pciConfigPtr xf86GetPciConfigFromTag(PCITAG Tag);
|
||||||
|
|
||||||
extern int pciNumBuses;
|
extern int pciNumBuses;
|
||||||
|
|
||||||
|
|
|
@ -201,12 +201,14 @@ xf86GetOSOffsetFromPCI(PCITAG tag, int space, unsigned long base)
|
||||||
fn = devfn & 0x7;
|
fn = devfn & 0x7;
|
||||||
if (tag == pciTag(bus,dev,fn)) {
|
if (tag == pciTag(bus,dev,fn)) {
|
||||||
/* ok now look through all the BAR values of this device */
|
/* ok now look through all the BAR values of this device */
|
||||||
|
pciConfigPtr pDev = xf86GetPciConfigFromTag(tag);
|
||||||
|
|
||||||
for (ndx=0; ndx<7; ndx++) {
|
for (ndx=0; ndx<7; ndx++) {
|
||||||
unsigned long savePtr, flagMask;
|
unsigned long savePtr, flagMask;
|
||||||
if (ndx == 6)
|
if (ndx == 6)
|
||||||
savePtr = pciReadLong(tag, PCI_CMD_BIOS_REG);
|
savePtr = pDev->pci_baserom;
|
||||||
else /* this the ROM bar */
|
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
|
/* Ignore unset base addresses. The kernel may
|
||||||
* have reported non-zero size and address even
|
* have reported non-zero size and address even
|
||||||
* if they are disabled (e.g. disabled ROM BAR).
|
* if they are disabled (e.g. disabled ROM BAR).
|
||||||
|
|
Loading…
Reference in New Issue