qemu-devel
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: [Qemu-devel] [PATCH 1/1] Add support for PCI Enhanced Allocation "BA


From: Michael S. Tsirkin
Subject: Re: [Qemu-devel] [PATCH 1/1] Add support for PCI Enhanced Allocation "BARs"
Date: Mon, 11 May 2015 21:26:08 +0200

On Mon, May 11, 2015 at 11:56:44AM -0700, Sean O. Stalley wrote:
> PCI Enhanced Allocation is a new method of allocating MMIO & IO
> resources for PCI devices & bridges. It can be used instead
> of the traditional PCI method of using BARs.
> 
> EA entries are hardware-initialized to a fixed address.
> Unlike BARs, regions described by EA are cannot be moved.
> Because of this, only devices which are perminately connected to
> the PCI bus can use EA. A removeable PCI card must not use EA.
> 
> This patchset enables any existing QEMU PCI model to use EA in leiu of
> BARs. It adds EA options to the PCI device paramaters.
> 
> The Enhanced Allocation ECN is publicly available here:
> https://www.pcisig.com/specifications/conventional/ECN_Enhanced_Allocation_23_Oct_2014_Final.pdf
> 
> Signed-off-by: Sean O. Stalley <address@hidden>


I will review this, thanks.  Will you also be sending a seabios patch to
support these registers?  When you do, please Cc me.


> ---
>  hw/pci/Makefile.objs      |   2 +-
>  hw/pci/pci.c              |  96 ++++++++++++++++------
>  hw/pci/pci_ea.c           | 203 
> ++++++++++++++++++++++++++++++++++++++++++++++
>  include/hw/pci/pci.h      |   7 ++
>  include/hw/pci/pci_ea.h   |  39 +++++++++
>  include/hw/pci/pci_regs.h |   4 +
>  include/qemu/typedefs.h   |   1 +
>  7 files changed, 328 insertions(+), 24 deletions(-)
>  create mode 100644 hw/pci/pci_ea.c
>  create mode 100644 include/hw/pci/pci_ea.h
> 
> diff --git a/hw/pci/Makefile.objs b/hw/pci/Makefile.objs
> index 9f905e6..e5d80cf 100644
> --- a/hw/pci/Makefile.objs
> +++ b/hw/pci/Makefile.objs
> @@ -3,7 +3,7 @@ common-obj-$(CONFIG_PCI) += msix.o msi.o
>  common-obj-$(CONFIG_PCI) += shpc.o
>  common-obj-$(CONFIG_PCI) += slotid_cap.o
>  common-obj-$(CONFIG_PCI) += pci_host.o pcie_host.o
> -common-obj-$(CONFIG_PCI) += pcie.o pcie_aer.o pcie_port.o
> +common-obj-$(CONFIG_PCI) += pcie.o pcie_aer.o pcie_port.o pci_ea.o
>  
>  common-obj-$(call lnot,$(CONFIG_PCI)) += pci-stub.o
>  common-obj-$(CONFIG_ALL) += pci-stub.o
> diff --git a/hw/pci/pci.c b/hw/pci/pci.c
> index b3d5100..c7552ca 100644
> --- a/hw/pci/pci.c
> +++ b/hw/pci/pci.c
> @@ -23,6 +23,7 @@
>   */
>  #include "hw/hw.h"
>  #include "hw/pci/pci.h"
> +#include "hw/pci/pci_ea.h"
>  #include "hw/pci/pci_bridge.h"
>  #include "hw/pci/pci_bus.h"
>  #include "hw/pci/pci_host.h"
> @@ -58,6 +59,10 @@ static Property pci_props[] = {
>                      QEMU_PCI_CAP_MULTIFUNCTION_BITNR, false),
>      DEFINE_PROP_BIT("command_serr_enable", PCIDevice, cap_present,
>                      QEMU_PCI_CAP_SERR_BITNR, true),
> +    DEFINE_PROP_BOOL("enhanced_allocation", PCIDevice, enhanced, false),
> +    DEFINE_PROP_ARRAY("ea_addr", PCIDevice, ea_addr_len, ea_addr,
> +                      qdev_prop_uint64, hwaddr),
> +
>      DEFINE_PROP_END_OF_LIST()
>  };
>  
> @@ -882,6 +887,7 @@ static PCIDevice *do_pci_register_device(PCIDevice 
> *pci_dev, PCIBus *bus,
>          config_read = pci_default_read_config;
>      if (!config_write)
>          config_write = pci_default_write_config;
> +
>      pci_dev->config_read = config_read;
>      pci_dev->config_write = config_write;
>      bus->devices[devfn] = pci_dev;
> @@ -929,40 +935,72 @@ void pci_register_bar(PCIDevice *pci_dev, int 
> region_num,
>  
>      assert(region_num >= 0);
>      assert(region_num < PCI_NUM_REGIONS);
> -    if (size & (size-1)) {
> +
> +
> +    /* TODO: these checks should be done earlier */
> +    if (!pci_dev->enhanced && (size & (size-1))) {
>          fprintf(stderr, "ERROR: PCI region size must be pow2 "
>                      "type=0x%x, size=0x%"FMT_PCIBUS"\n", type, size);
>          exit(1);
>      }
>  
> +    if (pci_dev->enhanced) {
> +
> +        if (pci_dev->ea_addr_len <= region_num) {
> +            fprintf(stderr, "ERROR: Address for EA entry %i not given\n",
> +                    region_num);
> +            exit(1);
> +        }
> +
> +        if (pci_dev->ea_addr[region_num] == 0x0) {
> +            fprintf(stderr, "ERROR: Address for EA entry %i not set\n",
> +                    region_num);
> +            exit(1);
> +        }
> +
> +        if (pci_ea_invalid_addr(pci_dev->ea_addr[region_num])) {
> +            fprintf(stderr, "ERROR: Address for EA entry %i not valid\n",
> +                    region_num);
> +            exit(1);
> +        }
> +    }
> +
>      r = &pci_dev->io_regions[region_num];
>      r->addr = PCI_BAR_UNMAPPED;
>      r->size = size;
>      r->type = type;
> -    r->memory = NULL;
> -
> -    wmask = ~(size - 1);
> -    addr = pci_bar(pci_dev, region_num);
> -    if (region_num == PCI_ROM_SLOT) {
> -        /* ROM enable bit is writable */
> -        wmask |= PCI_ROM_ADDRESS_ENABLE;
> -    }
> -    pci_set_long(pci_dev->config + addr, type);
> -    if (!(r->type & PCI_BASE_ADDRESS_SPACE_IO) &&
> -        r->type & PCI_BASE_ADDRESS_MEM_TYPE_64) {
> -        pci_set_quad(pci_dev->wmask + addr, wmask);
> -        pci_set_quad(pci_dev->cmask + addr, ~0ULL);
> -    } else {
> -        pci_set_long(pci_dev->wmask + addr, wmask & 0xffffffff);
> -        pci_set_long(pci_dev->cmask + addr, 0xffffffff);
> +    r->enhanced = pci_dev->enhanced;
> +    r->memory = memory;
> +    r->address_space = type & PCI_BASE_ADDRESS_SPACE_IO ?
> +        pci_dev->bus->address_space_io : pci_dev->bus->address_space_mem;
> +
> +    if (!pci_dev->enhanced) {
> +
> +        wmask = ~(size - 1);
> +        addr = pci_bar(pci_dev, region_num);
> +        if (region_num == PCI_ROM_SLOT) {
> +            /* ROM enable bit is writable */
> +            wmask |= PCI_ROM_ADDRESS_ENABLE;
> +        }
> +        pci_set_long(pci_dev->config + addr, type);
> +        if (!(r->type & PCI_BASE_ADDRESS_SPACE_IO) &&
> +            r->type & PCI_BASE_ADDRESS_MEM_TYPE_64) {
> +            pci_set_quad(pci_dev->wmask + addr, wmask);
> +            pci_set_quad(pci_dev->cmask + addr, ~0ULL);
> +        } else {
> +            pci_set_long(pci_dev->wmask + addr, wmask & 0xffffffff);
> +            pci_set_long(pci_dev->cmask + addr, 0xffffffff);
> +        }
> +    } else { /* enhanced */
> +
> +        /* add the memory space now */
> +        r->addr = pci_dev->ea_addr[region_num];
> +        memory_region_add_subregion(r->address_space, r->addr, r->memory);
>      }
> -    pci_dev->io_regions[region_num].memory = memory;
> -    pci_dev->io_regions[region_num].address_space
> -        = type & PCI_BASE_ADDRESS_SPACE_IO
> -        ? pci_dev->bus->address_space_io
> -        : pci_dev->bus->address_space_mem;
>  }
>  
> +
> +
>  static void pci_update_vga(PCIDevice *pci_dev)
>  {
>      uint16_t cmd;
> @@ -1107,6 +1145,11 @@ static void pci_update_mappings(PCIDevice *d)
>  
>          new_addr = pci_bar_address(d, i, r->type, r->size);
>  
> +        /* EA BARs cannot be moved */
> +        if (r->enhanced) {
> +            continue;
> +        }
> +
>          /* This bar isn't changed */
>          if (new_addr == r->addr)
>              continue;
> @@ -1785,8 +1828,9 @@ static void pci_qdev_realize(DeviceState *qdev, Error 
> **errp)
>      pci_dev = do_pci_register_device(pci_dev, bus,
>                                       object_get_typename(OBJECT(qdev)),
>                                       pci_dev->devfn, errp);
> -    if (pci_dev == NULL)
> +    if (pci_dev == NULL) {
>          return;
> +    }
>  
>      if (pc->realize) {
>          pc->realize(pci_dev, &local_err);
> @@ -1797,6 +1841,12 @@ static void pci_qdev_realize(DeviceState *qdev, Error 
> **errp)
>          }
>      }
>  
> +    /* write the EA entry */
> +    if (pci_dev->enhanced) {
> +        pci_ea_cap_init(pci_dev);
> +    }
> +
> +
>      /* rom loading */
>      is_default_rom = false;
>      if (pci_dev->romfile == NULL && pc->romfile != NULL) {
> diff --git a/hw/pci/pci_ea.c b/hw/pci/pci_ea.c
> new file mode 100644
> index 0000000..a052519
> --- /dev/null
> +++ b/hw/pci/pci_ea.c
> @@ -0,0 +1,203 @@
> +/*
> + * PCI Enhanced Allocation (EA) Helper functions
> + * Copyright (c) 2015, Intel Corporation.
> + *
> + * Written by Sean O. Stalley <address@hidden>
> + *
> + * This program is free software; you can redistribute it and/or modify it
> + * under the terms and conditions of the GNU General Public License,
> + * version 2, as published by the Free Software Foundation.
> + *
> + * This program is distributed in the hope it will be useful, but WITHOUT
> + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
> + * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
> + * more details.
> + */
> +
> +/*
> + * Contians a lot of code taken from pcie.c
> + */
> +
> +#include "qemu-common.h"
> +#include "hw/pci/pci.h"
> +#include "hw/pci/pci_ea.h"
> +
> +bool pci_ea_invalid_addr(uint64_t addr)
> +{
> +    return (addr != (addr & PCI_EA_ADDR_MASK_64));
> +}
> +
> +/* determines if a 64 bit address is necessary for this address */
> +static bool pci_ea_addr_is_64(uint64_t addr)
> +{
> +    return (addr != (uint32_t)addr);
> +}
> +
> +/* Sets the first 32 bits of the Base or MaxOffset field
> + * Returns the length of the address set
> + */
> +static uint8_t pci_ea_set_addr(PCIDevice *dev, uint8_t pos, uint64_t val)
> +{
> +    uint8_t *current_reg = dev->config + pos;
> +
> +    val = val & PCI_EA_ADDR_MASK_64;
> +
> +    if (pci_ea_addr_is_64(val)) {
> +        val |= PCI_EA_FIELD_SIZE_FLAG;
> +    }
> +
> +    pci_set_long(current_reg, val);
> +    return sizeof(int32_t);
> +}
> +
> +/* Determines EA entry length for this IORegion in bytes
> + * (including the first DWORD) */
> +static int pci_ea_entry_length(PCIIORegion *r)
> +{
> +
> +    int len = 3 * sizeof(int32_t); /* First 32 bits of Base & Offset */
> +
> +    if (pci_ea_addr_is_64(r->addr)) {
> +        len += sizeof(int32_t); /* Base (if 64 bit) */
> +    }
> +
> +    if (pci_ea_addr_is_64(r->size - 1)) {
> +        len += sizeof(int32_t); /* Offset (if 64 bit) */
> +    }
> +
> +    return len;
> +
> +}
> +
> +/* determines the value to be set in the Entry Size field */
> +static int pci_ea_get_es(PCIIORegion *r)
> +{
> +
> +    int es = (pci_ea_entry_length(r) / sizeof(int32_t)) - 1;
> +
> +    assert(PCI_EA_MAX_ES >= es);
> +
> +    return es;
> +}
> +
> +/* Initialize an EA entry for the given PCIIORegion
> + *
> + * @dev: The PCI Device
> + * @bei: The BAR Equivalent Indicator
> + * @pos: The offset of this entry in PCI Configspace;
> + */
> +
> +static int pci_ea_fill_entry(PCIDevice *dev, int bei, int pos)
> +{
> +    PCIIORegion *r = &dev->io_regions[bei];
> +    uint32_t dw0 = 0; /* used for setting first DWORD */
> +    int len = 0;
> +
> +    assert(bei <= PCI_EA_MAX_BEI);
> +
> +    /* Check that the base and size are DWORD aligned */
> +    assert(!pci_ea_invalid_addr(r->addr));
> +    assert(!pci_ea_invalid_addr(r->size));
> +
> +    dw0 |= pci_ea_get_es(r);
> +    dw0 |= (bei << PCI_EA_BEI_OFFSET);
> +    dw0 |= (PCI_EA_ENABLE);
> +
> +    /* base Primary Properties off of type field */
> +    if (r->type & PCI_BASE_ADDRESS_SPACE_IO) {
> +        dw0 |= (PCI_EA_IO << PCI_EA_PP_OFFSET);
> +
> +    } else if (r->type & PCI_BASE_ADDRESS_MEM_PREFETCH) {
> +        dw0 |= (PCI_EA_MEM_PREFETCH << PCI_EA_PP_OFFSET);
> +
> +    } else {
> +        dw0 |= (PCI_EA_MEM << PCI_EA_PP_OFFSET);
> +    }
> +
> +    /* Secondary Properties should be ignored */
> +    dw0 |= (PCI_EA_UNAVAL << PCI_EA_SP_OFFSET);
> +
> +    pci_set_long(dev->config + pos, dw0);
> +
> +    len += sizeof(int32_t);
> +
> +    len += pci_ea_set_addr(dev, pos + len, r->addr);
> +    len += pci_ea_set_addr(dev, pos + len, r->size - 1);
> +
> +    /* Base (if 64 bit) */
> +    if (pci_ea_addr_is_64(r->addr)) {
> +        pci_set_long(dev->config + pos + len, (r->addr >> 32));
> +        len += sizeof(int32_t);
> +    }
> +
> +    /* Offset (if 64 bit) */
> +    if (pci_ea_addr_is_64(r->size - 4)) {
> +        pci_set_long(dev->config + pos + len, (r->size >> 32));
> +        len += sizeof(int32_t);
> +    }
> +
> +    assert(len == pci_ea_entry_length(r));
> +
> +    return len;
> +
> +}
> +
> +/*
> + * Initialize the EA capability descriptor
> + * must be done after the EA memory regions are initialized
> + * (or else the EA entries won't get written)
> + */
> +int pci_ea_cap_init(PCIDevice *dev)
> +{
> +    int pos;
> +    int ret;
> +    int i;
> +    int num_entries = 0;
> +    uint8_t length; /* length of this cap entry */
> +    PCIIORegion *r;
> +
> +    /* Determine the length of the capability entry */
> +
> +    length = sizeof(int32_t); /* DWORD 0: Cap ID, Next Pointer, Num Entries 
> */
> +    /* TODO: DWORD 1 for Type 1 Functions */
> +
> +    /* add the length of every entry */
> +    for (i = 0; i < PCI_NUM_REGIONS; ++i) {
> +        r = &dev->io_regions[i];
> +
> +        if (r->enhanced) {
> +            num_entries++;
> +            length += pci_ea_entry_length(r);
> +        }
> +    }
> +
> +    /* add the EA CAP (sets DWORD 0) */
> +    pos = pci_add_capability(dev, PCI_CAP_ID_EA, 0, length);
> +    if (0 > pos) {
> +        return pos;
> +    }
> +
> +    /* DWORD 0: Cap ID, Next Pointer, Num Entries */
> +    assert(num_entries <= PCI_EA_MAX_NUM_ENTRIES); /* overflow */
> +    pci_set_byte(dev->config + pos + PCI_EA_NUM_ENTRIES_OFFSET, num_entries);
> +    pos += sizeof(int32_t);
> +
> +    for (i = 0; i < PCI_NUM_REGIONS; ++i) {
> +        r = &dev->io_regions[i];
> +
> +        if (r->enhanced) {
> +            ret = pci_ea_fill_entry(dev, i, pos);
> +
> +            if (0 > ret) {
> +                return ret;
> +            }
> +            pos += ret;
> +        }
> +
> +    }
> +
> +    assert(pos == pci_find_capability(dev, PCI_CAP_ID_EA) + length);
> +
> +    return pos;
> +
> +}
> diff --git a/include/hw/pci/pci.h b/include/hw/pci/pci.h
> index d4ffead..da64435 100644
> --- a/include/hw/pci/pci.h
> +++ b/include/hw/pci/pci.h
> @@ -12,6 +12,7 @@
>  #include "hw/isa/isa.h"
>  
>  #include "hw/pci/pcie.h"
> +#include "hw/pci/pci_ea.h"
>  
>  /* PCI bus */
>  
> @@ -109,6 +110,7 @@ typedef struct PCIIORegion {
>      uint8_t type;
>      MemoryRegion *memory;
>      MemoryRegion *address_space;
> +    bool          enhanced;
>  } PCIIORegion;
>  
>  #define PCI_ROM_SLOT 6
> @@ -288,6 +290,11 @@ struct PCIDevice {
>      /* PCI Express */
>      PCIExpressDevice exp;
>  
> +    /* Enhanced Allocation */
> +    bool enhanced;
> +    uint32_t ea_addr_len;
> +    hwaddr *ea_addr;
> +
>      /* SHPC */
>      SHPCDevice *shpc;
>  
> diff --git a/include/hw/pci/pci_ea.h b/include/hw/pci/pci_ea.h
> new file mode 100644
> index 0000000..2b63012
> --- /dev/null
> +++ b/include/hw/pci/pci_ea.h
> @@ -0,0 +1,39 @@
> +/* Constants for pci enhanced allocation (EA) capability descriptor */
> +
> +#ifndef QEMU_PCI_EA_H
> +#define QEMU_PCI_EA_H
> +
> +#define PCI_EA_FIELD_SIZE_FLAG 0x00000002
> +
> +/* the address' must be DWORD aligned */
> +#define PCI_EA_ADDR_MASK_32 0xfffffffc
> +#define PCI_EA_ADDR_MASK_64 0xfffffffffffffffc
> +
> +#define PCI_EA_MAX_NUM_ENTRIES 0x3f
> +
> +#define PCI_EA_BEI_OFFSET 4 /* In bits from beginning of EA entry */
> +#define PCI_EA_ENABLE 0x80000000
> +#define PCI_EA_MAX_BEI 0x8
> +#define PCI_EA_MAX_ES  0x4
> +
> +/* Primary & Secondary Properties, per table 6-1 */
> +#define PCI_EA_PP_OFFSET 8
> +#define PCI_EA_SP_OFFSET 16
> +
> +/* Primary & Secondary Properties Fields, per table 6-1 */
> +#define PCI_EA_MEM               0x00 /* Non-Prefetchable */
> +#define PCI_EA_MEM_PREFETCH      0x01
> +#define PCI_EA_IO                0x02
> +#define PCI_EA_VIRT_MEM_PREFETCH 0x03
> +#define PCI_EA_VIRT_MEM          0x04 /* Non-Prefetchable */
> +#define PCI_EA_UNAVAL_MEM        0xFD
> +#define PCI_EA_UNAVAL_IO         0xFE
> +#define PCI_EA_UNAVAL            0xFF
> +
> +/* checks to see if ea_address is invalid */
> +bool pci_ea_invalid_addr(uint64_t addr);
> +
> +/* Initialize the ea capability descriptor */
> +int pci_ea_cap_init(PCIDevice *dev);
> +
> +#endif /* QEMU_PCI_EA_REG_H */
> diff --git a/include/hw/pci/pci_regs.h b/include/hw/pci/pci_regs.h
> index 56a404b..746ba1a 100644
> --- a/include/hw/pci/pci_regs.h
> +++ b/include/hw/pci/pci_regs.h
> @@ -213,6 +213,7 @@
>  #define  PCI_CAP_ID_MSIX     0x11    /* MSI-X */
>  #define  PCI_CAP_ID_SATA     0x12    /* Serial ATA */
>  #define  PCI_CAP_ID_AF               0x13    /* PCI Advanced Features */
> +#define  PCI_CAP_ID_EA               0x14    /* PCI Enhanced Allocation */
>  #define PCI_CAP_LIST_NEXT    1       /* Next capability in the list */
>  #define PCI_CAP_FLAGS                2       /* Capability defined flags (16 
> bits) */
>  #define PCI_CAP_SIZEOF               4
> @@ -340,6 +341,9 @@
>  #define PCI_AF_STATUS                5
>  #define  PCI_AF_STATUS_TP    0x01
>  
> +/* PCI Enhanced Allocation registers */
> +#define PCI_EA_NUM_ENTRIES_OFFSET 2
> +
>  /* PCI-X registers */
>  
>  #define PCI_X_CMD            2       /* Modes & Features */
> diff --git a/include/qemu/typedefs.h b/include/qemu/typedefs.h
> index cde3314..70a9f3f 100644
> --- a/include/qemu/typedefs.h
> +++ b/include/qemu/typedefs.h
> @@ -48,6 +48,7 @@ typedef struct PcGuestInfo PcGuestInfo;
>  typedef struct PCIBridge PCIBridge;
>  typedef struct PCIBus PCIBus;
>  typedef struct PCIDevice PCIDevice;
> +typedef struct PCIEADevice PCIEADevice;
>  typedef struct PCIEAERErr PCIEAERErr;
>  typedef struct PCIEAERLog PCIEAERLog;
>  typedef struct PCIEAERMsg PCIEAERMsg;
> -- 
> 1.9.1



reply via email to

[Prev in Thread] Current Thread [Next in Thread]