|
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index] Re: [PATCH v6 06/13] vpci/header: implement guest BAR register handlers
On 08.02.22 11:25, Roger Pau Monné wrote:
> On Fri, Feb 04, 2022 at 08:34:52AM +0200, Oleksandr Andrushchenko wrote:
>> From: Oleksandr Andrushchenko <oleksandr_andrushchenko@xxxxxxxx>
>>
>> Add relevant vpci register handlers when assigning PCI device to a domain
>> and remove those when de-assigning. This allows having different
>> handlers for different domains, e.g. hwdom and other guests.
>>
>> Emulate guest BAR register values: this allows creating a guest view
>> of the registers and emulates size and properties probe as it is done
>> during PCI device enumeration by the guest.
>>
>> All empty, IO and ROM BARs for guests are emulated by returning 0 on
>> reads and ignoring writes: this BARs are special with this respect as
>> their lower bits have special meaning, so returning default ~0 on read
>> may confuse guest OS.
>>
>> Memory decoding is initially disabled when used by guests in order to
>> prevent the BAR being placed on top of a RAM region.
>>
>> Signed-off-by: Oleksandr Andrushchenko <oleksandr_andrushchenko@xxxxxxxx>
>> ---
>> Since v5:
>> - make sure that the guest set address has the same page offset
>> as the physical address on the host
>> - remove guest_rom_{read|write} as those just implement the default
>> behaviour of the registers not being handled
>> - adjusted comment for struct vpci.addr field
>> - add guest handlers for BARs which are not handled and will otherwise
>> return ~0 on read and ignore writes. The BARs are special with this
>> respect as their lower bits have special meaning, so returning ~0
>> doesn't seem to be right
>> Since v4:
>> - updated commit message
>> - s/guest_addr/guest_reg
>> Since v3:
>> - squashed two patches: dynamic add/remove handlers and guest BAR
>> handler implementation
>> - fix guest BAR read of the high part of a 64bit BAR (Roger)
>> - add error handling to vpci_assign_device
>> - s/dom%pd/%pd
>> - blank line before return
>> Since v2:
>> - remove unneeded ifdefs for CONFIG_HAS_VPCI_GUEST_SUPPORT as more code
>> has been eliminated from being built on x86
>> Since v1:
>> - constify struct pci_dev where possible
>> - do not open code is_system_domain()
>> - simplify some code3. simplify
>> - use gdprintk + error code instead of gprintk
>> - gate vpci_bar_{add|remove}_handlers with CONFIG_HAS_VPCI_GUEST_SUPPORT,
>> so these do not get compiled for x86
>> - removed unneeded is_system_domain check
>> - re-work guest read/write to be much simpler and do more work on write
>> than read which is expected to be called more frequently
>> - removed one too obvious comment
>> ---
>> xen/drivers/vpci/header.c | 131 +++++++++++++++++++++++++++++++++-----
>> xen/include/xen/vpci.h | 3 +
>> 2 files changed, 118 insertions(+), 16 deletions(-)
>>
>> diff --git a/xen/drivers/vpci/header.c b/xen/drivers/vpci/header.c
>> index bd23c0274d48..2620a95ff35b 100644
>> --- a/xen/drivers/vpci/header.c
>> +++ b/xen/drivers/vpci/header.c
>> @@ -406,6 +406,81 @@ static void bar_write(const struct pci_dev *pdev,
>> unsigned int reg,
>> pci_conf_write32(pdev->sbdf, reg, val);
>> }
>>
>> +static void guest_bar_write(const struct pci_dev *pdev, unsigned int reg,
>> + uint32_t val, void *data)
>> +{
>> + struct vpci_bar *bar = data;
>> + bool hi = false;
>> + uint64_t guest_reg = bar->guest_reg;
>> +
>> + if ( bar->type == VPCI_BAR_MEM64_HI )
>> + {
>> + ASSERT(reg > PCI_BASE_ADDRESS_0);
>> + bar--;
>> + hi = true;
>> + }
>> + else
>> + {
>> + val &= PCI_BASE_ADDRESS_MEM_MASK;
>> + val |= bar->type == VPCI_BAR_MEM32 ? PCI_BASE_ADDRESS_MEM_TYPE_32
>> + : PCI_BASE_ADDRESS_MEM_TYPE_64;
>> + val |= bar->prefetchable ? PCI_BASE_ADDRESS_MEM_PREFETCH : 0;
>> + }
>> +
>> + guest_reg &= ~(0xffffffffull << (hi ? 32 : 0));
>> + guest_reg |= (uint64_t)val << (hi ? 32 : 0);
>> +
>> + guest_reg &= ~(bar->size - 1) | ~PCI_BASE_ADDRESS_MEM_MASK;
>> +
>> + /*
>> + * Make sure that the guest set address has the same page offset
>> + * as the physical address on the host or otherwise things won't work as
>> + * expected.
>> + */
>> + if ( (guest_reg & (~PAGE_MASK & PCI_BASE_ADDRESS_MEM_MASK)) !=
>> + (bar->addr & ~PAGE_MASK) )
> This is only required when !hi, but I'm fine with doing it
> unconditionally as it's clearer.
This is correct wrt hi
>
>> + {
>> + gprintk(XENLOG_WARNING,
>> + "%pp: ignored BAR %zu write with wrong page offset\n",
> "%pp: ignored BAR %zu write attempting to change page offset\n"
Ok
>
>> + &pdev->sbdf, bar - pdev->vpci->header.bars + hi);
>> + return;
>> + }
>> +
>> + bar->guest_reg = guest_reg;
>> +}
>> +
>> +static uint32_t guest_bar_read(const struct pci_dev *pdev, unsigned int reg,
>> + void *data)
>> +{
>> + const struct vpci_bar *bar = data;
>> + bool hi = false;
>> +
>> + if ( bar->type == VPCI_BAR_MEM64_HI )
>> + {
>> + ASSERT(reg > PCI_BASE_ADDRESS_0);
>> + bar--;
>> + hi = true;
>> + }
>> +
>> + return bar->guest_reg >> (hi ? 32 : 0);
>> +}
>> +
>> +static uint32_t guest_bar_ignore_read(const struct pci_dev *pdev,
>> + unsigned int reg, void *data)
>> +{
>> + return 0;
>> +}
>> +
>> +static int bar_ignore_access(const struct pci_dev *pdev, unsigned int reg,
>> + struct vpci_bar *bar)
>> +{
>> + if ( is_hardware_domain(pdev->domain) )
>> + return 0;
>> +
>> + return vpci_add_register(pdev->vpci, guest_bar_ignore_read, NULL,
>> + reg, 4, bar);
>> +}
>> +
>> static void rom_write(const struct pci_dev *pdev, unsigned int reg,
>> uint32_t val, void *data)
>> {
>> @@ -462,6 +537,7 @@ static int init_bars(struct pci_dev *pdev)
>> struct vpci_header *header = &pdev->vpci->header;
>> struct vpci_bar *bars = header->bars;
>> int rc;
>> + bool is_hwdom = is_hardware_domain(pdev->domain);
>>
>> switch ( pci_conf_read8(pdev->sbdf, PCI_HEADER_TYPE) & 0x7f )
>> {
>> @@ -501,8 +577,10 @@ 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]);
>> + rc = vpci_add_register(pdev->vpci,
>> + is_hwdom ? vpci_hw_read32 :
>> guest_bar_read,
>> + is_hwdom ? bar_write : guest_bar_write,
>> + reg, 4, &bars[i]);
>> if ( rc )
>> {
>> pci_conf_write16(pdev->sbdf, PCI_COMMAND, cmd);
>> @@ -516,6 +594,11 @@ static int init_bars(struct pci_dev *pdev)
>> if ( (val & PCI_BASE_ADDRESS_SPACE) == PCI_BASE_ADDRESS_SPACE_IO )
>> {
>> bars[i].type = VPCI_BAR_IO;
>> +
>> + rc = bar_ignore_access(pdev, reg, &bars[i]);
> This is wrong: you only want to ignore access to IO BARs for Arm, for
> x86 we should keep the previous behavior. Even more if you go with
> Jan's suggestions to make bar_ignore_access also applicable to dom0.
How do we want this?
#ifdef CONFIG_ARM?
>
>> + if ( rc )
>> + return rc;
>> +
>> continue;
>> }
>> if ( (val & PCI_BASE_ADDRESS_MEM_TYPE_MASK) ==
>> @@ -535,6 +618,11 @@ static int init_bars(struct pci_dev *pdev)
>> if ( size == 0 )
>> {
>> bars[i].type = VPCI_BAR_EMPTY;
>> +
>> + rc = bar_ignore_access(pdev, reg, &bars[i]);
>> + if ( rc )
>> + return rc;
> I would be fine to just call vpci_add_register here, ie;
>
> if ( !is_hwdom )
> {
> rc = vpci_add_register(pdev->vpci, guest_bar_ignore_read, NULL,
> reg, 4, &bars[i]);
> if ( rc )
> {
> ...
> }
> }
But we have 3 places where we do the same and also handle errors
the same way. I was thinking having a helper will make the code
clearer. Do you want to open code all the uses?
> Feel free to unify the writing of the PCI_COMMAND register on the
> error path into a label, as then the error case would simply be a
> `goto error;`
I was thinking about it. Will it be ok to make this change in this patch
or you want a dedicated one for that?
> Thanks, Roger.
Thank you,
Oleksandr
|
![]() |
Lists.xenproject.org is hosted with RackSpace, monitoring our |