All of lore.kernel.org
 help / color / mirror / Atom feed
From: David Matlack <dmatlack@google.com>
To: Irene Wang <yiranirenewang@gmail.com>
Cc: Paolo Bonzini <pbonzini@redhat.com>,
	Sean Christopherson <seanjc@google.com>,
	Andrew Jones <andrew.jones@linux.dev>,
	Thomas Huth <thuth@redhat.com>,
	kvm@vger.kernel.org, Yosry Ahmed <yosryahmed@google.com>,
	Jim Mattson <jmattson@google.com>
Subject: Re: [kvm-unit-tests PATCH v2] x86: pci: Support unaligned register access in PCI config read/write
Date: Fri, 7 Aug 2026 22:47:45 +0000	[thread overview]
Message-ID: <anZgkSwhjHDG3dWy@google.com> (raw)
In-Reply-To: <20260804232344.2976694-2-yiranirenewang@gmail.com>

On 2026-08-04 11:23 PM, Irene Wang wrote:
> Per the PCI Local Bus Specification (Section 3.2.2.3.2, "Configuration
> Mechanism #1"), port 0xCF8 (CONFIG_ADDRESS) requires a DWORD-aligned
> register offset (bits [1:0] = 00b), while byte and word offsets within
> the DWORD must be selected via data port 0xCFC + (reg & 3).
> 
> Previously, PCI_CONF1_ADDRESS did not clear bits [1:0], and 8-bit /
> 16-bit helpers always accessed base port 0xCFC directly. This worked
> in QEMU because its PCI host bridge emulation preserves unaligned bits
> in CONFIG_ADDRESS and uses them during CONFIG_DATA accesses. However,
> in strictly spec-compliant VMMs (and potentially real hardware), bits
> [1:0] of 0xCF8 are ignored, causing non-aligned reads/writes at port
> 0xCFC to erroneously target byte 0 of the DWORD. In practice, this
> causes pci_find_dev() to read the vendor ID twice instead of reading
> the vendor ID and device ID.
> 
> Fix this by masking `reg` with `~3` in PCI_CONF1_ADDRESS and adding
> the `(reg & 3)` offset to the CONFIG_DATA port for 8-bit and 16-bit
> accessors, ensuring compatibility across QEMU and other VMMs. Additionally,
> assert that 16-bit accesses do not use an offset of 3, as reading/writing a
> word across DWORD boundaries (port 0xCFC + 3) is invalid per spec.
> 
> Assisted-by: Gemini:gemini-3.6-flash
> Signed-off-by: Irene Wang <yiranirenewang@gmail.com>
> ---
> v1 -> v2:
>  - Enclose macro parameter `dev` in parentheses in PCI_CONF1_ADDRESS.
>  - Add assert((reg & 3) != 3) in pci_config_readw() and pci_config_writew()
>    to guard against illegal 16-bit accesses crossing DWORD boundaries.
> 
> Link to v1: https://lore.kernel.org/kvm/20260730182053.905335-1-yiranirenewang@gmail.com/
> 
>  lib/x86/asm/pci.h | 12 +++++++-----
>  1 file changed, 7 insertions(+), 5 deletions(-)
> 
> diff --git a/lib/x86/asm/pci.h b/lib/x86/asm/pci.h
> index 03e55c27..da0b59a3 100644
> --- a/lib/x86/asm/pci.h
> +++ b/lib/x86/asm/pci.h
> @@ -9,18 +9,19 @@
>  #include "pci.h"
>  #include "x86/asm/io.h"
>  
> -#define PCI_CONF1_ADDRESS(dev, reg)	((0x1 << 31) | (dev << 8) | reg)
> +#define PCI_CONF1_ADDRESS(dev, reg)	((0x1 << 31) | ((dev) << 8) | ((reg) & ~3))

A comment here would be useful. Spec-compliant host bridges (including
emulations) should ignore the lower 2 bits so clearing it should
technically be unnecessary. The reason we have to clear it is because
QEMU is not spec-compliant and will consume those 2 bits.

>  
>  static inline uint8_t pci_config_readb(pcidevaddr_t dev, uint8_t reg)
>  {
>      outl(PCI_CONF1_ADDRESS(dev, reg), 0xCF8);
> -    return inb(0xCFC);
> +    return inb(0xCFC + (reg & 3));
>  }
>  
>  static inline uint16_t pci_config_readw(pcidevaddr_t dev, uint8_t reg)
>  {
> +    assert((reg & 3) != 3);
>      outl(PCI_CONF1_ADDRESS(dev, reg), 0xCF8);
> -    return inw(0xCFC);
> +    return inw(0xCFC + (reg & 3));
>  }
>  
>  static inline uint32_t pci_config_readl(pcidevaddr_t dev, uint8_t reg)
> @@ -33,14 +34,15 @@ static inline void pci_config_writeb(pcidevaddr_t dev, uint8_t reg,
>                                       uint8_t val)
>  {
>      outl(PCI_CONF1_ADDRESS(dev, reg), 0xCF8);
> -    outb(val, 0xCFC);
> +    outb(val, 0xCFC + (reg & 3));
>  }
>  
>  static inline void pci_config_writew(pcidevaddr_t dev, uint8_t reg,
>                                       uint16_t val)
>  {
> +    assert((reg & 3) != 3);

Should assert((reg & 3) == 0) be added to the readl/writel routines as
well?

>      outl(PCI_CONF1_ADDRESS(dev, reg), 0xCF8);
> -    outw(val, 0xCFC);
> +    outw(val, 0xCFC + (reg & 3));
>  }
>  
>  static inline void pci_config_writel(pcidevaddr_t dev, uint8_t reg,
> -- 
> 2.55.0.571.g244d577d93-goog
> 

  parent reply	other threads:[~2026-08-07 22:47 UTC|newest]

Thread overview: 5+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-08-04 23:23 [kvm-unit-tests PATCH v2] x86: pci: Support unaligned register access in PCI config read/write Irene Wang
2026-08-05 15:36 ` Jim Mattson
2026-08-07 22:47 ` David Matlack [this message]
2026-08-13 20:57   ` [kvm-unit-tests PATCH v3] " Irene Wang
2026-08-13 21:30     ` David Matlack

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=anZgkSwhjHDG3dWy@google.com \
    --to=dmatlack@google.com \
    --cc=andrew.jones@linux.dev \
    --cc=jmattson@google.com \
    --cc=kvm@vger.kernel.org \
    --cc=pbonzini@redhat.com \
    --cc=seanjc@google.com \
    --cc=thuth@redhat.com \
    --cc=yiranirenewang@gmail.com \
    --cc=yosryahmed@google.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.