Linux PCI subsystem development
 help / color / mirror / Atom feed
From: sashiko-bot@kernel.org
To: "Srirangan Madhavan" <smadhavan@nvidia.com>,
	linux-cxl@vger.kernel.org, linux-pci@vger.kernel.org
Subject: Re: [PATCH v16 11/18] PCI/CXL: Cache endpoint HDM state during PCI enumeration
Date: Thu, 29 Oct 2026 15:29:49 +0000	[thread overview]
Message-ID: <sashiko-outbox-189606@kernel.org> (raw)
In-Reply-To: <20261029145703.2185231-12-smadhavan@nvidia.com>

Thank you for your contribution! Sashiko AI review found 4 potential issue(s) to consider:
- [High] PCI/CXL: out-of-bounds MMIO read in cxl_pci_hdm_map_regs() before bounds check
- [High] PCI/CXL: pci_cxl_hdm_cache_init() runs before BAR assignment and bridge enable
- [Medium] cxl/pci: unchecked PCI_POSSIBLE_ERROR() in cxl_set_mem_enable() corrupts cached dvsec_ctrl
- [Medium] cxl/pci: disable_hdm() leaves stale CXL_HDM_DECODER_ENABLE in cached global_ctrl
--

commit a7ebc7643586c3c5e8b6adb597a5c7e1384802d3
Author: Srirangan Madhavan <smadhavan@nvidia.com>

PCI/CXL: Cache endpoint HDM state during PCI enumeration

This commit caches endpoint HDM decoder and Device DVSEC state in
pci_dev->hdm during PCI enumeration so that CXL reset paths can restore
hardware state without a bound CXL driver. It also hooks DVSEC control
updates in the CXL core to keep the cached DVSEC control value synchronized.

> diff --git a/drivers/cxl/core/pci.c b/drivers/cxl/core/pci.c
> index e2034a2179b48..44fa9d5d1a052 100644
> --- a/drivers/cxl/core/pci.c
> +++ b/drivers/cxl/core/pci.c
> @@ -209,22 +209,24 @@ static int cxl_set_mem_enable(struct cxl_dev_state *cxlds, u16 val)
>  	struct pci_dev *pdev = to_pci_dev(cxlds->dev);
>  	int d = cxlds->cxl_dvsec;
>  	u16 ctrl;
> -	int rc;
> +	int rc, ret = 1;
>  
>  	rc = pci_read_config_word(pdev, d + PCI_DVSEC_CXL_CTRL, &ctrl);
>  	if (rc)
>  		return pcibios_err_to_errno(rc);
>  
> -	if ((ctrl & PCI_DVSEC_CXL_MEM_ENABLE) == val)
> -		return 1;
> -	ctrl &= ~PCI_DVSEC_CXL_MEM_ENABLE;
> -	ctrl |= val;
> +	if ((ctrl & PCI_DVSEC_CXL_MEM_ENABLE) != val) {
> +		ctrl &= ~PCI_DVSEC_CXL_MEM_ENABLE;
> +		ctrl |= val;
>  
> -	rc = pci_write_config_word(pdev, d + PCI_DVSEC_CXL_CTRL, ctrl);
> -	if (rc)
> -		return pcibios_err_to_errno(rc);
> +		rc = pci_write_config_word(pdev, d + PCI_DVSEC_CXL_CTRL, ctrl);
> +		if (rc)
> +			return pcibios_err_to_errno(rc);
> +		ret = 0;
> +	}
>  
> -	return 0;
> +	cxl_hdm_cache_set_dvsec_ctrl(pdev, ctrl);

[Severity: Medium]
What happens in cxl_set_mem_enable() if the device is undergoing a reset or
the link is down when pci_read_config_word() reads PCI_DVSEC_CXL_CTRL?

On root complexes where config reads to an unresponsive device return
PCIBIOS_SUCCESSFUL with ctrl == 0xffff, cxl_set_mem_enable() does not check
PCI_POSSIBLE_ERROR(ctrl). When called from clear_mem_enable() during endpoint
port teardown (which is not serialized by pci_dev_lock(pdev)) or from
devm_cxl_enable_mem(), ctrl (0xffff) is masked to 0xfffb (or kept as 0xffff),
written back to PCI_DVSEC_CXL_CTRL (including RW1C Viral_Status,
Viral_Enable, Cache_Enable, and reserved bits), and then stored into
pdev->hdm->dvsec_ctrl via cxl_hdm_cache_set_dvsec_ctrl().

Unlike cxl_hdm_cache_refresh_decoder(), which checks global_ctrl != U32_MAX
to preserve the last good value when the device does not respond,
cxl_hdm_cache_set_dvsec_ctrl() unconditionally overwrites
pdev->hdm->dvsec_ctrl, permanently clobbering the cached DVSEC control state
for subsequent resets. Should cxl_set_mem_enable() check
PCI_POSSIBLE_ERROR(ctrl) before modifying and caching ctrl?

[Severity: Medium]
Also, while cxl_set_mem_enable() and clear_mem_enable() keep
pdev->hdm->dvsec_ctrl updated on both enable and teardown, does disable_hdm()
leave a stale CXL_HDM_DECODER_ENABLE bit in pdev->hdm->global_ctrl?

When the CXL driver binds and sets up endpoint decoders:

  devm_cxl_endpoint_decoders_setup()
    cxl_hdm_decode_init()
      devm_cxl_enable_hdm()
      devm_cxl_enable_mem()
        cxl_set_mem_enable()
          cxl_hdm_cache_set_dvsec_ctrl()
    devm_cxl_enumerate_decoders()
      cxl_hdm_refresh_decoder()
        cxl_hdm_cache_refresh_decoder()
          info->global_ctrl = readl(hdm + CXL_HDM_DECODER_CTRL_OFFSET);

cxl_hdm_cache_refresh_decoder() latches CXL_HDM_DECODER_ENABLE into
pdev->hdm->global_ctrl. When the endpoint port driver later unbinds (or fails
probe after refreshing a decoder), devres runs clear_mem_enable(), which
clears PCI_DVSEC_CXL_MEM_ENABLE in pdev->hdm->dvsec_ctrl, and then runs
disable_hdm():

drivers/cxl/core/pci.c:disable_hdm() {
	global_ctrl = readl(hdm + CXL_HDM_DECODER_CTRL_OFFSET);
	writel(global_ctrl & ~CXL_HDM_DECODER_ENABLE,
	       hdm + CXL_HDM_DECODER_CTRL_OFFSET);
}

Because disable_hdm() does not update pdev->hdm->global_ctrl, a subsequent
reset while unbound will restore CXL_HDM_DECODER_ENABLE in hardware while
PCI_DVSEC_CXL_MEM_ENABLE is disabled, and a later CXL driver re-bind in
cxl_hdm_decode_init() will see CXL_HDM_DECODER_ENABLE already set and skip
devm_cxl_enable_hdm(). Should disable_hdm() (and devm_cxl_enable_hdm()) also
update pdev->hdm->global_ctrl?

[ ... ]

> diff --git a/drivers/pci/bus.c b/drivers/pci/bus.c
> index 655ed53436d3e..aad567fb7c560 100644
> --- a/drivers/pci/bus.c
> +++ b/drivers/pci/bus.c
> @@ -359,6 +359,7 @@ void pci_bus_add_device(struct pci_dev *dev)
>  
>  	/* Save config space for error recoverability */
>  	pci_save_state(dev);
> +	pci_cxl_hdm_cache_init(dev);

[Severity: High]
Can pci_cxl_hdm_cache_init() run here before BAR resources are validated and
assigned, or while upstream bridges still have memory decoding disabled?

On x86 ACPI boot, acpi_init() (subsys_initcall) enumerates the root bus and
calls pci_bus_add_devices() -> pci_bus_add_device() before pci_subsys_init()
runs pcibios_resource_survey() and before pcibios_assign_resources()
(fs_initcall) assigns unassigned resources:

  acpi_init()
    acpi_pci_root_add()
      pci_bus_add_devices()
        pci_bus_add_device()
          pci_cxl_hdm_cache_init()

In drivers/pci/cxl.c, cxl_pci_bar_assigned() checks res->start and res->end
without checking res->parent:

drivers/pci/cxl.c:cxl_pci_bar_assigned() {
	struct resource *res = &pdev->resource[bar];

	return pci_resource_len(pdev, bar) &&
	       !(res->flags & (IORESOURCE_UNSET | IORESOURCE_DISABLED)) &&
	       resource_type(res) == IORESOURCE_MEM && res->start && res->end;
}

And cxl_pci_hdm_read_info() temporarily sets PCI_COMMAND_MEMORY directly on
pdev without enabling upstream bridges:

drivers/pci/cxl.c:cxl_pci_hdm_read_info() {
	if (!(command & PCI_COMMAND_MEMORY)) {
		rc = pci_write_config_word(pdev, PCI_COMMAND,
					   command | PCI_COMMAND_MEMORY);
		...
	}
}

This appears to have two consequences:
1. If a disabled device has an unclaimed BIOS BAR address that conflicts with
   another resource, enabling PCI_COMMAND_MEMORY before
   pcibios_resource_survey() claims and validates resources can activate the
   conflicting BAR decoding.
2. If the endpoint BAR or an upstream bridge window is assigned later during
   pcibios_assign_resources(), or if an upstream PCI-to-PCI bridge does not
   have PCI_COMMAND_MEMORY enabled yet, pci_cxl_hdm_cache_init() fails and
   leaves pdev->hdm permanently NULL.

> diff --git a/drivers/pci/cxl.c b/drivers/pci/cxl.c
> new file mode 100644
> index 0000000000000..b992f71d964a8
> --- /dev/null
> +++ b/drivers/pci/cxl.c
[ ... ]
> +static void __iomem *cxl_pci_hdm_map_regs(struct pci_dev *pdev,
> +					  struct cxl_register_map *map,
> +					  resource_size_t *hdm_offset,
> +					  int *decoder_count)
> +{
> +	struct cxl_reg_map *hdm_map = &map->component_map.hdm_decoder;
> +	void __iomem *hdm;
> +	int rc;
> +
> +	/* cxl_setup_regs() populates hdm_map, including its valid bit. */
> +	rc = cxl_setup_regs(map);
> +	if (rc)
> +		return ERR_PTR(rc);
> +	if (!hdm_map->valid)
> +		return ERR_PTR(-ENODEV);
> +	if (hdm_map->offset > map->max_size ||
> +	    hdm_map->size > map->max_size - hdm_map->offset)
> +		return ERR_PTR(-ENXIO);

[Severity: High]
Does cxl_setup_regs(map) perform MMIO reads past map->max_size before the
bounds check in cxl_pci_hdm_map_regs() is reached?

In __pci_cxl_hdm_cache_init(), cxl_find_regblock() populates map via
cxl_decode_regblock(), which only checks offset > pci_resource_len(pdev, bar)
and sets map->max_size = pci_resource_len(pdev, bar) - offset.

When cxl_pci_hdm_map_regs() calls cxl_setup_regs(map):

  cxl_pci_hdm_map_regs()
    cxl_setup_regs()
      cxl_map_regblock()
        map->base = ioremap(map->resource, map->max_size);
      cxl_probe_regs()
        cxl_probe_component_regs()

cxl_probe_component_regs() unconditionally adds CXL_CM_OFFSET (0x1000) to
map->base, reads CXL_CM_CAP_HDR_OFFSET, and then reads each capability header
at base + offset (which is map->base + hdm_map->offset) without checking
map->max_size.

If a device's remaining BAR length (map->max_size) is smaller than
CXL_CM_OFFSET + sizeof(u32) or smaller than hdm_map->offset + sizeof(u32),
won't cxl_probe_component_regs() fault on the unmapped ioremap guard page
before cxl_pci_hdm_map_regs() checks hdm_map->offset against map->max_size?

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20261029145703.2185231-1-smadhavan@nvidia.com?part=11

  reply	other threads:[~2026-10-29 15:29 UTC|newest]

Thread overview: 37+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-10-29 14:56 [PATCH v16 00/18] PCI/CXL: Add CXL reset support for Type 2 devices Srirangan Madhavan
2026-10-29 14:56 ` [PATCH v16 01/18] cxl: Clarify decoder interleave limit Srirangan Madhavan
2026-10-29 15:00   ` sashiko-bot
2026-10-29 14:56 ` [PATCH v16 02/18] cxl: Share CXL port upstream PCI device lookup Srirangan Madhavan
2026-10-29 15:07   ` sashiko-bot
2026-10-29 14:56 ` [PATCH v16 03/18] cxl: Move decoder declarations to shared header Srirangan Madhavan
2026-10-29 15:03   ` sashiko-bot
2026-10-29 14:56 ` [PATCH v16 04/18] cxl: Embed decoder configuration in a standalone structure Srirangan Madhavan
2026-10-29 15:03   ` sashiko-bot
2026-10-29 14:56 ` [PATCH v16 05/18] cxl: Introduce endpoint HDM decoder settings Srirangan Madhavan
2026-10-29 15:03   ` sashiko-bot
2026-10-29 14:56 ` [PATCH v16 06/18] cxl: Move HDM decoder helpers to built-in code Srirangan Madhavan
2026-10-29 15:03   ` sashiko-bot
2026-10-29 14:56 ` [PATCH v16 07/18] cxl: Share HDM decoder register unpacking Srirangan Madhavan
2026-10-29 15:09   ` sashiko-bot
2026-10-29 14:56 ` [PATCH v16 08/18] cxl: Move cxl_rwsem into built-in code Srirangan Madhavan
2026-10-29 15:04   ` sashiko-bot
2026-10-29 14:56 ` [PATCH v16 09/18] cxl: Refresh cached PCI HDM decoder settings Srirangan Madhavan
2026-10-29 15:08   ` sashiko-bot
2026-10-29 14:56 ` [PATCH v16 10/18] cxl: Build register block helpers into the kernel Srirangan Madhavan
2026-10-29 15:07   ` sashiko-bot
2026-10-29 14:56 ` [PATCH v16 11/18] PCI/CXL: Cache endpoint HDM state during PCI enumeration Srirangan Madhavan
2026-10-29 15:29   ` sashiko-bot [this message]
2026-10-29 14:56 ` [PATCH v16 12/18] PCI/CXL: Add CXL Device Reset sequencing Srirangan Madhavan
2026-10-29 15:05   ` sashiko-bot
2026-10-29 14:56 ` [PATCH v16 13/18] cxl: Validate and synchronize HDM ranges around reset Srirangan Madhavan
2026-10-29 15:25   ` sashiko-bot
2026-10-29 14:56 ` [PATCH v16 14/18] PCI/CXL: Reject reset with unsafe function scope Srirangan Madhavan
2026-10-29 15:10   ` sashiko-bot
2026-10-29 14:57 ` [PATCH v16 15/18] PCI/CXL: Restore CXL state after PCI reset Srirangan Madhavan
2026-10-29 15:29   ` sashiko-bot
2026-10-29 14:57 ` [PATCH v16 16/18] PCI/CXL: Expose CXL Reset as a PCI reset method Srirangan Madhavan
2026-10-29 15:12   ` sashiko-bot
2026-10-29 14:57 ` [PATCH v16 17/18] PCI/CXL: Restore CXL state after CXL bus reset Srirangan Madhavan
2026-10-29 15:22   ` sashiko-bot
2026-10-29 14:57 ` [PATCH v16 18/18] cxl: Add KUnit checks for HDM decoder unpacking Srirangan Madhavan
2026-10-29 15:09   ` sashiko-bot

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=sashiko-outbox-189606@kernel.org \
    --to=sashiko-bot@kernel.org \
    --cc=linux-cxl@vger.kernel.org \
    --cc=linux-pci@vger.kernel.org \
    --cc=sashiko-reviews@lists.linux.dev \
    --cc=smadhavan@nvidia.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 a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox