Skip to content

Commit b98096e

Browse files
chejianjwenlingz
authored andcommitted
dm: pci: fix the MMIO regions overlap when getting bar size
PCI spec says that the procedure of sizing a BAR is as follows: 1) disable the decode via command register 2) save the original value of BAR register 3) write all-1 to the BAR register 4) read BAR register back, calculate the size 5) restore the original value to BAR register 6) re-enable the decode via command register Some driver does not disable the decode of BAR register via the command register before sizing a BAR. This will lead to a overlay of the BAR addresses when trying to register the intermediate BAR address via register_bar. A stateful variable sizing is used to keep track of such kind of BAR address changes and workaroud this violation. Currently this issue is only found when audio device is passed through to Windows 10 guest. When it is fixed in the Windows audio driver, this patch should be reverted. v1 -> v2: - change the commit message to add the procedure of BAR sizing from PCI spec Tracked-On: #2962 Signed-off-by: Jian Jun Chen <jian.jun.chen@intel.com> Reviewed-by: Binbin Wu <binbin.wu@intel.com> Acked-by: Yu Wang <yu1.wang@intel.com>
1 parent 011134d commit b98096e

File tree

2 files changed

+44
-11
lines changed

2 files changed

+44
-11
lines changed

devicemodel/hw/pci/core.c

Lines changed: 42 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -595,15 +595,17 @@ memen(struct pci_vdev *dev)
595595
*/
596596
static void
597597
update_bar_address(struct vmctx *ctx, struct pci_vdev *dev, uint64_t addr,
598-
int idx, int type)
598+
int idx, int type, bool ignore_reg_unreg)
599599
{
600-
bool decode;
600+
bool decode = false;
601601
uint64_t orig_addr = dev->bar[idx].addr;
602602

603-
if (dev->bar[idx].type == PCIBAR_IO)
604-
decode = porten(dev);
605-
else
606-
decode = memen(dev);
603+
if (!ignore_reg_unreg) {
604+
if (dev->bar[idx].type == PCIBAR_IO)
605+
decode = porten(dev);
606+
else
607+
decode = memen(dev);
608+
}
607609

608610
if (decode)
609611
unregister_bar(dev, idx);
@@ -629,7 +631,7 @@ update_bar_address(struct vmctx *ctx, struct pci_vdev *dev, uint64_t addr,
629631
register_bar(dev, idx);
630632

631633
/* update bar mapping */
632-
if (dev->dev_ops->vdev_update_bar_map)
634+
if (dev->dev_ops->vdev_update_bar_map && decode)
633635
dev->dev_ops->vdev_update_bar_map(ctx, dev, idx, orig_addr);
634636
}
635637

@@ -2048,6 +2050,7 @@ pci_cfgrw(struct vmctx *ctx, int vcpu, int in, int bus, int slot, int func,
20482050
struct pci_vdev_ops *ops;
20492051
int idx, needcfg;
20502052
uint64_t addr, bar, mask;
2053+
bool decode, ignore_reg_unreg = false;
20512054

20522055
bi = pci_businfo[bus];
20532056
if (bi != NULL) {
@@ -2130,6 +2133,30 @@ pci_cfgrw(struct vmctx *ctx, int vcpu, int in, int bus, int slot, int func,
21302133
return;
21312134
idx = (coff - PCIR_BAR(0)) / 4;
21322135
mask = ~(dev->bar[idx].size - 1);
2136+
2137+
if (dev->bar[idx].type == PCIBAR_IO)
2138+
decode = porten(dev);
2139+
else
2140+
decode = memen(dev);
2141+
2142+
/* Some driver does not disable the decode of BAR
2143+
* register via the command register before sizing a
2144+
* BAR. This will lead to a overlay of the BAR
2145+
* addresses when trying to register the intermediate
2146+
* BAR address via register_bar. A stateful variable
2147+
* sizing is used to keep track of such kind of BAR
2148+
* address changes and workaroud this violation.
2149+
*/
2150+
if (decode) {
2151+
if (!dev->bar[idx].sizing && (*eax == ~0U)) {
2152+
dev->bar[idx].sizing = true;
2153+
ignore_reg_unreg = true;
2154+
} else if (dev->bar[idx].sizing && (*eax != ~0U)) {
2155+
dev->bar[idx].sizing = false;
2156+
ignore_reg_unreg = true;
2157+
}
2158+
}
2159+
21332160
switch (dev->bar[idx].type) {
21342161
case PCIBAR_NONE:
21352162
dev->bar[idx].addr = bar = 0;
@@ -2143,15 +2170,17 @@ pci_cfgrw(struct vmctx *ctx, int vcpu, int in, int bus, int slot, int func,
21432170
*/
21442171
if (addr != dev->bar[idx].addr) {
21452172
update_bar_address(ctx, dev, addr, idx,
2146-
PCIBAR_IO);
2173+
PCIBAR_IO,
2174+
ignore_reg_unreg);
21472175
}
21482176
break;
21492177
case PCIBAR_MEM32:
21502178
addr = bar = *eax & mask;
21512179
bar |= PCIM_BAR_MEM_SPACE | PCIM_BAR_MEM_32;
21522180
if (addr != dev->bar[idx].addr) {
21532181
update_bar_address(ctx, dev, addr, idx,
2154-
PCIBAR_MEM32);
2182+
PCIBAR_MEM32,
2183+
ignore_reg_unreg);
21552184
}
21562185
break;
21572186
case PCIBAR_MEM64:
@@ -2160,7 +2189,8 @@ pci_cfgrw(struct vmctx *ctx, int vcpu, int in, int bus, int slot, int func,
21602189
PCIM_BAR_MEM_PREFETCH;
21612190
if (addr != (uint32_t)dev->bar[idx].addr) {
21622191
update_bar_address(ctx, dev, addr, idx,
2163-
PCIBAR_MEM64);
2192+
PCIBAR_MEM64,
2193+
ignore_reg_unreg);
21642194
}
21652195
break;
21662196
case PCIBAR_MEMHI64:
@@ -2170,7 +2200,8 @@ pci_cfgrw(struct vmctx *ctx, int vcpu, int in, int bus, int slot, int func,
21702200
bar = addr >> 32;
21712201
if (bar != dev->bar[idx - 1].addr >> 32) {
21722202
update_bar_address(ctx, dev, addr, idx - 1,
2173-
PCIBAR_MEMHI64);
2203+
PCIBAR_MEMHI64,
2204+
ignore_reg_unreg);
21742205
}
21752206
break;
21762207
default:

devicemodel/include/pci_core.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@
3232
#include <sys/queue.h>
3333

3434
#include <assert.h>
35+
#include <stdbool.h>
3536
#include "types.h"
3637
#include "pcireg.h"
3738

@@ -104,6 +105,7 @@ struct pcibar {
104105
enum pcibar_type type; /* io or memory */
105106
uint64_t size;
106107
uint64_t addr;
108+
bool sizing;
107109
};
108110

109111
#define PI_NAMESZ 40

0 commit comments

Comments
 (0)