All of lore.kernel.org
 help / color / mirror / Atom feed
From: Nirmal Patel <nirmal.patel@linux.intel.com>
To: <linux-pci@vger.kernel.org>, nirmal.patel@intel.com
Subject: Re: [PATCH v4] PCI: vmd: Add feature to scan BIOS enumerated devices.
Date: Wed, 27 May 2026 12:01:39 -0700	[thread overview]
Message-ID: <20260527120139.00004254@linux.intel.com> (raw)
In-Reply-To: <20260522150829.5246-1-nirmal.patel@linux.intel.com>

On Fri, 22 May 2026 15:08:29 +0000
Nirmal Patel <nirmal.patel@linux.intel.com> wrote:

> Newer VMD  with device ID 0x28c1 has unique settings compared to its
> predecessor where BIOS enumerates the entire VMD device tree and
> assigns respective configurations.
> 
> VMD configuration BAR0 carries over from GNR legacy VMD as the
> mechanism to access the configuration space of the devices owned by
> VMD. The size of this window is fixed at 256 MB, where each function
> consumes 4 KB and every bus consumes 1 MB.
> 
> The shadow and scratchpad registers have been relocated from the VMD
> configuration space to the VMD MMIO space in VMD BAR4/BAR5, otherwise
> refers to as MEMBAR2 or MSI-X bar.
> 
> VMD MSI-X remapping enable/disable is no longer supported.
> 
> All the VMD driver code needs to do is to obtain bus hide range along
> with shadow register values set by BIOS and perform a bus scan.
> 
> The patch also involves small refactoring of vmd_enable_domain
> function.
> 
> Signed-off-by: Nirmal Patel <nirmal.patel@linux.intel.com>
> ---
> v4 : Updating vmd_set_msi_remapping for supported devices only.
> v3 : Hard code configbar .end to 0xff same as probe.c; Adjust membar2
>      offset to accommodate more registers in 28C1. Remove redundant
>      IORESOURCE_MEM check in vmd_prepare_offsets_and_bus.
> v2 : Using PCI features flag instead of devic ID and fixing corner
> cases for vmd_remove_irq.
> ---
>  drivers/pci/controller/vmd.c | 174
> +++++++++++++++++++++++++++-------- include/linux/pci_ids.h      |
> 1 + 2 files changed, 136 insertions(+), 39 deletions(-)
> 
> diff --git a/drivers/pci/controller/vmd.c
> b/drivers/pci/controller/vmd.c index d4ae250d4bc6..7c90243b5ff0 100644
> --- a/drivers/pci/controller/vmd.c
> +++ b/drivers/pci/controller/vmd.c
> @@ -37,6 +37,12 @@
>  #define MB2_SHADOW_OFFSET	0x2000
>  #define MB2_SHADOW_SIZE		16
>  
> +/* DMR BAR4 register offsets */
> +#define SHADOW_MEMBAR1_28C1		0x2818 /* MEMBAR1
> physical address */ +#define SHADOW_MEMBAR2_28C1
> 0x2820 /* MEMBAR2 physical address */ +#define
> BASE_ID_REG_28C1		0x2840 +#define
> MEMBAR2_OFFSET_28C1		0x30d0 +
>  enum vmd_features {
>  	/*
>  	 * Device may contain registers which hint the physical
> location of the @@ -77,6 +83,15 @@ enum vmd_features {
>  	 * proper power management of the SoC.
>  	 */
>  	VMD_FEAT_BIOS_PM_QUIRK		= (1 << 5),
> +
> +	/*
> +	 * Newer VMD  with device ID 0x28c1 has unique settings
> compared to its
> +	 * predecessor where BIOS enumerates the entire VMD device
> tree and
> +	 * stores respective configurations including bus start
> range and
> +	 * shadow registers in VMD MMIO space in VMD BAR4/BAR5,
> otherwise refers
> +	 * to as MEMBAR2 or MSI-X bar.
> +	 */
> +	VMD_FEAT_USE_BIOS_INFO		= (1 << 6),
>  };
>  
>  #define VMD_BIOS_PM_QUIRK_LTR	0x1003	/* 3145728 ns */
> @@ -142,6 +157,7 @@ struct vmd_dev {
>  	u8			first_vec;
>  	char			*name;
>  	int			instance;
> +	unsigned long		features;
>  };
>  
>  static inline struct vmd_dev *vmd_from_bus(struct pci_bus *bus)
> @@ -365,6 +381,8 @@ static int vmd_create_irq_domain(struct vmd_dev
> *vmd) static void vmd_set_msi_remapping(struct vmd_dev *vmd, bool
> enable) {
>  	u16 reg;
> +	if (!!(vmd->features & VMD_FEAT_USE_BIOS_INFO))
> +		return;
>  
>  	pci_read_config_word(vmd->dev, PCI_REG_VMCONFIG, &reg);
>  	reg = enable ? (reg & ~VMCONFIG_MSI_REMAP) :
> @@ -374,6 +392,8 @@ static void vmd_set_msi_remapping(struct vmd_dev
> *vmd, bool enable) 
>  static void vmd_remove_irq_domain(struct vmd_dev *vmd)
>  {
> +	if (!!(vmd->features & VMD_FEAT_USE_BIOS_INFO))
> +		return;
>  	/*
>  	 * Some production BIOS won't enable remapping between soft
> reboots.
>  	 * Ensure remapping is restored before unloading the driver.
> @@ -393,7 +413,12 @@ static void __iomem *vmd_cfg_addr(struct vmd_dev
> *vmd, struct pci_bus *bus, unsigned int devfn, int reg, int len)
>  {
>  	unsigned int busnr_ecam = bus->number - vmd->busn_start;
> -	u32 offset = PCIE_ECAM_OFFSET(busnr_ecam, devfn, reg);
> +	u32 offset;
> +
> +	if (!!(vmd->features & VMD_FEAT_USE_BIOS_INFO))
> +		busnr_ecam = bus->number;
> +
> +	offset = PCIE_ECAM_OFFSET(busnr_ecam, devfn, reg);
>  
>  	if (offset + len >=
> resource_size(&vmd->dev->resource[VMD_CFGBAR])) return NULL;
> @@ -661,6 +686,46 @@ static int vmd_get_bus_number_start(struct
> vmd_dev *vmd) return 0;
>  }
>  
> +static int vmd_get_bus_info_from_bar4(struct vmd_dev *vmd,
> +				       resource_size_t *offset1,
> +				       resource_size_t *offset2)
> +{
> +	u64 phys1, phys2, bar4_2840;
> +	void __iomem *bar4;
> +	u32 base_id;
> +	u8 base_bus;
> +
> +
> +	bar4 = pci_ioremap_bar(vmd->dev, 4);
> +	if (!bar4)
> +		return -ENOMEM;
> +
> +	/* Read shadow registers for MEMBAR1 and MEMBAR2 physical
> addresses. */
> +	phys1 = readq(bar4 + SHADOW_MEMBAR1_28C1);
> +	phys2 = readq(bar4 + SHADOW_MEMBAR2_28C1);
> +
> +	/*
> +	 * Read and set bus start number from Base ID register.
> +	 * 24-bit Base ID register is part of 64-bit shadowed reqid
> hide
> +	 * range register and holds segment, bus, device and
> function.
> +	 */
> +	bar4_2840 = readq(bar4 + BASE_ID_REG_28C1);
> +	base_id = bar4_2840 & 0xFFFFFF;
> +	base_bus = base_id >> 8;
> +	vmd->busn_start = base_bus;
> +
> +	/* Calculate offsets like vmd_get_phys_offsets() does. */
> +	if (phys1)
> +		*offset1 = vmd->dev->resource[VMD_MEMBAR1].start -
> +			(phys1 & PCI_BASE_ADDRESS_MEM_MASK);
> +	if (phys2)
> +		*offset2 = vmd->dev->resource[VMD_MEMBAR2].start -
> +			(phys2 & PCI_BASE_ADDRESS_MEM_MASK);
> +
> +	pci_iounmap(vmd->dev, bar4);
> +	return 0;
> +}
> +
>  static irqreturn_t vmd_irq(int irq, void *data)
>  {
>  	struct vmd_irq_list *irqs = data;
> @@ -711,6 +776,53 @@ static int vmd_alloc_irqs(struct vmd_dev *vmd)
>  	return 0;
>  }
>  
> +static int vmd_prepare_offsets_and_bus(struct vmd_dev *vmd,
> +					unsigned long features,
> +					resource_size_t
> *membar2_offset,
> +					resource_size_t *offset1,
> +					resource_size_t *offset2)
> +{
> +	int ret;
> +
> +	/*
> +	 * Shadow registers may exist in certain VMD device ids
> which allow
> +	 * guests to correctly assign host physical addresses to the
> root ports
> +	 * and child devices. These registers will either return the
> host value
> +	 * or 0, depending on an enable bit in the VMD device.
> +	 */
> +	/*
> +	 * For certain VMD devices (i.e. 0x28C1), BIOS places device
> info
> +	 * in BAR4 shadow registers to determine the base bus number
> and memory
> +	 * offsets.
> +	 */
> +	if (features & VMD_FEAT_USE_BIOS_INFO) {
> +		*membar2_offset = MEMBAR2_OFFSET_28C1;
> +		ret = vmd_get_bus_info_from_bar4(vmd, offset1,
> offset2);
> +		if (ret)
> +			return ret;
> +	} else if (features & VMD_FEAT_HAS_MEMBAR_SHADOW) {
> +		*membar2_offset = MB2_SHADOW_OFFSET +
> MB2_SHADOW_SIZE;
> +		ret = vmd_get_phys_offsets(vmd, true, offset1,
> offset2);
> +		if (ret)
> +			return ret;
> +	} else if (features & VMD_FEAT_HAS_MEMBAR_SHADOW_VSCAP) {
> +		ret = vmd_get_phys_offsets(vmd, false, offset1,
> offset2);
> +		if (ret)
> +			return ret;
> +	}
> +
> +	/*
> +	 * Certain VMD devices may have a root port configuration
> option which
> +	 * limits the bus range to between 0-127, 128-255, or
> 224-255.
> +	 */
> +	if (features & VMD_FEAT_HAS_BUS_RESTRICTIONS) {
> +		ret = vmd_get_bus_number_start(vmd);
> +		if (ret)
> +			return ret;
> +	}
> +	return 0;
> +}
> +
>  /*
>   * Since VMD is an aperture to regular PCIe root ports, only allow
> it to
>   * control features that the OS is allowed to control on the
> physical PCI bus. @@ -784,38 +896,16 @@ static int
> vmd_enable_domain(struct vmd_dev *vmd, unsigned long features) struct
> pci_dev *dev; int ret;
>  
> -	/*
> -	 * Shadow registers may exist in certain VMD device ids
> which allow
> -	 * guests to correctly assign host physical addresses to the
> root ports
> -	 * and child devices. These registers will either return the
> host value
> -	 * or 0, depending on an enable bit in the VMD device.
> -	 */
> -	if (features & VMD_FEAT_HAS_MEMBAR_SHADOW) {
> -		membar2_offset = MB2_SHADOW_OFFSET + MB2_SHADOW_SIZE;
> -		ret = vmd_get_phys_offsets(vmd, true, &offset[0],
> &offset[1]);
> -		if (ret)
> -			return ret;
> -	} else if (features & VMD_FEAT_HAS_MEMBAR_SHADOW_VSCAP) {
> -		ret = vmd_get_phys_offsets(vmd, false, &offset[0],
> &offset[1]);
> -		if (ret)
> -			return ret;
> -	}
> -
> -	/*
> -	 * Certain VMD devices may have a root port configuration
> option which
> -	 * limits the bus range to between 0-127, 128-255, or 224-255
> -	 */
> -	if (features & VMD_FEAT_HAS_BUS_RESTRICTIONS) {
> -		ret = vmd_get_bus_number_start(vmd);
> -		if (ret)
> -			return ret;
> -	}
> +	ret = vmd_prepare_offsets_and_bus(vmd, features,
> &membar2_offset,
> +					  &offset[0], &offset[1]);
> +	if (ret)
> +		return ret;
>  
>  	res = &vmd->dev->resource[VMD_CFGBAR];
>  	vmd->resources[0] = (struct resource) {
>  		.name  = "VMD CFGBAR",
>  		.start = vmd->busn_start,
> -		.end   = vmd->busn_start + (resource_size(res) >>
> 20) - 1,
> +		.end   = 0xff,
>  		.flags = IORESOURCE_BUS | IORESOURCE_PCI_FIXED,
>  	};
>  
> @@ -868,19 +958,21 @@ static int vmd_enable_domain(struct vmd_dev
> *vmd, unsigned long features)
>  	 * acceptable because the guest is usually CPU-limited and
> MSI
>  	 * remapping doesn't become a performance bottleneck.
>  	 */
> -	if (!(features & VMD_FEAT_CAN_BYPASS_MSI_REMAP) ||
> -	    offset[0] || offset[1]) {
> -		ret = vmd_alloc_irqs(vmd);
> -		if (ret)
> -			return ret;
> +	if (!(features & VMD_FEAT_USE_BIOS_INFO)) {
> +		if (!(features & VMD_FEAT_CAN_BYPASS_MSI_REMAP) ||
> +		    offset[0] || offset[1]) {
> +			ret = vmd_alloc_irqs(vmd);
> +			if (ret)
> +				return ret;
>  
> -		vmd_set_msi_remapping(vmd, true);
> +			vmd_set_msi_remapping(vmd, true);
>  
> -		ret = vmd_create_irq_domain(vmd);
> -		if (ret)
> -			return ret;
> -	} else {
> -		vmd_set_msi_remapping(vmd, false);
> +			ret = vmd_create_irq_domain(vmd);
> +			if (ret)
> +				return ret;
> +		} else {
> +			vmd_set_msi_remapping(vmd, false);
> +		}
>  	}
>  
>  	pci_add_resource(&resources, &vmd->resources[0]);
> @@ -998,6 +1090,7 @@ static int vmd_probe(struct pci_dev *dev, const
> struct pci_device_id *id) 
>  	vmd->dev = dev;
>  	vmd->sysdata.domain = PCI_DOMAIN_NR_NOT_SET;
> +	vmd->features = features;
>  	vmd->instance = ida_alloc(&vmd_instance_ida, GFP_KERNEL);
>  	if (vmd->instance < 0)
>  		return vmd->instance;
> @@ -1114,6 +1207,9 @@ static const struct pci_device_id vmd_ids[] = {
>  		.driver_data = VMD_FEAT_HAS_MEMBAR_SHADOW |
>  				VMD_FEAT_HAS_BUS_RESTRICTIONS |
>  				VMD_FEAT_CAN_BYPASS_MSI_REMAP,},
> +	{PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_VMD_28C1),
> +		.driver_data = VMD_FEAT_HAS_MEMBAR_SHADOW |
> +				VMD_FEAT_USE_BIOS_INFO,},
>  	{PCI_VDEVICE(INTEL, 0x467f),
>  		.driver_data = VMD_FEATS_CLIENT,},
>  	{PCI_VDEVICE(INTEL, 0x4c3d),
> diff --git a/include/linux/pci_ids.h b/include/linux/pci_ids.h
> index 24cb42f66e4b..2a8ebe7df92e 100644
> --- a/include/linux/pci_ids.h
> +++ b/include/linux/pci_ids.h
> @@ -2889,6 +2889,7 @@
>  #define PCI_DEVICE_ID_INTEL_HDA_ICH8	0x284b
>  #define PCI_DEVICE_ID_INTEL_ICH8_6	0x2850
>  #define PCI_DEVICE_ID_INTEL_VMD_28C0	0x28c0
> +#define PCI_DEVICE_ID_INTEL_VMD_28C1    0x28c1
>  #define PCI_DEVICE_ID_INTEL_ICH9_0	0x2910
>  #define PCI_DEVICE_ID_INTEL_ICH9_2	0x2912
>  #define PCI_DEVICE_ID_INTEL_ICH9_3	0x2913

Gentle reminder. FYI, I also addressed comments from AI review.

Thanks.

      parent reply	other threads:[~2026-05-27 19:01 UTC|newest]

Thread overview: 4+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-05-22 15:08 [PATCH v4] PCI: vmd: Add feature to scan BIOS enumerated devices Nirmal Patel
2026-05-22 15:42 ` sashiko-bot
2026-05-26 19:43   ` Nirmal Patel
2026-05-27 19:01 ` Nirmal Patel [this message]

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=20260527120139.00004254@linux.intel.com \
    --to=nirmal.patel@linux.intel.com \
    --cc=linux-pci@vger.kernel.org \
    --cc=nirmal.patel@intel.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.