diff mbox series

[v3,03/11] vpci/header: Move register assignments from init_bars

Message ID 20210930075223.860329-4-andr2000@gmail.com (mailing list archive)
State Superseded
Headers show
Series PCI devices passthrough on Arm, part 3 | expand

Commit Message

Oleksandr Andrushchenko Sept. 30, 2021, 7:52 a.m. UTC
From: Oleksandr Andrushchenko <oleksandr_andrushchenko@epam.com>

This is in preparation for dynamic assignment of the vPCI register
handlers depending on the domain: hwdom or guest.
The need for this step is that it is easier to have all related functionality
put at one place. When the subsequent patches add decisions on which
handlers to install, e.g. hwdom or guest handlers, then this is easily
achievable.

Signed-off-by: Oleksandr Andrushchenko <oleksandr_andrushchenko@epam.com>

---
Since v1:
 - constify struct pci_dev where possible
 - extend patch description
---
 xen/drivers/vpci/header.c | 83 ++++++++++++++++++++++++++-------------
 1 file changed, 56 insertions(+), 27 deletions(-)

Comments

Roger Pau Monné Oct. 13, 2021, 1:51 p.m. UTC | #1
On Thu, Sep 30, 2021 at 10:52:15AM +0300, Oleksandr Andrushchenko wrote:
> From: Oleksandr Andrushchenko <oleksandr_andrushchenko@epam.com>
> 
> This is in preparation for dynamic assignment of the vPCI register
> handlers depending on the domain: hwdom or guest.
> The need for this step is that it is easier to have all related functionality
> put at one place. When the subsequent patches add decisions on which
> handlers to install, e.g. hwdom or guest handlers, then this is easily
> achievable.

Won't it be possible to select the handlers to install in init_bars
itself?

Splitting it like that means you need to iterate over the numbers of
BARs twice (one in add_bar_handlers and one in init_bars), which makes
it more likely to introduce errors or divergences.

Decoupling the filling of vpci_bar data with setting the handlers
seems slightly confusing.

> Signed-off-by: Oleksandr Andrushchenko <oleksandr_andrushchenko@epam.com>
> 
> ---
> Since v1:
>  - constify struct pci_dev where possible
>  - extend patch description
> ---
>  xen/drivers/vpci/header.c | 83 ++++++++++++++++++++++++++-------------
>  1 file changed, 56 insertions(+), 27 deletions(-)
> 
> diff --git a/xen/drivers/vpci/header.c b/xen/drivers/vpci/header.c
> index f8cd55e7c024..3d571356397a 100644
> --- a/xen/drivers/vpci/header.c
> +++ b/xen/drivers/vpci/header.c
> @@ -445,6 +445,55 @@ static void rom_write(const struct pci_dev *pdev, unsigned int reg,
>          rom->addr = val & PCI_ROM_ADDRESS_MASK;
>  }
>  
> +static int add_bar_handlers(const struct pci_dev *pdev)

Making this const is again misleading IMO, as you end up modifying
fields inside the pdev, you get away with it because vpci data is
stored in a pointer.

> +{
> +    unsigned int i;
> +    struct vpci_header *header = &pdev->vpci->header;
> +    struct vpci_bar *bars = header->bars;
> +    int rc;
> +
> +    /* Setup a handler for the command register. */
> +    rc = vpci_add_register(pdev->vpci, vpci_hw_read16, cmd_write, PCI_COMMAND,
> +                           2, header);
> +    if ( rc )
> +        return rc;
> +
> +    if ( pdev->ignore_bars )
> +        return 0;

You can join both ifs above:

if ( rc || pdev->ignore_bars )
    return rc;

> +
> +    for ( i = 0; i < PCI_HEADER_NORMAL_NR_BARS + 1; i++ )

init_bars deals with both TYPE_NORMAL and TYPE_BRIDGE classes, yet you
seem to unconditionally assume PCI_HEADER_NORMAL_NR_BARS here (even
when below you take into account the different ROM BAR position).

> +    {
> +        if ( (bars[i].type == VPCI_BAR_IO) || (bars[i].type == VPCI_BAR_EMPTY) )
> +            continue;
> +
> +        if ( bars[i].type == VPCI_BAR_ROM )
> +        {
> +            unsigned int rom_reg;
> +            uint8_t header_type = pci_conf_read8(pdev->sbdf,
> +                                                 PCI_HEADER_TYPE) & 0x7f;

Missing newline, and unsigned int preferably for header_type.

> +            if ( header_type == PCI_HEADER_TYPE_NORMAL )
> +                rom_reg = PCI_ROM_ADDRESS;
> +            else
> +                rom_reg = PCI_ROM_ADDRESS1;
> +            rc = vpci_add_register(pdev->vpci, vpci_hw_read32, rom_write,
> +                                   rom_reg, 4, &bars[i]);
> +            if ( rc )
> +                return rc;
> +        }
> +        else
> +        {
> +            uint8_t reg = PCI_BASE_ADDRESS_0 + i * 4;

unsigned int please, we try to avoid using explicitly sized types
unless strictly necessary (ie: when dealing with hardware values for
example).

> +
> +            /* This is either VPCI_BAR_MEM32 or VPCI_BAR_MEM64_{LO|HI}. */
> +            rc = vpci_add_register(pdev->vpci, vpci_hw_read32, bar_write, reg,
> +                                   4, &bars[i]);
> +            if ( rc )
> +                return rc;
> +        }
> +    }
> +    return 0;
> +}
> +
>  static int init_bars(struct pci_dev *pdev)
>  {
>      uint16_t cmd;
> @@ -470,14 +519,8 @@ static int init_bars(struct pci_dev *pdev)
>          return -EOPNOTSUPP;
>      }
>  
> -    /* Setup a handler for the command register. */
> -    rc = vpci_add_register(pdev->vpci, vpci_hw_read16, cmd_write, PCI_COMMAND,
> -                           2, header);
> -    if ( rc )
> -        return rc;

I don't think you need to move the handler for the command register
inside add_bar_handlers: for once it makes the function name not
reflect what it actually does (as it then deals with both BARs and the
command register), and it would also prevent you from having to call
add_bar_handlers in if ignore_bars is set.

Thanks, Roger.
Jan Beulich Oct. 15, 2021, 6:04 a.m. UTC | #2
On 13.10.2021 15:51, Roger Pau Monné wrote:
> On Thu, Sep 30, 2021 at 10:52:15AM +0300, Oleksandr Andrushchenko wrote:
>> --- a/xen/drivers/vpci/header.c
>> +++ b/xen/drivers/vpci/header.c
>> @@ -445,6 +445,55 @@ static void rom_write(const struct pci_dev *pdev, unsigned int reg,
>>          rom->addr = val & PCI_ROM_ADDRESS_MASK;
>>  }
>>  
>> +static int add_bar_handlers(const struct pci_dev *pdev)
> 
> Making this const is again misleading IMO, as you end up modifying
> fields inside the pdev, you get away with it because vpci data is
> stored in a pointer.

I think it was me who asked for const to be added in places like this
one. vpci data hanging off of struct pci_dev is an implementation
artifact imo, not an unavoidable connection. In principle the vpci
data corresponding to a physical device could also be looked up using
e.g. SBDF.

Here the intention really is to leave the physical device unchanged;
that's what the const documents (apart from enforcing).

Jan
Roger Pau Monné Oct. 25, 2021, 2:28 p.m. UTC | #3
On Fri, Oct 15, 2021 at 08:04:56AM +0200, Jan Beulich wrote:
> On 13.10.2021 15:51, Roger Pau Monné wrote:
> > On Thu, Sep 30, 2021 at 10:52:15AM +0300, Oleksandr Andrushchenko wrote:
> >> --- a/xen/drivers/vpci/header.c
> >> +++ b/xen/drivers/vpci/header.c
> >> @@ -445,6 +445,55 @@ static void rom_write(const struct pci_dev *pdev, unsigned int reg,
> >>          rom->addr = val & PCI_ROM_ADDRESS_MASK;
> >>  }
> >>  
> >> +static int add_bar_handlers(const struct pci_dev *pdev)
> > 
> > Making this const is again misleading IMO, as you end up modifying
> > fields inside the pdev, you get away with it because vpci data is
> > stored in a pointer.
> 
> I think it was me who asked for const to be added in places like this
> one. vpci data hanging off of struct pci_dev is an implementation
> artifact imo, not an unavoidable connection. In principle the vpci
> data corresponding to a physical device could also be looked up using
> e.g. SBDF.

I was considering vPCI part an intrinsic part of the pci_dev, but I
can see you thinking otherwise. We similarly have other pieces of data
hanging off pci_dev, so I think it's hard to tell which ones as fine
to have as part of the struct vs as pointer references.

> Here the intention really is to leave the physical device unchanged;
> that's what the const documents (apart from enforcing).

Ack. I wouldn't have asked for those myself, but as said above I can
see your point.

Regards, Roger.
Oleksandr Andrushchenko Oct. 27, 2021, 10:17 a.m. UTC | #4
Hi, Roger!

On 13.10.21 16:51, Roger Pau Monné wrote:
> On Thu, Sep 30, 2021 at 10:52:15AM +0300, Oleksandr Andrushchenko wrote:
>> From: Oleksandr Andrushchenko <oleksandr_andrushchenko@epam.com>
>>
>> This is in preparation for dynamic assignment of the vPCI register
>> handlers depending on the domain: hwdom or guest.
>> The need for this step is that it is easier to have all related functionality
>> put at one place. When the subsequent patches add decisions on which
>> handlers to install, e.g. hwdom or guest handlers, then this is easily
>> achievable.
> Won't it be possible to select the handlers to install in init_bars
> itself?
It is possible
>
> Splitting it like that means you need to iterate over the numbers of
> BARs twice (one in add_bar_handlers and one in init_bars), which makes
> it more likely to introduce errors or divergences.
>
> Decoupling the filling of vpci_bar data with setting the handlers
> seems slightly confusing.
Ok, I won't introduce add_bar_handlers, thus rendering this patch useless.
I'll drop it and re-work the upcoming patches with this respect

Thank you,
Oleksandr
Oleksandr Andrushchenko Oct. 27, 2021, 11:59 a.m. UTC | #5
Hi, Roger!

On 27.10.21 13:17, Oleksandr Andrushchenko wrote:
> Hi, Roger!
>
> On 13.10.21 16:51, Roger Pau Monné wrote:
>> On Thu, Sep 30, 2021 at 10:52:15AM +0300, Oleksandr Andrushchenko wrote:
>>> From: Oleksandr Andrushchenko <oleksandr_andrushchenko@epam.com>
>>>
>>> This is in preparation for dynamic assignment of the vPCI register
>>> handlers depending on the domain: hwdom or guest.
>>> The need for this step is that it is easier to have all related functionality
>>> put at one place. When the subsequent patches add decisions on which
>>> handlers to install, e.g. hwdom or guest handlers, then this is easily
>>> achievable.
>> Won't it be possible to select the handlers to install in init_bars
>> itself?
> It is possible
>> Splitting it like that means you need to iterate over the numbers of
>> BARs twice (one in add_bar_handlers and one in init_bars), which makes
>> it more likely to introduce errors or divergences.
>>
>> Decoupling the filling of vpci_bar data with setting the handlers
>> seems slightly confusing.
> Ok, I won't introduce add_bar_handlers, thus rendering this patch useless.
> I'll drop it and re-work the upcoming patches with this respect
On the other hand after thinking a bit more.
What actually init_bars do?
1. Runs once per each pdev (__init?)
2. Sizes the BARs and detects their type, sets up pdev->vpci->header BAR values
3. Adds register handlers.

For DomU we only need 3), so we can setup guest handlers.
So, from this POV either we need to have a yet another add_bar_handlers
or similar for at least the guests and the case when pdev is assigned back to hwdom.

So this can be a reason to defend the current approach with add_bar_handlers.

Or? Do you have an idea how to do that some other way?
>
> Thank you,
> Oleksandr
Roger Pau Monné Oct. 27, 2021, 1:23 p.m. UTC | #6
On Wed, Oct 27, 2021 at 11:59:47AM +0000, Oleksandr Andrushchenko wrote:
> Hi, Roger!
> 
> On 27.10.21 13:17, Oleksandr Andrushchenko wrote:
> > Hi, Roger!
> >
> > On 13.10.21 16:51, Roger Pau Monné wrote:
> >> On Thu, Sep 30, 2021 at 10:52:15AM +0300, Oleksandr Andrushchenko wrote:
> >>> From: Oleksandr Andrushchenko <oleksandr_andrushchenko@epam.com>
> >>>
> >>> This is in preparation for dynamic assignment of the vPCI register
> >>> handlers depending on the domain: hwdom or guest.
> >>> The need for this step is that it is easier to have all related functionality
> >>> put at one place. When the subsequent patches add decisions on which
> >>> handlers to install, e.g. hwdom or guest handlers, then this is easily
> >>> achievable.
> >> Won't it be possible to select the handlers to install in init_bars
> >> itself?
> > It is possible
> >> Splitting it like that means you need to iterate over the numbers of
> >> BARs twice (one in add_bar_handlers and one in init_bars), which makes
> >> it more likely to introduce errors or divergences.
> >>
> >> Decoupling the filling of vpci_bar data with setting the handlers
> >> seems slightly confusing.
> > Ok, I won't introduce add_bar_handlers, thus rendering this patch useless.
> > I'll drop it and re-work the upcoming patches with this respect
> On the other hand after thinking a bit more.
> What actually init_bars do?
> 1. Runs once per each pdev (__init?)
> 2. Sizes the BARs and detects their type, sets up pdev->vpci->header BAR values
> 3. Adds register handlers.
> 
> For DomU we only need 3), so we can setup guest handlers.

I think you assume that there will always be a hardware domain with
vPCI enabled that will get the device assigned and thus init_bars will
be executed prior to assigning to a domU.

But what about dom0less, or when using a classic PV dom0? In that case
the device won't get assigned to a hardware domain with vPCI support,
so the vpci structure won't be allocated or filled, and hence
init_bars would have to be executed when assigning to a domU.

Thanks, Roger.
Oleksandr Andrushchenko Oct. 27, 2021, 2:06 p.m. UTC | #7
On 27.10.21 16:23, Roger Pau Monné wrote:
> On Wed, Oct 27, 2021 at 11:59:47AM +0000, Oleksandr Andrushchenko wrote:
>> Hi, Roger!
>>
>> On 27.10.21 13:17, Oleksandr Andrushchenko wrote:
>>> Hi, Roger!
>>>
>>> On 13.10.21 16:51, Roger Pau Monné wrote:
>>>> On Thu, Sep 30, 2021 at 10:52:15AM +0300, Oleksandr Andrushchenko wrote:
>>>>> From: Oleksandr Andrushchenko <oleksandr_andrushchenko@epam.com>
>>>>>
>>>>> This is in preparation for dynamic assignment of the vPCI register
>>>>> handlers depending on the domain: hwdom or guest.
>>>>> The need for this step is that it is easier to have all related functionality
>>>>> put at one place. When the subsequent patches add decisions on which
>>>>> handlers to install, e.g. hwdom or guest handlers, then this is easily
>>>>> achievable.
>>>> Won't it be possible to select the handlers to install in init_bars
>>>> itself?
>>> It is possible
>>>> Splitting it like that means you need to iterate over the numbers of
>>>> BARs twice (one in add_bar_handlers and one in init_bars), which makes
>>>> it more likely to introduce errors or divergences.
>>>>
>>>> Decoupling the filling of vpci_bar data with setting the handlers
>>>> seems slightly confusing.
>>> Ok, I won't introduce add_bar_handlers, thus rendering this patch useless.
>>> I'll drop it and re-work the upcoming patches with this respect
>> On the other hand after thinking a bit more.
>> What actually init_bars do?
>> 1. Runs once per each pdev (__init?)
>> 2. Sizes the BARs and detects their type, sets up pdev->vpci->header BAR values
>> 3. Adds register handlers.
>>
>> For DomU we only need 3), so we can setup guest handlers.
> I think you assume that there will always be a hardware domain with
> vPCI enabled that will get the device assigned and thus init_bars will
> be executed prior to assigning to a domU.
Yes, this is the current assumption...
>
> But what about dom0less,
it was decided to put dom0less out of scope for now
>   or when using a classic PV dom0?
I thought that vPCI is only used for PVH Dom0 and it is enough for now
(yes, this is a weak argument, but we do not want PCI passthrough on Arm
to become a never ending game... since 2015...)
>   In that case
> the device won't get assigned to a hardware domain with vPCI support,
> so the vpci structure won't be allocated or filled,
Yes, this is true. But because of the 3 functionflities of the init_bars is
doing it might still need some dis-aggregation, e.g. BAR sizing
is not needed and might not be possible while assigning to a DomU.
So, I think that init_bars will need to be split in any case.
>   and hence
> init_bars would have to be executed when assigning to a domU.
Please see above: not sure init_bars can exist in its form to achieve that.
One of the steps this patch is doing is we split init_bars into
a) register assignment
b) all the reset: initial pdev's header initialization, sizing etc.

The same is true for MSI/MSI-X. When we add support for MSI/MSI-X on Arm
you will see the same: we need to split [1] (this is WIP).

So, I am still convinced that we need add_bar_handlers in some form.
> Thanks, Roger.
>
[1] https://gitlab.com/rahsingh/xen-integration/-/commit/7b898601261fc3ad834ac3d06cc4c784f33c95bb
Roger Pau Monné Oct. 27, 2021, 3:34 p.m. UTC | #8
On Wed, Oct 27, 2021 at 02:06:40PM +0000, Oleksandr Andrushchenko wrote:
> 
> 
> On 27.10.21 16:23, Roger Pau Monné wrote:
> > On Wed, Oct 27, 2021 at 11:59:47AM +0000, Oleksandr Andrushchenko wrote:
> >> Hi, Roger!
> >>
> >> On 27.10.21 13:17, Oleksandr Andrushchenko wrote:
> >>> Hi, Roger!
> >>>
> >>> On 13.10.21 16:51, Roger Pau Monné wrote:
> >>>> On Thu, Sep 30, 2021 at 10:52:15AM +0300, Oleksandr Andrushchenko wrote:
> >>>>> From: Oleksandr Andrushchenko <oleksandr_andrushchenko@epam.com>
> >>>>>
> >>>>> This is in preparation for dynamic assignment of the vPCI register
> >>>>> handlers depending on the domain: hwdom or guest.
> >>>>> The need for this step is that it is easier to have all related functionality
> >>>>> put at one place. When the subsequent patches add decisions on which
> >>>>> handlers to install, e.g. hwdom or guest handlers, then this is easily
> >>>>> achievable.
> >>>> Won't it be possible to select the handlers to install in init_bars
> >>>> itself?
> >>> It is possible
> >>>> Splitting it like that means you need to iterate over the numbers of
> >>>> BARs twice (one in add_bar_handlers and one in init_bars), which makes
> >>>> it more likely to introduce errors or divergences.
> >>>>
> >>>> Decoupling the filling of vpci_bar data with setting the handlers
> >>>> seems slightly confusing.
> >>> Ok, I won't introduce add_bar_handlers, thus rendering this patch useless.
> >>> I'll drop it and re-work the upcoming patches with this respect
> >> On the other hand after thinking a bit more.
> >> What actually init_bars do?
> >> 1. Runs once per each pdev (__init?)
> >> 2. Sizes the BARs and detects their type, sets up pdev->vpci->header BAR values
> >> 3. Adds register handlers.
> >>
> >> For DomU we only need 3), so we can setup guest handlers.
> > I think you assume that there will always be a hardware domain with
> > vPCI enabled that will get the device assigned and thus init_bars will
> > be executed prior to assigning to a domU.
> Yes, this is the current assumption...
> >
> > But what about dom0less,
> it was decided to put dom0less out of scope for now
> >   or when using a classic PV dom0?
> I thought that vPCI is only used for PVH Dom0 and it is enough for now
> (yes, this is a weak argument, but we do not want PCI passthrough on Arm
> to become a never ending game... since 2015...)

I understand that not everything will be supported, that's perfectly
fine, but we should aim to not make supporting those use cases
harder in the future.

> >   In that case
> > the device won't get assigned to a hardware domain with vPCI support,
> > so the vpci structure won't be allocated or filled,
> Yes, this is true. But because of the 3 functionflities of the init_bars is
> doing it might still need some dis-aggregation, e.g. BAR sizing
> is not needed and might not be possible while assigning to a DomU.
> So, I think that init_bars will need to be split in any case.

I understand that BAR sizing will not be needed if the structure is
pre-initialized, but I also cannot see why it would be impossible, at
least on x86.

> >   and hence
> > init_bars would have to be executed when assigning to a domU.
> Please see above: not sure init_bars can exist in its form to achieve that.
> One of the steps this patch is doing is we split init_bars into
> a) register assignment
> b) all the reset: initial pdev's header initialization, sizing etc.
> 
> The same is true for MSI/MSI-X. When we add support for MSI/MSI-X on Arm
> you will see the same: we need to split [1] (this is WIP).
> 
> So, I am still convinced that we need add_bar_handlers in some form.

I'm fine to split it if there's a hard requirement, but I'm afraid so
far I'm not convinced it's required. Maybe if you could elaborate on
why BAR sizing might not be possible when assigning to domU I could be
convinced.

Another option might be to just modify init_bars to have slightly
different paths for dom0 vs domU.

Thanks, Roger.
diff mbox series

Patch

diff --git a/xen/drivers/vpci/header.c b/xen/drivers/vpci/header.c
index f8cd55e7c024..3d571356397a 100644
--- a/xen/drivers/vpci/header.c
+++ b/xen/drivers/vpci/header.c
@@ -445,6 +445,55 @@  static void rom_write(const struct pci_dev *pdev, unsigned int reg,
         rom->addr = val & PCI_ROM_ADDRESS_MASK;
 }
 
+static int add_bar_handlers(const struct pci_dev *pdev)
+{
+    unsigned int i;
+    struct vpci_header *header = &pdev->vpci->header;
+    struct vpci_bar *bars = header->bars;
+    int rc;
+
+    /* Setup a handler for the command register. */
+    rc = vpci_add_register(pdev->vpci, vpci_hw_read16, cmd_write, PCI_COMMAND,
+                           2, header);
+    if ( rc )
+        return rc;
+
+    if ( pdev->ignore_bars )
+        return 0;
+
+    for ( i = 0; i < PCI_HEADER_NORMAL_NR_BARS + 1; i++ )
+    {
+        if ( (bars[i].type == VPCI_BAR_IO) || (bars[i].type == VPCI_BAR_EMPTY) )
+            continue;
+
+        if ( bars[i].type == VPCI_BAR_ROM )
+        {
+            unsigned int rom_reg;
+            uint8_t header_type = pci_conf_read8(pdev->sbdf,
+                                                 PCI_HEADER_TYPE) & 0x7f;
+            if ( header_type == PCI_HEADER_TYPE_NORMAL )
+                rom_reg = PCI_ROM_ADDRESS;
+            else
+                rom_reg = PCI_ROM_ADDRESS1;
+            rc = vpci_add_register(pdev->vpci, vpci_hw_read32, rom_write,
+                                   rom_reg, 4, &bars[i]);
+            if ( rc )
+                return rc;
+        }
+        else
+        {
+            uint8_t reg = PCI_BASE_ADDRESS_0 + i * 4;
+
+            /* This is either VPCI_BAR_MEM32 or VPCI_BAR_MEM64_{LO|HI}. */
+            rc = vpci_add_register(pdev->vpci, vpci_hw_read32, bar_write, reg,
+                                   4, &bars[i]);
+            if ( rc )
+                return rc;
+        }
+    }
+    return 0;
+}
+
 static int init_bars(struct pci_dev *pdev)
 {
     uint16_t cmd;
@@ -470,14 +519,8 @@  static int init_bars(struct pci_dev *pdev)
         return -EOPNOTSUPP;
     }
 
-    /* Setup a handler for the command register. */
-    rc = vpci_add_register(pdev->vpci, vpci_hw_read16, cmd_write, PCI_COMMAND,
-                           2, header);
-    if ( rc )
-        return rc;
-
     if ( pdev->ignore_bars )
-        return 0;
+        return add_bar_handlers(pdev);
 
     /* Disable memory decoding before sizing. */
     cmd = pci_conf_read16(pdev->sbdf, PCI_COMMAND);
@@ -492,14 +535,6 @@  static int init_bars(struct pci_dev *pdev)
         if ( i && bars[i - 1].type == VPCI_BAR_MEM64_LO )
         {
             bars[i].type = VPCI_BAR_MEM64_HI;
-            rc = vpci_add_register(pdev->vpci, vpci_hw_read32, bar_write, reg,
-                                   4, &bars[i]);
-            if ( rc )
-            {
-                pci_conf_write16(pdev->sbdf, PCI_COMMAND, cmd);
-                return rc;
-            }
-
             continue;
         }
 
@@ -532,14 +567,6 @@  static int init_bars(struct pci_dev *pdev)
         bars[i].addr = addr;
         bars[i].size = size;
         bars[i].prefetchable = val & PCI_BASE_ADDRESS_MEM_PREFETCH;
-
-        rc = vpci_add_register(pdev->vpci, vpci_hw_read32, bar_write, reg, 4,
-                               &bars[i]);
-        if ( rc )
-        {
-            pci_conf_write16(pdev->sbdf, PCI_COMMAND, cmd);
-            return rc;
-        }
     }
 
     /* Check expansion ROM. */
@@ -553,11 +580,13 @@  static int init_bars(struct pci_dev *pdev)
         rom->addr = addr;
         header->rom_enabled = pci_conf_read32(pdev->sbdf, rom_reg) &
                               PCI_ROM_ADDRESS_ENABLE;
+    }
 
-        rc = vpci_add_register(pdev->vpci, vpci_hw_read32, rom_write, rom_reg,
-                               4, rom);
-        if ( rc )
-            rom->type = VPCI_BAR_EMPTY;
+    rc = add_bar_handlers(pdev);
+    if ( rc )
+    {
+        pci_conf_write16(pdev->sbdf, PCI_COMMAND, cmd);
+        return rc;
     }
 
     return (cmd & PCI_COMMAND_MEMORY) ? modify_bars(pdev, cmd, false) : 0;