Bug 198113

Summary: Linux does not check for PCI I/O collisions with VGA ISA alias positively decoded by PCI/PCIe bridges
Product: Drivers Reporter: Rudolf Marek (r.marek)
Component: PCIAssignee: drivers_pci (drivers_pci)
Status: NEW ---    
Severity: normal    
Priority: P1    
Hardware: All   
OS: Linux   
Kernel Version: any Subsystem:
Regression: No Bisected commit-id:
Attachments: lspci from affected system

Description Rudolf Marek 2017-12-09 19:23:05 UTC
Created attachment 261065 [details]
lspci from affected system

The Gigabyte motherboard GA-P67-DS3-B3 MB has a legacy PCI slots. The audigy
PCI card cannot work properly. Similar card, audigi2 works fine in same slot (also INTA). The windoze notice this problem and move card to some other I/O.

snd_emu10k1 0000:05:00.0: AC'97 0 does not respond - RESET
snd_emu10k1 0000:05:00.0: AC'97 0 access error (not audio or modem codec)
snd_emu10k1: probe of 0000:05:00.0 failed with error -13

The culprit is actually not a IRQ routing problem but a problem with wrong PCI I/O port allocation done by BIOS.

The audigy card has only one I/O region:
05:00.0 Multimedia audio controller: Creative Labs SB Audigy (rev 03)
        Subsystem: Creative Labs SB0090 Audigy Player/OEM
        Control: I/O+ Mem- BusMaster- SpecCycle- MemWINV- VGASnoop- ParErr- Stepping- SERR- FastB2B- DisINTx-
        Status: Cap+ 66MHz- UDF- FastB2B+ ParErr- DEVSEL=medium >TAbort- <TAbort- <MAbort- >SERR- <PERR- INTx-
        Interrupt: pin A routed to IRQ 0
        Region 0: I/O ports at cfc0 [size=32]
        Capabilities: [dc] Power Management version 2
                Flags: PMEClk- DSI+ D1+ D2+ AuxCurrent=0mA PME(D0-,D1-,D2-,D3hot-,D3cold-)
                Status: D0 NoSoftRst- PME-Enable- DSel=0 DScale=0 PME-

The dump with isadump -f reveals that the actual BAR address is not occupied by the card I/O space but by a alias which repeats all over the PCI I/O space.

The culprit is enabled legacy VGA routing over a PCIe bridge from bus 0 to bus 1 (see a1pci.txt attached). The bridge has VGA+ bit turned on.

Overall it looks like this:

  00:01.0 PCI bridge to [bus 01]
  00:01.0   implicit bridge window [io 0x03b0-0x03df] + ISA aliases
  01:xx.0 VGA device legacy resource [io 0x03b0-0x03df]
  00:1c.3 PCI bridge to [bus 04-05]
  00:1c.3   bridge window [io 0xc000-0xcfff]
  04:00.0 PCI bridge to [bus 05]
  04:00.0   bridge window [io 0xc000-0xcfff]
  05:00.0 Audigy reg 0x10: [io 0xcfc0-0xcfdf]

0xcfc0 is a 10-bit alias of 0x03c0, so both bridges (00:01.0 and
00:1c.3) will claim it

Here is a quote from random datasheet of PCI/PCIe bridge:


*  Memory accesses in the range 0x000A_0000 to
*  I/O addresses in the first 64 Kbytes of the I/O address
space (Address[31:16] for PCIe are 0x0000) and where
Address[9:0] is in the range of 0x3B0 to 0x3BB or 0x3C0
to 0x3DF (inclusive of ISA address aliases -
Address[15:10] may possess any value and is not used in
the decoding)

So, as you can see, it effectively forces the VGA I/O region to be mirrored all over the 64K I/O space.

What is interesting  is that the PCI bridge specs version 1.2?, added a new bit 4 (which is previously RO 0) which allows to decode only the full address for the 0x3b0/0x3c0 :


This bit enables the bridge to provide 16-bit decoding of
VGA I/O address precluding the decoding of alias addresses
every 1 KB. This bit has meaning only if VGA Enable bit is

According to Bjorn, The PCI-to-PCI Bridge spec, r1.2, sec, says VGA_16BIT_EN controls the bridge decoding for "all VGA I/O register accesses that are forwarded from primary to secondary." 

The Linux should do following:

Turn on VGA_16BIT_EN if supported by the bridge

If VGA_16BIT_EN is not supported by the bridge, try to reassign
the peer I/O BAR so it doesn't conflict with the 0x3b0-0x3df

The older PCI specs specifies the VGA_16BIT_EN as read as zero, so
it could be safe to try to turn it on all bridges, because maybe
the VGA arbiter can later switch another VGA card as primary.

For additional details and discussion see:
"PCI VGA 0x3b0/0x3C0 aliases handling in Linux" on linux-pci mailing list: