|
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index] Re: [PATCH v4] vpci: Add resizable bar support
On 19.12.2024 06:21, Jiqian Chen wrote:
> --- /dev/null
> +++ b/xen/drivers/vpci/rebar.c
> @@ -0,0 +1,131 @@
> +/* SPDX-License-Identifier: GPL-2.0-only */
> +/*
> + * Copyright (C) 2024 Advanced Micro Devices, Inc. All Rights Reserved.
> + *
> + * Author: Jiqian Chen <Jiqian.Chen@xxxxxxx>
> + */
> +
> +#include <xen/sched.h>
> +#include <xen/vpci.h>
> +
> +static void cf_check rebar_ctrl_write(const struct pci_dev *pdev,
> + unsigned int reg,
> + uint32_t val,
> + void *data)
> +{
> + struct vpci_bar *bar = data;
> + uint64_t size = PCI_REBAR_CTRL_SIZE(val);
> +
> + if ( bar->enabled )
> + {
> + /*
> + * Refuse to resize a BAR while memory decoding is enabled, as
> + * otherwise the size of the mapped region in the p2m would become
> + * stale with the newly set BAR size, and the position of the BAR
> + * would be reset to undefined. Note the PCIe specification also
> + * forbids resizing a BAR with memory decoding enabled.
> + */
> + if ( size != bar->size )
> + gprintk(XENLOG_ERR,
> + "%pp: refuse to resize BAR with memory decoding
> enabled\n",
> + &pdev->sbdf);
> + return;
> + }
> +
> + if ( !((size >> PCI_REBAR_SIZE_BIAS) & bar->resizable_sizes) )
> + gprintk(XENLOG_WARNING,
> + "%pp: new size %#lx is not supported by hardware\n",
> + &pdev->sbdf, size);
> +
> + bar->size = size;
Shouldn't at least this be in an "else" to the if() above?
> + bar->addr = 0;
For maximum compatibility with the behavior on bare metal, would we
perhaps better ...
> + bar->guest_addr = 0;
> + pci_conf_write32(pdev->sbdf, reg, val);
... re-read the BAR from hardware after this write?
Similar consideration may apply to ->guest_addr: Driver writers knowing
how their hardware behaves may expect that merely some of the bits of
the address get cleared (if the size increases).
> +static int cf_check init_rebar(struct pci_dev *pdev)
> +{
> + uint32_t ctrl;
> + unsigned int nbars;
> + unsigned int rebar_offset = pci_find_ext_capability(pdev->sbdf,
> +
> PCI_EXT_CAP_ID_REBAR);
> +
> + if ( !rebar_offset )
> + return 0;
> +
> + if ( !is_hardware_domain(pdev->domain) )
> + {
> + printk(XENLOG_ERR "%pp: resizable BARs unsupported for unpriv %pd\n",
> + &pdev->sbdf, pdev->domain);
> + return -EOPNOTSUPP;
> + }
> +
> + ctrl = pci_conf_read32(pdev->sbdf, rebar_offset + PCI_REBAR_CTRL(0));
> + nbars = MASK_EXTR(ctrl, PCI_REBAR_CTRL_NBAR_MASK);
> +
> + for ( unsigned int i = 0; i < nbars; i++ )
> + {
> + int rc;
> + struct vpci_bar *bar;
> + unsigned int index;
> +
> + ctrl = pci_conf_read32(pdev->sbdf, rebar_offset + PCI_REBAR_CTRL(i));
> + index = ctrl & PCI_REBAR_CTRL_BAR_IDX;;
Nit: No double semicolons please.
> + if ( index >= PCI_HEADER_NORMAL_NR_BARS )
> + {
> + /*
> + * TODO: for failed pathes, need to hide ReBar capability
> + * from hardware domain instead of returning an error.
> + */
> + printk(XENLOG_ERR "%pd %pp: too big BAR number %u in
> REBAR_CTRL\n",
> + pdev->domain, &pdev->sbdf, index);
> + return -EINVAL;
With the TODO unaddressed, is it actually appropriate to return an error
here? Shouldn't we continue in a best effort manner? (Question also to
Roger as the maintainer.)
> + }
> +
> + bar = &pdev->vpci->header.bars[index];
> + if ( bar->type != VPCI_BAR_MEM64_LO && bar->type != VPCI_BAR_MEM32 )
> + {
> + printk(XENLOG_ERR "%pd %pp: BAR%u is not in memory space\n",
> + pdev->domain, &pdev->sbdf, index);
> + return -EINVAL;
Same question here then.
> + }
> +
> + rc = vpci_add_register(pdev->vpci, vpci_hw_read32, vpci_hw_write32,
> + rebar_offset + PCI_REBAR_CAP(i), 4, NULL);
> + if ( rc )
> + {
> + printk(XENLOG_ERR "%pd %pp: fail to add reg of REBAR_CAP
> rc=%d\n",
> + pdev->domain, &pdev->sbdf, rc);
> + return rc;
> + }
> +
> + rc = vpci_add_register(pdev->vpci, vpci_hw_read32, rebar_ctrl_write,
> + rebar_offset + PCI_REBAR_CTRL(i), 4, bar);
> + if ( rc )
> + {
> + printk(XENLOG_ERR "%pd %pp: fail to add reg of REBAR_CTRL
> rc=%d\n",
> + pdev->domain, &pdev->sbdf, rc);
> + return rc;
> + }
> +
> + bar->resizable_sizes |=
> + (pci_conf_read32(pdev->sbdf, rebar_offset + PCI_REBAR_CAP(i)) >>
> + PCI_REBAR_CAP_SHIFT);
Imo this would better use = in place of |= and (see also below) would also
better use MASK_EXTR() just like ...
> + bar->resizable_sizes |=
> + ((uint64_t)MASK_EXTR(ctrl, PCI_REBAR_CTRL_SIZES) <<
> + (32 - PCI_REBAR_CAP_SHIFT));
... this one does.
Further I think you want to truncate the value for 32-bit BARs, such that
rebar_ctrl_write() would properly reject attempts to set sizes of 4G and
above for them.
> --- a/xen/drivers/vpci/vpci.c
> +++ b/xen/drivers/vpci/vpci.c
> @@ -232,6 +232,12 @@ void cf_check vpci_hw_write16(
> pci_conf_write16(pdev->sbdf, reg, val);
> }
>
> +void cf_check vpci_hw_write32(
> + const struct pci_dev *pdev, unsigned int reg, uint32_t val, void *data)
> +{
> + pci_conf_write32(pdev->sbdf, reg, val);
> +}
This function is being added just to handle writing of a r/o register.
Can't you better re-use vpci_ignored_write()?
> --- a/xen/include/xen/pci_regs.h
> +++ b/xen/include/xen/pci_regs.h
> @@ -459,6 +459,7 @@
> #define PCI_EXT_CAP_ID_ARI 14
> #define PCI_EXT_CAP_ID_ATS 15
> #define PCI_EXT_CAP_ID_SRIOV 16
> +#define PCI_EXT_CAP_ID_REBAR 21 /* Resizable BAR */
>
> /* Advanced Error Reporting */
> #define PCI_ERR_UNCOR_STATUS 4 /* Uncorrectable Error Status */
> @@ -541,6 +542,19 @@
> #define PCI_VNDR_HEADER_REV(x) (((x) >> 16) & 0xf)
> #define PCI_VNDR_HEADER_LEN(x) (((x) >> 20) & 0xfff)
>
> +/* Resizable BARs */
> +#define PCI_REBAR_SIZE_BIAS 20
I think it would be best if all register definitions came first, and
auxiliary ones followed afterwards (maybe even separated by a brief
comment for clarity).
> +#define PCI_REBAR_CAP(n) (4 + 8 * (n)) /* capability register */
> +#define PCI_REBAR_CAP_SHIFT 4 /* shift for supported
> BAR sizes */
> +#define PCI_REBAR_CTRL(n) (8 + 8 * (n)) /* control register */
Something's odd with the padding here. Please be consistent with the use
of whitespace (ought to be only hard tabs here afaict).
> +#define PCI_REBAR_CTRL_BAR_IDX 0x00000007 /* BAR index */
> +#define PCI_REBAR_CTRL_NBAR_MASK 0x000000E0 /* # of resizable BARs
> */
> +#define PCI_REBAR_CTRL_BAR_SIZE 0x00001F00 /* BAR size */
This field is 6 bits wide in the spec I'm looking at. Or else BAR sizes
2^^52 and up can't be encoded.
> +#define PCI_REBAR_CTRL_SIZE(v) \
> + (1UL << (MASK_EXTR(v, PCI_REBAR_CTRL_BAR_SIZE) \
> + + PCI_REBAR_SIZE_BIAS))
> +#define PCI_REBAR_CTRL_SIZES 0xFFFF0000U /* supported
> BAR sizes */
PCI_REBAR_CAP_SHIFT and PCI_REBAR_CTRL_SIZES don't fit together very well.
Imo both want representing as masks.
Jan
|
![]() |
Lists.xenproject.org is hosted with RackSpace, monitoring our |