diff mbox

[6/6] pc: limit 64 bit hole to 2G by default

Message ID 1374996553-21828-7-git-send-email-imammedo@redhat.com
State New
Headers show

Commit Message

Igor Mammedov July 28, 2013, 7:29 a.m. UTC
It turns out that some 32 bit windows guests crash
if 64 bit PCI hole size is >2G.
Limit it to 2G for piix and q35 by default.
User may override default boundaries by using
"pci_hole64_end " property.

Examples:
-global i440FX-pcihost.pci_hole64_end=0xffffffffffffffff

-global q35-pcihost.pci_hole64_end=0xffffffffffffffff

Reported-by: Igor Mammedov <imammedo@redhat.com>,
Signed-off-by: Michael S. Tsirkin <mst@redhat.com>
Signed-off-by: Igor Mammedov <imammedo@redhat.com>
---
 hw/i386/pc.c              | 59 +++++++++++++++++++++++++++++------------------
 hw/i386/pc_piix.c         | 14 +----------
 hw/pci-host/piix.c        | 37 +++++++++++++++++++++++++----
 hw/pci-host/q35.c         | 32 +++++++++++++++----------
 include/hw/i386/pc.h      |  5 ++--
 include/hw/pci-host/q35.h |  1 +
 6 files changed, 94 insertions(+), 54 deletions(-)

Comments

Michael S. Tsirkin July 28, 2013, 7:57 a.m. UTC | #1
On Sun, Jul 28, 2013 at 09:29:13AM +0200, Igor Mammedov wrote:
> It turns out that some 32 bit windows guests crash
> if 64 bit PCI hole size is >2G.
> Limit it to 2G for piix and q35 by default.
> User may override default boundaries by using
> "pci_hole64_end " property.
> 
> Examples:
> -global i440FX-pcihost.pci_hole64_end=0xffffffffffffffff
> 
> -global q35-pcihost.pci_hole64_end=0xffffffffffffffff

IMO that's pretty bad as user interfaces go.
In particular if you are not careful you can make
end < start.
Why not set the size, and include a patch that
let people write "1G" or "2G" for convenience?

> Reported-by: Igor Mammedov <imammedo@redhat.com>,
> Signed-off-by: Michael S. Tsirkin <mst@redhat.com>
> Signed-off-by: Igor Mammedov <imammedo@redhat.com>
> ---
>  hw/i386/pc.c              | 59 +++++++++++++++++++++++++++++------------------
>  hw/i386/pc_piix.c         | 14 +----------
>  hw/pci-host/piix.c        | 37 +++++++++++++++++++++++++----
>  hw/pci-host/q35.c         | 32 +++++++++++++++----------
>  include/hw/i386/pc.h      |  5 ++--
>  include/hw/pci-host/q35.h |  1 +
>  6 files changed, 94 insertions(+), 54 deletions(-)
> 
> diff --git a/hw/i386/pc.c b/hw/i386/pc.c
> index b0b98a8..acaeb6c 100644
> --- a/hw/i386/pc.c
> +++ b/hw/i386/pc.c
> @@ -1003,15 +1003,28 @@ typedef struct PcRomPciInfo {
>  static void pc_fw_cfg_guest_info(PcGuestInfo *guest_info)
>  {
>      PcRomPciInfo *info;
> +    Object *pci_info;
> +
>      if (!guest_info->has_pci_info || !guest_info->fw_cfg) {
>          return;
>      }
> +    pci_info = object_resolve_path("/machine/i440fx", NULL);
> +    if (!pci_info) {
> +        pci_info = object_resolve_path("/machine/q35", NULL);
> +        if (!pci_info) {
> +            return;
> +        }
> +    }


So is the path /machine/i440fx? /machine/i440FX?
/machine/i440FX-pcihost?
There's no way to check this code is right without
actually running it.

How about i44fx_get_pci_info so we can have this
knowledge of paths localized in specific chipset code?


>  
>      info = g_malloc(sizeof *info);
> -    info->w32_min = cpu_to_le64(guest_info->pci_info.w32.begin);
> -    info->w32_max = cpu_to_le64(guest_info->pci_info.w32.end);
> -    info->w64_min = cpu_to_le64(guest_info->pci_info.w64.begin);
> -    info->w64_max = cpu_to_le64(guest_info->pci_info.w64.end);
> +    info->w32_min = cpu_to_le64(object_property_get_int(pci_info,
> +                                "pci_hole_start", NULL));
> +    info->w32_max = cpu_to_le64(object_property_get_int(pci_info,
> +                                "pci_hole_end", NULL));

Looks wrong.
object_property_get_int returns a signed int64.
w32 is unsigned.
Happens to work but I think we need an explicit API.

Property names are hard-coded string literals.
Please add macros to set and get them
so we can avoid duplicating code.
E.g.

#define PCI_HOST_PROPS...
static inline get_



> +    info->w64_min = cpu_to_le64(object_property_get_int(pci_info,
> +                                "pci_hole64_start", NULL));
> +    info->w64_max = cpu_to_le64(object_property_get_int(pci_info,
> +                                "pci_hole64_end", NULL));
>      /* Pass PCI hole info to guest via a side channel.
>       * Required so guest PCI enumeration does the right thing. */
>      fw_cfg_add_file(guest_info->fw_cfg, "etc/pci-info", info, sizeof *info);
> @@ -1037,29 +1050,31 @@ PcGuestInfo *pc_guest_info_init(ram_addr_t below_4g_mem_size,
>      PcGuestInfoState *guest_info_state = g_malloc0(sizeof *guest_info_state);
>      PcGuestInfo *guest_info = &guest_info_state->info;
>  
> -    guest_info->pci_info.w32.end = IO_APIC_DEFAULT_ADDRESS;
> -    if (sizeof(hwaddr) == 4) {
> -        guest_info->pci_info.w64.begin = 0;
> -        guest_info->pci_info.w64.end = 0;
> -    } else {
> -        /*
> -         * BIOS does not set MTRR entries for the 64 bit window, so no need to
> -         * align address to power of two.  Align address at 1G, this makes sure
> -         * it can be exactly covered with a PAT entry even when using huge
> -         * pages.
> -         */
> -        guest_info->pci_info.w64.begin =
> -            ROUND_UP((0x1ULL << 32) + above_4g_mem_size, 0x1ULL << 30);
> -        guest_info->pci_info.w64.end = guest_info->pci_info.w64.begin +
> -            (0x1ULL << 62);
> -        assert(guest_info->pci_info.w64.begin <= guest_info->pci_info.w64.end);
> -    }
> -
>      guest_info_state->machine_done.notify = pc_guest_info_machine_done;
>      qemu_add_machine_init_done_notifier(&guest_info_state->machine_done);
>      return guest_info;
>  }
>  
> +void pc_init_pci64_hole(PcPciInfo *pci_info, uint64_t pci_hole64_start)
> +{
> +    if (sizeof(hwaddr) == 4) {
> +        memset(&pci_info->w64, sizeof(pci_info->w64), 0);
> +        return;
> +    }
> +    /*
> +     * BIOS does not set MTRR entries for the 64 bit window, so no need to
> +     * align address to power of two.  Align address at 1G, this makes sure
> +     * it can be exactly covered with a PAT entry even when using huge
> +     * pages.
> +     */
> +    pci_info->w64.begin = ROUND_UP(pci_hole64_start, 0x1ULL << 30);
> +    if (!pci_info->w64.end) {
> +        /* set default end if it wasn't specified, + 2 Gb past start */
> +        pci_info->w64.end = pci_info->w64.begin + (0x1ULL << 31);
> +    }
> +    assert(pci_info->w64.begin < pci_info->w64.end);
> +}
> +
>  void pc_acpi_init(const char *default_dsdt)
>  {
>      char *filename;
> diff --git a/hw/i386/pc_piix.c b/hw/i386/pc_piix.c
> index b58c255..ab25458 100644
> --- a/hw/i386/pc_piix.c
> +++ b/hw/i386/pc_piix.c
> @@ -129,15 +129,6 @@ static void pc_init1(MemoryRegion *system_memory,
>      guest_info = pc_guest_info_init(below_4g_mem_size, above_4g_mem_size);
>      guest_info->has_pci_info = has_pci_info;
>  
> -    /* Set PCI window size the way seabios has always done it. */
> -    /* Power of 2 so bios can cover it with a single MTRR */
> -    if (ram_size <= 0x80000000)
> -        guest_info->pci_info.w32.begin = 0x80000000;
> -    else if (ram_size <= 0xc0000000)
> -        guest_info->pci_info.w32.begin = 0xc0000000;
> -    else
> -        guest_info->pci_info.w32.begin = 0xe0000000;
> -
>      /* allocate ram and load rom/bios */
>      if (!xen_enabled()) {
>          fw_cfg = pc_memory_init(system_memory,
> @@ -160,10 +151,7 @@ static void pc_init1(MemoryRegion *system_memory,
>                                system_memory, system_io, ram_size,
>                                below_4g_mem_size,
>                                0x100000000ULL - below_4g_mem_size,
> -                              0x100000000ULL + above_4g_mem_size,
> -                              (sizeof(hwaddr) == 4
> -                               ? 0
> -                               : ((uint64_t)1 << 62)),
> +                              above_4g_mem_size,
>                                pci_memory, ram_memory);
>      } else {
>          pci_bus = NULL;
> diff --git a/hw/pci-host/piix.c b/hw/pci-host/piix.c
> index 4624d04..59a42c5 100644
> --- a/hw/pci-host/piix.c
> +++ b/hw/pci-host/piix.c
> @@ -32,6 +32,7 @@
>  #include "hw/xen/xen.h"
>  #include "hw/pci-host/pam.h"
>  #include "sysemu/sysemu.h"
> +#include "hw/i386/ioapic.h"
>  
>  /*
>   * I440FX chipset data sheet.
> @@ -44,6 +45,7 @@
>  
>  typedef struct I440FXState {
>      PCIHostState parent_obj;
> +    PcPciInfo pci_info;
>  } I440FXState;
>  
>  #define PIIX_NUM_PIC_IRQS       16      /* i8259 * 2 */
> @@ -247,8 +249,7 @@ PCIBus *i440fx_init(PCII440FXState **pi440fx_state,
>                      ram_addr_t ram_size,
>                      hwaddr pci_hole_start,
>                      hwaddr pci_hole_size,
> -                    hwaddr pci_hole64_start,
> -                    hwaddr pci_hole64_size,
> +                    ram_addr_t above_4g_mem_size,
>                      MemoryRegion *pci_address_space,
>                      MemoryRegion *ram_memory)
>  {
> @@ -259,6 +260,8 @@ PCIBus *i440fx_init(PCII440FXState **pi440fx_state,
>      PIIX3State *piix3;
>      PCII440FXState *f;
>      unsigned i;
> +    I440FXState *i440fx;
> +    uint64_t pci_hole64_size;
>  
>      dev = qdev_create(NULL, TYPE_I440FX_PCI_HOST);
>      s = PCI_HOST_BRIDGE(dev);
> @@ -274,14 +277,30 @@ PCIBus *i440fx_init(PCII440FXState **pi440fx_state,
>      f->system_memory = address_space_mem;
>      f->pci_address_space = pci_address_space;
>      f->ram_memory = ram_memory;
> +
> +    i440fx = I440FX_PCI_HOST(dev);
> +    /* Set PCI window size the way seabios has always done it. */
> +    /* Power of 2 so bios can cover it with a single MTRR */
> +    if (ram_size <= 0x80000000) {
> +        i440fx->pci_info.w32.begin = 0x80000000;
> +    } else if (ram_size <= 0xc0000000) {
> +        i440fx->pci_info.w32.begin = 0xc0000000;
> +    } else {
> +        i440fx->pci_info.w32.begin = 0xe0000000;
> +    }
> +
>      memory_region_init_alias(&f->pci_hole, OBJECT(d), "pci-hole", f->pci_address_space,
>                               pci_hole_start, pci_hole_size);
>      memory_region_add_subregion(f->system_memory, pci_hole_start, &f->pci_hole);
> +
> +    pc_init_pci64_hole(&i440fx->pci_info, 0x100000000ULL + above_4g_mem_size);
> +    pci_hole64_size = range_size(i440fx->pci_info.w64);
>      memory_region_init_alias(&f->pci_hole_64bit, OBJECT(d), "pci-hole64",
>                               f->pci_address_space,
> -                             pci_hole64_start, pci_hole64_size);
> +                             i440fx->pci_info.w64.begin, pci_hole64_size);
>      if (pci_hole64_size) {
> -        memory_region_add_subregion(f->system_memory, pci_hole64_start,
> +        memory_region_add_subregion(f->system_memory,
> +                                    i440fx->pci_info.w64.begin,
>                                      &f->pci_hole_64bit);
>      }
>      memory_region_init_alias(&f->smram_region, OBJECT(d), "smram-region",
> @@ -629,6 +648,15 @@ static const char *i440fx_pcihost_root_bus_path(PCIHostState *host_bridge,
>      return "0000";
>  }
>  
> +static Property i440fx_props[] = {
> +    DEFINE_PROP_UINT64("pci_hole64_start", I440FXState, pci_info.w64.begin, 0),
> +    DEFINE_PROP_UINT64("pci_hole64_end", I440FXState, pci_info.w64.end, 0),
> +    DEFINE_PROP_UINT64("pci_hole_start", I440FXState, pci_info.w32.begin, 0),
> +    DEFINE_PROP_UINT64("pci_hole_end", I440FXState, pci_info.w32.end,
> +                       IO_APIC_DEFAULT_ADDRESS),
> +    DEFINE_PROP_END_OF_LIST(),
> +};
> +

So we have 4 properties. One of them pci_hole64_end
is supposed to be set to a value.
Others should not be touched under any circuimstances.
Of course if you query properties you have no way
to know which is which and what are the legal values.
Ouch.

>  static void i440fx_pcihost_class_init(ObjectClass *klass, void *data)
>  {
>      DeviceClass *dc = DEVICE_CLASS(klass);
> @@ -638,6 +666,7 @@ static void i440fx_pcihost_class_init(ObjectClass *klass, void *data)
>      dc->realize = i440fx_pcihost_realize;
>      dc->fw_name = "pci";
>      dc->no_user = 1;
> +    dc->props = i440fx_props;
>  }
>  
>  static const TypeInfo i440fx_pcihost_info = {
> diff --git a/hw/pci-host/q35.c b/hw/pci-host/q35.c
> index 6b1b3b7..a6936e6 100644
> --- a/hw/pci-host/q35.c
> +++ b/hw/pci-host/q35.c
> @@ -67,6 +67,21 @@ static const char *q35_host_root_bus_path(PCIHostState *host_bridge,
>  static Property mch_props[] = {
>      DEFINE_PROP_UINT64("MCFG", Q35PCIHost, parent_obj.base_addr,
>                          MCH_HOST_BRIDGE_PCIEXBAR_DEFAULT),
> +    DEFINE_PROP_UINT64("pci_hole64_start", Q35PCIHost,
> +                       mch.pci_info.w64.begin, 0),
> +    DEFINE_PROP_UINT64("pci_hole64_end", Q35PCIHost,
> +                       mch.pci_info.w64.end, 0),
> +    /* Leave enough space for the biggest MCFG BAR */
> +    /* TODO: this matches current bios behaviour, but
> +     * it's not a power of two, which means an MTRR
> +     * can't cover it exactly.
> +     */
> +    DEFINE_PROP_UINT64("pci_hole_start", Q35PCIHost,
> +                       mch.pci_info.w32.begin,
> +                       MCH_HOST_BRIDGE_PCIEXBAR_DEFAULT +
> +                       MCH_HOST_BRIDGE_PCIEXBAR_MAX),
> +    DEFINE_PROP_UINT64("pci_hole_end", Q35PCIHost,
> +                       mch.pci_info.w32.end, IO_APIC_DEFAULT_ADDRESS),
>      DEFINE_PROP_END_OF_LIST(),
>  };
>  
> @@ -255,14 +270,6 @@ static int mch_init(PCIDevice *d)
>      hwaddr pci_hole64_size;
>      MCHPCIState *mch = MCH_PCI_DEVICE(d);
>  
> -    /* Leave enough space for the biggest MCFG BAR */
> -    /* TODO: this matches current bios behaviour, but
> -     * it's not a power of two, which means an MTRR
> -     * can't cover it exactly.
> -     */
> -    mch->guest_info->pci_info.w32.begin = MCH_HOST_BRIDGE_PCIEXBAR_DEFAULT +
> -        MCH_HOST_BRIDGE_PCIEXBAR_MAX;
> -
>      /* setup pci memory regions */
>      memory_region_init_alias(&mch->pci_hole, OBJECT(mch), "pci-hole",
>                               mch->pci_address_space,
> @@ -270,15 +277,16 @@ static int mch_init(PCIDevice *d)
>                               0x100000000ULL - mch->below_4g_mem_size);
>      memory_region_add_subregion(mch->system_memory, mch->below_4g_mem_size,
>                                  &mch->pci_hole);
> -    pci_hole64_size = (sizeof(hwaddr) == 4 ? 0 :
> -                       ((uint64_t)1 << 62));
> +
> +    pc_init_pci64_hole(&mch->pci_info, 0x100000000ULL + mch->above_4g_mem_size);
> +    pci_hole64_size = range_size(mch->pci_info.w64);
>      memory_region_init_alias(&mch->pci_hole_64bit, OBJECT(mch), "pci-hole64",
>                               mch->pci_address_space,
> -                             0x100000000ULL + mch->above_4g_mem_size,
> +                             mch->pci_info.w64.begin,
>                               pci_hole64_size);
>      if (pci_hole64_size) {
>          memory_region_add_subregion(mch->system_memory,
> -                                    0x100000000ULL + mch->above_4g_mem_size,
> +                                    mch->pci_info.w64.begin,
>                                      &mch->pci_hole_64bit);
>      }
>      /* smram */
> diff --git a/include/hw/i386/pc.h b/include/hw/i386/pc.h
> index 7fb97b0..ab747b7 100644
> --- a/include/hw/i386/pc.h
> +++ b/include/hw/i386/pc.h
> @@ -18,7 +18,6 @@ typedef struct PcPciInfo {
>  } PcPciInfo;
>  
>  struct PcGuestInfo {
> -    PcPciInfo pci_info;
>      bool has_pci_info;
>      FWCfgState *fw_cfg;
>  };
> @@ -100,6 +99,7 @@ void pc_acpi_init(const char *default_dsdt);
>  
>  PcGuestInfo *pc_guest_info_init(ram_addr_t below_4g_mem_size,
>                                  ram_addr_t above_4g_mem_size);
> +void pc_init_pci64_hole(PcPciInfo *pci_info, uint64_t pci_hole64_start);
>  
>  FWCfgState *pc_memory_init(MemoryRegion *system_memory,
>                             const char *kernel_filename,
> @@ -150,8 +150,7 @@ PCIBus *i440fx_init(PCII440FXState **pi440fx_state, int *piix_devfn,
>                      ram_addr_t ram_size,
>                      hwaddr pci_hole_start,
>                      hwaddr pci_hole_size,
> -                    hwaddr pci_hole64_start,
> -                    hwaddr pci_hole64_size,
> +                    ram_addr_t above_4g_mem_size,
>                      MemoryRegion *pci_memory,
>                      MemoryRegion *ram_memory);
>  
> diff --git a/include/hw/pci-host/q35.h b/include/hw/pci-host/q35.h
> index 3cb631e..3a9e04b 100644
> --- a/include/hw/pci-host/q35.h
> +++ b/include/hw/pci-host/q35.h
> @@ -55,6 +55,7 @@ typedef struct MCHPCIState {
>      MemoryRegion smram_region;
>      MemoryRegion pci_hole;
>      MemoryRegion pci_hole_64bit;
> +    PcPciInfo pci_info;
>      uint8_t smm_enabled;
>      ram_addr_t below_4g_mem_size;
>      ram_addr_t above_4g_mem_size;
> -- 
> 1.8.3.1
Igor Mammedov July 28, 2013, 8:21 a.m. UTC | #2
On Sun, 28 Jul 2013 10:57:12 +0300
"Michael S. Tsirkin" <mst@redhat.com> wrote:

> On Sun, Jul 28, 2013 at 09:29:13AM +0200, Igor Mammedov wrote:
> > It turns out that some 32 bit windows guests crash
> > if 64 bit PCI hole size is >2G.
> > Limit it to 2G for piix and q35 by default.
> > User may override default boundaries by using
> > "pci_hole64_end " property.
> > 
> > Examples:
> > -global i440FX-pcihost.pci_hole64_end=0xffffffffffffffff
> > 
> > -global q35-pcihost.pci_hole64_end=0xffffffffffffffff
> 
> IMO that's pretty bad as user interfaces go.
> In particular if you are not careful you can make
> end < start.
> Why not set the size, and include a patch that
> let people write "1G" or "2G" for convenience?
sure as convenience why not, on top of this patches.
 
> 
> > Reported-by: Igor Mammedov <imammedo@redhat.com>,
> > Signed-off-by: Michael S. Tsirkin <mst@redhat.com>
> > Signed-off-by: Igor Mammedov <imammedo@redhat.com>
> > ---
> >  hw/i386/pc.c              | 59 +++++++++++++++++++++++++++++------------------
> >  hw/i386/pc_piix.c         | 14 +----------
> >  hw/pci-host/piix.c        | 37 +++++++++++++++++++++++++----
> >  hw/pci-host/q35.c         | 32 +++++++++++++++----------
> >  include/hw/i386/pc.h      |  5 ++--
> >  include/hw/pci-host/q35.h |  1 +
> >  6 files changed, 94 insertions(+), 54 deletions(-)
> > 
> > diff --git a/hw/i386/pc.c b/hw/i386/pc.c
> > index b0b98a8..acaeb6c 100644
> > --- a/hw/i386/pc.c
> > +++ b/hw/i386/pc.c
> > @@ -1003,15 +1003,28 @@ typedef struct PcRomPciInfo {
> >  static void pc_fw_cfg_guest_info(PcGuestInfo *guest_info)
> >  {
> >      PcRomPciInfo *info;
> > +    Object *pci_info;
> > +
> >      if (!guest_info->has_pci_info || !guest_info->fw_cfg) {
> >          return;
> >      }
> > +    pci_info = object_resolve_path("/machine/i440fx", NULL);
> > +    if (!pci_info) {
> > +        pci_info = object_resolve_path("/machine/q35", NULL);
> > +        if (!pci_info) {
> > +            return;
> > +        }
> > +    }
> 
> 
> So is the path /machine/i440fx? /machine/i440FX?
> /machine/i440FX-pcihost?
> There's no way to check this code is right without
> actually running it.
that drawback of dynamic lookup,
QOM paths supposed to be stable.

> 
> How about i44fx_get_pci_info so we can have this
> knowledge of paths localized in specific chipset code?
I've seen objections from afaerber about this approach, so dropped
this idea.

> 
> >  
> >      info = g_malloc(sizeof *info);
> > -    info->w32_min = cpu_to_le64(guest_info->pci_info.w32.begin);
> > -    info->w32_max = cpu_to_le64(guest_info->pci_info.w32.end);
> > -    info->w64_min = cpu_to_le64(guest_info->pci_info.w64.begin);
> > -    info->w64_max = cpu_to_le64(guest_info->pci_info.w64.end);
> > +    info->w32_min = cpu_to_le64(object_property_get_int(pci_info,
> > +                                "pci_hole_start", NULL));
> > +    info->w32_max = cpu_to_le64(object_property_get_int(pci_info,
> > +                                "pci_hole_end", NULL));
> 
> Looks wrong.
> object_property_get_int returns a signed int64.
> w32 is unsigned.
> Happens to work but I think we need an explicit API.
I guess it's copy-past error s/cpu_to_le64/cpu_to_le32/

but not need for extra API, with fixed property definition
i.e. s/UINT64/UNIT32/ property set code will take care about limits.

> 
> Property names are hard-coded string literals.
> Please add macros to set and get them
> so we can avoid duplicating code.
> E.g.
sure.

> 
> #define PCI_HOST_PROPS...
> static inline get_
> 
> 
> 
> > +    info->w64_min = cpu_to_le64(object_property_get_int(pci_info,
> > +                                "pci_hole64_start", NULL));
> > +    info->w64_max = cpu_to_le64(object_property_get_int(pci_info,
> > +                                "pci_hole64_end", NULL));
> >      /* Pass PCI hole info to guest via a side channel.
> >       * Required so guest PCI enumeration does the right thing. */
> >      fw_cfg_add_file(guest_info->fw_cfg, "etc/pci-info", info, sizeof *info);
> > @@ -1037,29 +1050,31 @@ PcGuestInfo *pc_guest_info_init(ram_addr_t below_4g_mem_size,
> >      PcGuestInfoState *guest_info_state = g_malloc0(sizeof *guest_info_state);
> >      PcGuestInfo *guest_info = &guest_info_state->info;
> >  
> > -    guest_info->pci_info.w32.end = IO_APIC_DEFAULT_ADDRESS;
> > -    if (sizeof(hwaddr) == 4) {
> > -        guest_info->pci_info.w64.begin = 0;
> > -        guest_info->pci_info.w64.end = 0;
> > -    } else {
> > -        /*
> > -         * BIOS does not set MTRR entries for the 64 bit window, so no need to
> > -         * align address to power of two.  Align address at 1G, this makes sure
> > -         * it can be exactly covered with a PAT entry even when using huge
> > -         * pages.
> > -         */
> > -        guest_info->pci_info.w64.begin =
> > -            ROUND_UP((0x1ULL << 32) + above_4g_mem_size, 0x1ULL << 30);
> > -        guest_info->pci_info.w64.end = guest_info->pci_info.w64.begin +
> > -            (0x1ULL << 62);
> > -        assert(guest_info->pci_info.w64.begin <= guest_info->pci_info.w64.end);
> > -    }
> > -
> >      guest_info_state->machine_done.notify = pc_guest_info_machine_done;
> >      qemu_add_machine_init_done_notifier(&guest_info_state->machine_done);
> >      return guest_info;
> >  }
> >  
> > +void pc_init_pci64_hole(PcPciInfo *pci_info, uint64_t pci_hole64_start)
> > +{
> > +    if (sizeof(hwaddr) == 4) {
> > +        memset(&pci_info->w64, sizeof(pci_info->w64), 0);
> > +        return;
> > +    }
> > +    /*
> > +     * BIOS does not set MTRR entries for the 64 bit window, so no need to
> > +     * align address to power of two.  Align address at 1G, this makes sure
> > +     * it can be exactly covered with a PAT entry even when using huge
> > +     * pages.
> > +     */
> > +    pci_info->w64.begin = ROUND_UP(pci_hole64_start, 0x1ULL << 30);
> > +    if (!pci_info->w64.end) {
> > +        /* set default end if it wasn't specified, + 2 Gb past start */
> > +        pci_info->w64.end = pci_info->w64.begin + (0x1ULL << 31);
> > +    }
> > +    assert(pci_info->w64.begin < pci_info->w64.end);
> > +}
> > +
> >  void pc_acpi_init(const char *default_dsdt)
> >  {
> >      char *filename;
> > diff --git a/hw/i386/pc_piix.c b/hw/i386/pc_piix.c
> > index b58c255..ab25458 100644
> > --- a/hw/i386/pc_piix.c
> > +++ b/hw/i386/pc_piix.c
> > @@ -129,15 +129,6 @@ static void pc_init1(MemoryRegion *system_memory,
> >      guest_info = pc_guest_info_init(below_4g_mem_size, above_4g_mem_size);
> >      guest_info->has_pci_info = has_pci_info;
> >  
> > -    /* Set PCI window size the way seabios has always done it. */
> > -    /* Power of 2 so bios can cover it with a single MTRR */
> > -    if (ram_size <= 0x80000000)
> > -        guest_info->pci_info.w32.begin = 0x80000000;
> > -    else if (ram_size <= 0xc0000000)
> > -        guest_info->pci_info.w32.begin = 0xc0000000;
> > -    else
> > -        guest_info->pci_info.w32.begin = 0xe0000000;
> > -
> >      /* allocate ram and load rom/bios */
> >      if (!xen_enabled()) {
> >          fw_cfg = pc_memory_init(system_memory,
> > @@ -160,10 +151,7 @@ static void pc_init1(MemoryRegion *system_memory,
> >                                system_memory, system_io, ram_size,
> >                                below_4g_mem_size,
> >                                0x100000000ULL - below_4g_mem_size,
> > -                              0x100000000ULL + above_4g_mem_size,
> > -                              (sizeof(hwaddr) == 4
> > -                               ? 0
> > -                               : ((uint64_t)1 << 62)),
> > +                              above_4g_mem_size,
> >                                pci_memory, ram_memory);
> >      } else {
> >          pci_bus = NULL;
> > diff --git a/hw/pci-host/piix.c b/hw/pci-host/piix.c
> > index 4624d04..59a42c5 100644
> > --- a/hw/pci-host/piix.c
> > +++ b/hw/pci-host/piix.c
> > @@ -32,6 +32,7 @@
> >  #include "hw/xen/xen.h"
> >  #include "hw/pci-host/pam.h"
> >  #include "sysemu/sysemu.h"
> > +#include "hw/i386/ioapic.h"
> >  
> >  /*
> >   * I440FX chipset data sheet.
> > @@ -44,6 +45,7 @@
> >  
> >  typedef struct I440FXState {
> >      PCIHostState parent_obj;
> > +    PcPciInfo pci_info;
> >  } I440FXState;
> >  
> >  #define PIIX_NUM_PIC_IRQS       16      /* i8259 * 2 */
> > @@ -247,8 +249,7 @@ PCIBus *i440fx_init(PCII440FXState **pi440fx_state,
> >                      ram_addr_t ram_size,
> >                      hwaddr pci_hole_start,
> >                      hwaddr pci_hole_size,
> > -                    hwaddr pci_hole64_start,
> > -                    hwaddr pci_hole64_size,
> > +                    ram_addr_t above_4g_mem_size,
> >                      MemoryRegion *pci_address_space,
> >                      MemoryRegion *ram_memory)
> >  {
> > @@ -259,6 +260,8 @@ PCIBus *i440fx_init(PCII440FXState **pi440fx_state,
> >      PIIX3State *piix3;
> >      PCII440FXState *f;
> >      unsigned i;
> > +    I440FXState *i440fx;
> > +    uint64_t pci_hole64_size;
> >  
> >      dev = qdev_create(NULL, TYPE_I440FX_PCI_HOST);
> >      s = PCI_HOST_BRIDGE(dev);
> > @@ -274,14 +277,30 @@ PCIBus *i440fx_init(PCII440FXState **pi440fx_state,
> >      f->system_memory = address_space_mem;
> >      f->pci_address_space = pci_address_space;
> >      f->ram_memory = ram_memory;
> > +
> > +    i440fx = I440FX_PCI_HOST(dev);
> > +    /* Set PCI window size the way seabios has always done it. */
> > +    /* Power of 2 so bios can cover it with a single MTRR */
> > +    if (ram_size <= 0x80000000) {
> > +        i440fx->pci_info.w32.begin = 0x80000000;
> > +    } else if (ram_size <= 0xc0000000) {
> > +        i440fx->pci_info.w32.begin = 0xc0000000;
> > +    } else {
> > +        i440fx->pci_info.w32.begin = 0xe0000000;
> > +    }
> > +
> >      memory_region_init_alias(&f->pci_hole, OBJECT(d), "pci-hole", f->pci_address_space,
> >                               pci_hole_start, pci_hole_size);
> >      memory_region_add_subregion(f->system_memory, pci_hole_start, &f->pci_hole);
> > +
> > +    pc_init_pci64_hole(&i440fx->pci_info, 0x100000000ULL + above_4g_mem_size);
> > +    pci_hole64_size = range_size(i440fx->pci_info.w64);
> >      memory_region_init_alias(&f->pci_hole_64bit, OBJECT(d), "pci-hole64",
> >                               f->pci_address_space,
> > -                             pci_hole64_start, pci_hole64_size);
> > +                             i440fx->pci_info.w64.begin, pci_hole64_size);
> >      if (pci_hole64_size) {
> > -        memory_region_add_subregion(f->system_memory, pci_hole64_start,
> > +        memory_region_add_subregion(f->system_memory,
> > +                                    i440fx->pci_info.w64.begin,
> >                                      &f->pci_hole_64bit);
> >      }
> >      memory_region_init_alias(&f->smram_region, OBJECT(d), "smram-region",
> > @@ -629,6 +648,15 @@ static const char *i440fx_pcihost_root_bus_path(PCIHostState *host_bridge,
> >      return "0000";
> >  }
> >  
> > +static Property i440fx_props[] = {
> > +    DEFINE_PROP_UINT64("pci_hole64_start", I440FXState, pci_info.w64.begin, 0),
> > +    DEFINE_PROP_UINT64("pci_hole64_end", I440FXState, pci_info.w64.end, 0),
> > +    DEFINE_PROP_UINT64("pci_hole_start", I440FXState, pci_info.w32.begin, 0),
> > +    DEFINE_PROP_UINT64("pci_hole_end", I440FXState, pci_info.w32.end,
> > +                       IO_APIC_DEFAULT_ADDRESS),
> > +    DEFINE_PROP_END_OF_LIST(),
> > +};
> > +
> 
> So we have 4 properties. One of them pci_hole64_end
> is supposed to be set to a value.
> Others should not be touched under any circuimstances.
> Of course if you query properties you have no way
> to know which is which and what are the legal values.
> Ouch.
read-only properties are possible but we would have to drop
usage DEFINE_PROP_UINT64 of and explicitly use only setter in PropertyInfo,
user better not to touch these properties since they are mostly internal API.
but if we say it's internal properties then enforcing read-only might be
overkill.

For user friendly property "pci_hole64_size" would be nice to have.

> 
> >  static void i440fx_pcihost_class_init(ObjectClass *klass, void *data)
> >  {
> >      DeviceClass *dc = DEVICE_CLASS(klass);
> > @@ -638,6 +666,7 @@ static void i440fx_pcihost_class_init(ObjectClass *klass, void *data)
> >      dc->realize = i440fx_pcihost_realize;
> >      dc->fw_name = "pci";
> >      dc->no_user = 1;
> > +    dc->props = i440fx_props;
> >  }
> >  
> >  static const TypeInfo i440fx_pcihost_info = {
> > diff --git a/hw/pci-host/q35.c b/hw/pci-host/q35.c
> > index 6b1b3b7..a6936e6 100644
> > --- a/hw/pci-host/q35.c
> > +++ b/hw/pci-host/q35.c
> > @@ -67,6 +67,21 @@ static const char *q35_host_root_bus_path(PCIHostState *host_bridge,
> >  static Property mch_props[] = {
> >      DEFINE_PROP_UINT64("MCFG", Q35PCIHost, parent_obj.base_addr,
> >                          MCH_HOST_BRIDGE_PCIEXBAR_DEFAULT),
> > +    DEFINE_PROP_UINT64("pci_hole64_start", Q35PCIHost,
> > +                       mch.pci_info.w64.begin, 0),
> > +    DEFINE_PROP_UINT64("pci_hole64_end", Q35PCIHost,
> > +                       mch.pci_info.w64.end, 0),
> > +    /* Leave enough space for the biggest MCFG BAR */
> > +    /* TODO: this matches current bios behaviour, but
> > +     * it's not a power of two, which means an MTRR
> > +     * can't cover it exactly.
> > +     */
> > +    DEFINE_PROP_UINT64("pci_hole_start", Q35PCIHost,
> > +                       mch.pci_info.w32.begin,
> > +                       MCH_HOST_BRIDGE_PCIEXBAR_DEFAULT +
> > +                       MCH_HOST_BRIDGE_PCIEXBAR_MAX),
> > +    DEFINE_PROP_UINT64("pci_hole_end", Q35PCIHost,
> > +                       mch.pci_info.w32.end, IO_APIC_DEFAULT_ADDRESS),
> >      DEFINE_PROP_END_OF_LIST(),
> >  };
> >  
> > @@ -255,14 +270,6 @@ static int mch_init(PCIDevice *d)
> >      hwaddr pci_hole64_size;
> >      MCHPCIState *mch = MCH_PCI_DEVICE(d);
> >  
> > -    /* Leave enough space for the biggest MCFG BAR */
> > -    /* TODO: this matches current bios behaviour, but
> > -     * it's not a power of two, which means an MTRR
> > -     * can't cover it exactly.
> > -     */
> > -    mch->guest_info->pci_info.w32.begin = MCH_HOST_BRIDGE_PCIEXBAR_DEFAULT +
> > -        MCH_HOST_BRIDGE_PCIEXBAR_MAX;
> > -
> >      /* setup pci memory regions */
> >      memory_region_init_alias(&mch->pci_hole, OBJECT(mch), "pci-hole",
> >                               mch->pci_address_space,
> > @@ -270,15 +277,16 @@ static int mch_init(PCIDevice *d)
> >                               0x100000000ULL - mch->below_4g_mem_size);
> >      memory_region_add_subregion(mch->system_memory, mch->below_4g_mem_size,
> >                                  &mch->pci_hole);
> > -    pci_hole64_size = (sizeof(hwaddr) == 4 ? 0 :
> > -                       ((uint64_t)1 << 62));
> > +
> > +    pc_init_pci64_hole(&mch->pci_info, 0x100000000ULL + mch->above_4g_mem_size);
> > +    pci_hole64_size = range_size(mch->pci_info.w64);
> >      memory_region_init_alias(&mch->pci_hole_64bit, OBJECT(mch), "pci-hole64",
> >                               mch->pci_address_space,
> > -                             0x100000000ULL + mch->above_4g_mem_size,
> > +                             mch->pci_info.w64.begin,
> >                               pci_hole64_size);
> >      if (pci_hole64_size) {
> >          memory_region_add_subregion(mch->system_memory,
> > -                                    0x100000000ULL + mch->above_4g_mem_size,
> > +                                    mch->pci_info.w64.begin,
> >                                      &mch->pci_hole_64bit);
> >      }
> >      /* smram */
> > diff --git a/include/hw/i386/pc.h b/include/hw/i386/pc.h
> > index 7fb97b0..ab747b7 100644
> > --- a/include/hw/i386/pc.h
> > +++ b/include/hw/i386/pc.h
> > @@ -18,7 +18,6 @@ typedef struct PcPciInfo {
> >  } PcPciInfo;
> >  
> >  struct PcGuestInfo {
> > -    PcPciInfo pci_info;
> >      bool has_pci_info;
> >      FWCfgState *fw_cfg;
> >  };
> > @@ -100,6 +99,7 @@ void pc_acpi_init(const char *default_dsdt);
> >  
> >  PcGuestInfo *pc_guest_info_init(ram_addr_t below_4g_mem_size,
> >                                  ram_addr_t above_4g_mem_size);
> > +void pc_init_pci64_hole(PcPciInfo *pci_info, uint64_t pci_hole64_start);
> >  
> >  FWCfgState *pc_memory_init(MemoryRegion *system_memory,
> >                             const char *kernel_filename,
> > @@ -150,8 +150,7 @@ PCIBus *i440fx_init(PCII440FXState **pi440fx_state, int *piix_devfn,
> >                      ram_addr_t ram_size,
> >                      hwaddr pci_hole_start,
> >                      hwaddr pci_hole_size,
> > -                    hwaddr pci_hole64_start,
> > -                    hwaddr pci_hole64_size,
> > +                    ram_addr_t above_4g_mem_size,
> >                      MemoryRegion *pci_memory,
> >                      MemoryRegion *ram_memory);
> >  
> > diff --git a/include/hw/pci-host/q35.h b/include/hw/pci-host/q35.h
> > index 3cb631e..3a9e04b 100644
> > --- a/include/hw/pci-host/q35.h
> > +++ b/include/hw/pci-host/q35.h
> > @@ -55,6 +55,7 @@ typedef struct MCHPCIState {
> >      MemoryRegion smram_region;
> >      MemoryRegion pci_hole;
> >      MemoryRegion pci_hole_64bit;
> > +    PcPciInfo pci_info;
> >      uint8_t smm_enabled;
> >      ram_addr_t below_4g_mem_size;
> >      ram_addr_t above_4g_mem_size;
> > -- 
> > 1.8.3.1
>
Michael S. Tsirkin July 28, 2013, 9:11 a.m. UTC | #3
On Sun, Jul 28, 2013 at 10:21:56AM +0200, Igor Mammedov wrote:
> On Sun, 28 Jul 2013 10:57:12 +0300
> "Michael S. Tsirkin" <mst@redhat.com> wrote:
> 
> > On Sun, Jul 28, 2013 at 09:29:13AM +0200, Igor Mammedov wrote:
> > > It turns out that some 32 bit windows guests crash
> > > if 64 bit PCI hole size is >2G.
> > > Limit it to 2G for piix and q35 by default.
> > > User may override default boundaries by using
> > > "pci_hole64_end " property.
> > > 
> > > Examples:
> > > -global i440FX-pcihost.pci_hole64_end=0xffffffffffffffff
> > > 
> > > -global q35-pcihost.pci_hole64_end=0xffffffffffffffff
> > 
> > IMO that's pretty bad as user interfaces go.
> > In particular if you are not careful you can make
> > end < start.
> > Why not set the size, and include a patch that
> > let people write "1G" or "2G" for convenience?
> sure as convenience why not, on top of this patches.

Well because you are specifying end as 0xffffffffffffffff
so it's not a multiple of 1G?

> > 
> > > Reported-by: Igor Mammedov <imammedo@redhat.com>,
> > > Signed-off-by: Michael S. Tsirkin <mst@redhat.com>
> > > Signed-off-by: Igor Mammedov <imammedo@redhat.com>
> > > ---
> > >  hw/i386/pc.c              | 59 +++++++++++++++++++++++++++++------------------
> > >  hw/i386/pc_piix.c         | 14 +----------
> > >  hw/pci-host/piix.c        | 37 +++++++++++++++++++++++++----
> > >  hw/pci-host/q35.c         | 32 +++++++++++++++----------
> > >  include/hw/i386/pc.h      |  5 ++--
> > >  include/hw/pci-host/q35.h |  1 +
> > >  6 files changed, 94 insertions(+), 54 deletions(-)
> > > 
> > > diff --git a/hw/i386/pc.c b/hw/i386/pc.c
> > > index b0b98a8..acaeb6c 100644
> > > --- a/hw/i386/pc.c
> > > +++ b/hw/i386/pc.c
> > > @@ -1003,15 +1003,28 @@ typedef struct PcRomPciInfo {
> > >  static void pc_fw_cfg_guest_info(PcGuestInfo *guest_info)
> > >  {
> > >      PcRomPciInfo *info;
> > > +    Object *pci_info;
> > > +
> > >      if (!guest_info->has_pci_info || !guest_info->fw_cfg) {
> > >          return;
> > >      }
> > > +    pci_info = object_resolve_path("/machine/i440fx", NULL);
> > > +    if (!pci_info) {
> > > +        pci_info = object_resolve_path("/machine/q35", NULL);
> > > +        if (!pci_info) {
> > > +            return;
> > > +        }
> > > +    }
> > 
> > 
> > So is the path /machine/i440fx? /machine/i440FX?
> > /machine/i440FX-pcihost?
> > There's no way to check this code is right without
> > actually running it.
> that drawback of dynamic lookup,
> QOM paths supposed to be stable.
> 
> > 
> > How about i44fx_get_pci_info so we can have this
> > knowledge of paths localized in specific chipset code?
> I've seen objections from afaerber about this approach, so dropped
> this idea.
> 
> > 
> > >  
> > >      info = g_malloc(sizeof *info);
> > > -    info->w32_min = cpu_to_le64(guest_info->pci_info.w32.begin);
> > > -    info->w32_max = cpu_to_le64(guest_info->pci_info.w32.end);
> > > -    info->w64_min = cpu_to_le64(guest_info->pci_info.w64.begin);
> > > -    info->w64_max = cpu_to_le64(guest_info->pci_info.w64.end);
> > > +    info->w32_min = cpu_to_le64(object_property_get_int(pci_info,
> > > +                                "pci_hole_start", NULL));
> > > +    info->w32_max = cpu_to_le64(object_property_get_int(pci_info,
> > > +                                "pci_hole_end", NULL));
> > 
> > Looks wrong.
> > object_property_get_int returns a signed int64.
> > w32 is unsigned.
> > Happens to work but I think we need an explicit API.
> I guess it's copy-past error s/cpu_to_le64/cpu_to_le32/

Not these are 64 bit values, but they need to be
unsigned not signed.

> but not need for extra API, with fixed property definition
> i.e. s/UINT64/UNIT32/ property set code will take care about limits.

If you replace these with UINT32 you won't be able to
specify values >4G.

> > 
> > Property names are hard-coded string literals.
> > Please add macros to set and get them
> > so we can avoid duplicating code.
> > E.g.
> sure.
> 
> > 
> > #define PCI_HOST_PROPS...
> > static inline get_
> > 
> > 
> > 
> > > +    info->w64_min = cpu_to_le64(object_property_get_int(pci_info,
> > > +                                "pci_hole64_start", NULL));
> > > +    info->w64_max = cpu_to_le64(object_property_get_int(pci_info,
> > > +                                "pci_hole64_end", NULL));
> > >      /* Pass PCI hole info to guest via a side channel.
> > >       * Required so guest PCI enumeration does the right thing. */
> > >      fw_cfg_add_file(guest_info->fw_cfg, "etc/pci-info", info, sizeof *info);
> > > @@ -1037,29 +1050,31 @@ PcGuestInfo *pc_guest_info_init(ram_addr_t below_4g_mem_size,
> > >      PcGuestInfoState *guest_info_state = g_malloc0(sizeof *guest_info_state);
> > >      PcGuestInfo *guest_info = &guest_info_state->info;
> > >  
> > > -    guest_info->pci_info.w32.end = IO_APIC_DEFAULT_ADDRESS;
> > > -    if (sizeof(hwaddr) == 4) {
> > > -        guest_info->pci_info.w64.begin = 0;
> > > -        guest_info->pci_info.w64.end = 0;
> > > -    } else {
> > > -        /*
> > > -         * BIOS does not set MTRR entries for the 64 bit window, so no need to
> > > -         * align address to power of two.  Align address at 1G, this makes sure
> > > -         * it can be exactly covered with a PAT entry even when using huge
> > > -         * pages.
> > > -         */
> > > -        guest_info->pci_info.w64.begin =
> > > -            ROUND_UP((0x1ULL << 32) + above_4g_mem_size, 0x1ULL << 30);
> > > -        guest_info->pci_info.w64.end = guest_info->pci_info.w64.begin +
> > > -            (0x1ULL << 62);
> > > -        assert(guest_info->pci_info.w64.begin <= guest_info->pci_info.w64.end);
> > > -    }
> > > -
> > >      guest_info_state->machine_done.notify = pc_guest_info_machine_done;
> > >      qemu_add_machine_init_done_notifier(&guest_info_state->machine_done);
> > >      return guest_info;
> > >  }
> > >  
> > > +void pc_init_pci64_hole(PcPciInfo *pci_info, uint64_t pci_hole64_start)
> > > +{
> > > +    if (sizeof(hwaddr) == 4) {
> > > +        memset(&pci_info->w64, sizeof(pci_info->w64), 0);
> > > +        return;
> > > +    }
> > > +    /*
> > > +     * BIOS does not set MTRR entries for the 64 bit window, so no need to
> > > +     * align address to power of two.  Align address at 1G, this makes sure
> > > +     * it can be exactly covered with a PAT entry even when using huge
> > > +     * pages.
> > > +     */
> > > +    pci_info->w64.begin = ROUND_UP(pci_hole64_start, 0x1ULL << 30);
> > > +    if (!pci_info->w64.end) {
> > > +        /* set default end if it wasn't specified, + 2 Gb past start */
> > > +        pci_info->w64.end = pci_info->w64.begin + (0x1ULL << 31);
> > > +    }
> > > +    assert(pci_info->w64.begin < pci_info->w64.end);
> > > +}
> > > +
> > >  void pc_acpi_init(const char *default_dsdt)
> > >  {
> > >      char *filename;
> > > diff --git a/hw/i386/pc_piix.c b/hw/i386/pc_piix.c
> > > index b58c255..ab25458 100644
> > > --- a/hw/i386/pc_piix.c
> > > +++ b/hw/i386/pc_piix.c
> > > @@ -129,15 +129,6 @@ static void pc_init1(MemoryRegion *system_memory,
> > >      guest_info = pc_guest_info_init(below_4g_mem_size, above_4g_mem_size);
> > >      guest_info->has_pci_info = has_pci_info;
> > >  
> > > -    /* Set PCI window size the way seabios has always done it. */
> > > -    /* Power of 2 so bios can cover it with a single MTRR */
> > > -    if (ram_size <= 0x80000000)
> > > -        guest_info->pci_info.w32.begin = 0x80000000;
> > > -    else if (ram_size <= 0xc0000000)
> > > -        guest_info->pci_info.w32.begin = 0xc0000000;
> > > -    else
> > > -        guest_info->pci_info.w32.begin = 0xe0000000;
> > > -
> > >      /* allocate ram and load rom/bios */
> > >      if (!xen_enabled()) {
> > >          fw_cfg = pc_memory_init(system_memory,
> > > @@ -160,10 +151,7 @@ static void pc_init1(MemoryRegion *system_memory,
> > >                                system_memory, system_io, ram_size,
> > >                                below_4g_mem_size,
> > >                                0x100000000ULL - below_4g_mem_size,
> > > -                              0x100000000ULL + above_4g_mem_size,
> > > -                              (sizeof(hwaddr) == 4
> > > -                               ? 0
> > > -                               : ((uint64_t)1 << 62)),
> > > +                              above_4g_mem_size,
> > >                                pci_memory, ram_memory);
> > >      } else {
> > >          pci_bus = NULL;
> > > diff --git a/hw/pci-host/piix.c b/hw/pci-host/piix.c
> > > index 4624d04..59a42c5 100644
> > > --- a/hw/pci-host/piix.c
> > > +++ b/hw/pci-host/piix.c
> > > @@ -32,6 +32,7 @@
> > >  #include "hw/xen/xen.h"
> > >  #include "hw/pci-host/pam.h"
> > >  #include "sysemu/sysemu.h"
> > > +#include "hw/i386/ioapic.h"
> > >  
> > >  /*
> > >   * I440FX chipset data sheet.
> > > @@ -44,6 +45,7 @@
> > >  
> > >  typedef struct I440FXState {
> > >      PCIHostState parent_obj;
> > > +    PcPciInfo pci_info;
> > >  } I440FXState;
> > >  
> > >  #define PIIX_NUM_PIC_IRQS       16      /* i8259 * 2 */
> > > @@ -247,8 +249,7 @@ PCIBus *i440fx_init(PCII440FXState **pi440fx_state,
> > >                      ram_addr_t ram_size,
> > >                      hwaddr pci_hole_start,
> > >                      hwaddr pci_hole_size,
> > > -                    hwaddr pci_hole64_start,
> > > -                    hwaddr pci_hole64_size,
> > > +                    ram_addr_t above_4g_mem_size,
> > >                      MemoryRegion *pci_address_space,
> > >                      MemoryRegion *ram_memory)
> > >  {
> > > @@ -259,6 +260,8 @@ PCIBus *i440fx_init(PCII440FXState **pi440fx_state,
> > >      PIIX3State *piix3;
> > >      PCII440FXState *f;
> > >      unsigned i;
> > > +    I440FXState *i440fx;
> > > +    uint64_t pci_hole64_size;
> > >  
> > >      dev = qdev_create(NULL, TYPE_I440FX_PCI_HOST);
> > >      s = PCI_HOST_BRIDGE(dev);
> > > @@ -274,14 +277,30 @@ PCIBus *i440fx_init(PCII440FXState **pi440fx_state,
> > >      f->system_memory = address_space_mem;
> > >      f->pci_address_space = pci_address_space;
> > >      f->ram_memory = ram_memory;
> > > +
> > > +    i440fx = I440FX_PCI_HOST(dev);
> > > +    /* Set PCI window size the way seabios has always done it. */
> > > +    /* Power of 2 so bios can cover it with a single MTRR */
> > > +    if (ram_size <= 0x80000000) {
> > > +        i440fx->pci_info.w32.begin = 0x80000000;
> > > +    } else if (ram_size <= 0xc0000000) {
> > > +        i440fx->pci_info.w32.begin = 0xc0000000;
> > > +    } else {
> > > +        i440fx->pci_info.w32.begin = 0xe0000000;
> > > +    }
> > > +
> > >      memory_region_init_alias(&f->pci_hole, OBJECT(d), "pci-hole", f->pci_address_space,
> > >                               pci_hole_start, pci_hole_size);
> > >      memory_region_add_subregion(f->system_memory, pci_hole_start, &f->pci_hole);
> > > +
> > > +    pc_init_pci64_hole(&i440fx->pci_info, 0x100000000ULL + above_4g_mem_size);
> > > +    pci_hole64_size = range_size(i440fx->pci_info.w64);
> > >      memory_region_init_alias(&f->pci_hole_64bit, OBJECT(d), "pci-hole64",
> > >                               f->pci_address_space,
> > > -                             pci_hole64_start, pci_hole64_size);
> > > +                             i440fx->pci_info.w64.begin, pci_hole64_size);
> > >      if (pci_hole64_size) {
> > > -        memory_region_add_subregion(f->system_memory, pci_hole64_start,
> > > +        memory_region_add_subregion(f->system_memory,
> > > +                                    i440fx->pci_info.w64.begin,
> > >                                      &f->pci_hole_64bit);
> > >      }
> > >      memory_region_init_alias(&f->smram_region, OBJECT(d), "smram-region",
> > > @@ -629,6 +648,15 @@ static const char *i440fx_pcihost_root_bus_path(PCIHostState *host_bridge,
> > >      return "0000";
> > >  }
> > >  
> > > +static Property i440fx_props[] = {
> > > +    DEFINE_PROP_UINT64("pci_hole64_start", I440FXState, pci_info.w64.begin, 0),
> > > +    DEFINE_PROP_UINT64("pci_hole64_end", I440FXState, pci_info.w64.end, 0),
> > > +    DEFINE_PROP_UINT64("pci_hole_start", I440FXState, pci_info.w32.begin, 0),
> > > +    DEFINE_PROP_UINT64("pci_hole_end", I440FXState, pci_info.w32.end,
> > > +                       IO_APIC_DEFAULT_ADDRESS),
> > > +    DEFINE_PROP_END_OF_LIST(),
> > > +};
> > > +
> > 
> > So we have 4 properties. One of them pci_hole64_end
> > is supposed to be set to a value.
> > Others should not be touched under any circuimstances.
> > Of course if you query properties you have no way
> > to know which is which and what are the legal values.
> > Ouch.
> read-only properties are possible but we would have to drop
> usage DEFINE_PROP_UINT64 of and explicitly use only setter in PropertyInfo,

Or add DEFINE_PROP_UINT64_RO for this?

> user better not to touch these properties since they are mostly internal API.
> but if we say it's internal properties then enforcing read-only might be
> overkill.
> For user friendly property "pci_hole64_size" would be nice to have.

So at the moment I do

qemu -device i440FX-pcihost,help

and this will get all properties.

If we add some properties that user can not set
they should not appear in this output.


> > 
> > >  static void i440fx_pcihost_class_init(ObjectClass *klass, void *data)
> > >  {
> > >      DeviceClass *dc = DEVICE_CLASS(klass);
> > > @@ -638,6 +666,7 @@ static void i440fx_pcihost_class_init(ObjectClass *klass, void *data)
> > >      dc->realize = i440fx_pcihost_realize;
> > >      dc->fw_name = "pci";
> > >      dc->no_user = 1;
> > > +    dc->props = i440fx_props;
> > >  }
> > >  
> > >  static const TypeInfo i440fx_pcihost_info = {
> > > diff --git a/hw/pci-host/q35.c b/hw/pci-host/q35.c
> > > index 6b1b3b7..a6936e6 100644
> > > --- a/hw/pci-host/q35.c
> > > +++ b/hw/pci-host/q35.c
> > > @@ -67,6 +67,21 @@ static const char *q35_host_root_bus_path(PCIHostState *host_bridge,
> > >  static Property mch_props[] = {
> > >      DEFINE_PROP_UINT64("MCFG", Q35PCIHost, parent_obj.base_addr,
> > >                          MCH_HOST_BRIDGE_PCIEXBAR_DEFAULT),
> > > +    DEFINE_PROP_UINT64("pci_hole64_start", Q35PCIHost,
> > > +                       mch.pci_info.w64.begin, 0),
> > > +    DEFINE_PROP_UINT64("pci_hole64_end", Q35PCIHost,
> > > +                       mch.pci_info.w64.end, 0),
> > > +    /* Leave enough space for the biggest MCFG BAR */
> > > +    /* TODO: this matches current bios behaviour, but
> > > +     * it's not a power of two, which means an MTRR
> > > +     * can't cover it exactly.
> > > +     */
> > > +    DEFINE_PROP_UINT64("pci_hole_start", Q35PCIHost,
> > > +                       mch.pci_info.w32.begin,
> > > +                       MCH_HOST_BRIDGE_PCIEXBAR_DEFAULT +
> > > +                       MCH_HOST_BRIDGE_PCIEXBAR_MAX),
> > > +    DEFINE_PROP_UINT64("pci_hole_end", Q35PCIHost,
> > > +                       mch.pci_info.w32.end, IO_APIC_DEFAULT_ADDRESS),
> > >      DEFINE_PROP_END_OF_LIST(),
> > >  };
> > >  
> > > @@ -255,14 +270,6 @@ static int mch_init(PCIDevice *d)
> > >      hwaddr pci_hole64_size;
> > >      MCHPCIState *mch = MCH_PCI_DEVICE(d);
> > >  
> > > -    /* Leave enough space for the biggest MCFG BAR */
> > > -    /* TODO: this matches current bios behaviour, but
> > > -     * it's not a power of two, which means an MTRR
> > > -     * can't cover it exactly.
> > > -     */
> > > -    mch->guest_info->pci_info.w32.begin = MCH_HOST_BRIDGE_PCIEXBAR_DEFAULT +
> > > -        MCH_HOST_BRIDGE_PCIEXBAR_MAX;
> > > -
> > >      /* setup pci memory regions */
> > >      memory_region_init_alias(&mch->pci_hole, OBJECT(mch), "pci-hole",
> > >                               mch->pci_address_space,
> > > @@ -270,15 +277,16 @@ static int mch_init(PCIDevice *d)
> > >                               0x100000000ULL - mch->below_4g_mem_size);
> > >      memory_region_add_subregion(mch->system_memory, mch->below_4g_mem_size,
> > >                                  &mch->pci_hole);
> > > -    pci_hole64_size = (sizeof(hwaddr) == 4 ? 0 :
> > > -                       ((uint64_t)1 << 62));
> > > +
> > > +    pc_init_pci64_hole(&mch->pci_info, 0x100000000ULL + mch->above_4g_mem_size);
> > > +    pci_hole64_size = range_size(mch->pci_info.w64);
> > >      memory_region_init_alias(&mch->pci_hole_64bit, OBJECT(mch), "pci-hole64",
> > >                               mch->pci_address_space,
> > > -                             0x100000000ULL + mch->above_4g_mem_size,
> > > +                             mch->pci_info.w64.begin,
> > >                               pci_hole64_size);
> > >      if (pci_hole64_size) {
> > >          memory_region_add_subregion(mch->system_memory,
> > > -                                    0x100000000ULL + mch->above_4g_mem_size,
> > > +                                    mch->pci_info.w64.begin,
> > >                                      &mch->pci_hole_64bit);
> > >      }
> > >      /* smram */
> > > diff --git a/include/hw/i386/pc.h b/include/hw/i386/pc.h
> > > index 7fb97b0..ab747b7 100644
> > > --- a/include/hw/i386/pc.h
> > > +++ b/include/hw/i386/pc.h
> > > @@ -18,7 +18,6 @@ typedef struct PcPciInfo {
> > >  } PcPciInfo;
> > >  
> > >  struct PcGuestInfo {
> > > -    PcPciInfo pci_info;
> > >      bool has_pci_info;
> > >      FWCfgState *fw_cfg;
> > >  };
> > > @@ -100,6 +99,7 @@ void pc_acpi_init(const char *default_dsdt);
> > >  
> > >  PcGuestInfo *pc_guest_info_init(ram_addr_t below_4g_mem_size,
> > >                                  ram_addr_t above_4g_mem_size);
> > > +void pc_init_pci64_hole(PcPciInfo *pci_info, uint64_t pci_hole64_start);
> > >  
> > >  FWCfgState *pc_memory_init(MemoryRegion *system_memory,
> > >                             const char *kernel_filename,
> > > @@ -150,8 +150,7 @@ PCIBus *i440fx_init(PCII440FXState **pi440fx_state, int *piix_devfn,
> > >                      ram_addr_t ram_size,
> > >                      hwaddr pci_hole_start,
> > >                      hwaddr pci_hole_size,
> > > -                    hwaddr pci_hole64_start,
> > > -                    hwaddr pci_hole64_size,
> > > +                    ram_addr_t above_4g_mem_size,
> > >                      MemoryRegion *pci_memory,
> > >                      MemoryRegion *ram_memory);
> > >  
> > > diff --git a/include/hw/pci-host/q35.h b/include/hw/pci-host/q35.h
> > > index 3cb631e..3a9e04b 100644
> > > --- a/include/hw/pci-host/q35.h
> > > +++ b/include/hw/pci-host/q35.h
> > > @@ -55,6 +55,7 @@ typedef struct MCHPCIState {
> > >      MemoryRegion smram_region;
> > >      MemoryRegion pci_hole;
> > >      MemoryRegion pci_hole_64bit;
> > > +    PcPciInfo pci_info;
> > >      uint8_t smm_enabled;
> > >      ram_addr_t below_4g_mem_size;
> > >      ram_addr_t above_4g_mem_size;
> > > -- 
> > > 1.8.3.1
> > 
> 
> 
> -- 
> Regards,
>   Igor
Michael S. Tsirkin July 28, 2013, 9:13 a.m. UTC | #4
On Sun, Jul 28, 2013 at 10:21:56AM +0200, Igor Mammedov wrote:
> On Sun, 28 Jul 2013 10:57:12 +0300
> "Michael S. Tsirkin" <mst@redhat.com> wrote:
> 
> > On Sun, Jul 28, 2013 at 09:29:13AM +0200, Igor Mammedov wrote:
> > > It turns out that some 32 bit windows guests crash
> > > if 64 bit PCI hole size is >2G.
> > > Limit it to 2G for piix and q35 by default.
> > > User may override default boundaries by using
> > > "pci_hole64_end " property.
> > > 
> > > Examples:
> > > -global i440FX-pcihost.pci_hole64_end=0xffffffffffffffff
> > > 
> > > -global q35-pcihost.pci_hole64_end=0xffffffffffffffff
> > 
> > IMO that's pretty bad as user interfaces go.
> > In particular if you are not careful you can make
> > end < start.
> > Why not set the size, and include a patch that
> > let people write "1G" or "2G" for convenience?
> sure as convenience why not, on top of this patches.
>  
> > 
> > > Reported-by: Igor Mammedov <imammedo@redhat.com>,
> > > Signed-off-by: Michael S. Tsirkin <mst@redhat.com>
> > > Signed-off-by: Igor Mammedov <imammedo@redhat.com>
> > > ---
> > >  hw/i386/pc.c              | 59 +++++++++++++++++++++++++++++------------------
> > >  hw/i386/pc_piix.c         | 14 +----------
> > >  hw/pci-host/piix.c        | 37 +++++++++++++++++++++++++----
> > >  hw/pci-host/q35.c         | 32 +++++++++++++++----------
> > >  include/hw/i386/pc.h      |  5 ++--
> > >  include/hw/pci-host/q35.h |  1 +
> > >  6 files changed, 94 insertions(+), 54 deletions(-)
> > > 
> > > diff --git a/hw/i386/pc.c b/hw/i386/pc.c
> > > index b0b98a8..acaeb6c 100644
> > > --- a/hw/i386/pc.c
> > > +++ b/hw/i386/pc.c
> > > @@ -1003,15 +1003,28 @@ typedef struct PcRomPciInfo {
> > >  static void pc_fw_cfg_guest_info(PcGuestInfo *guest_info)
> > >  {
> > >      PcRomPciInfo *info;
> > > +    Object *pci_info;
> > > +
> > >      if (!guest_info->has_pci_info || !guest_info->fw_cfg) {
> > >          return;
> > >      }
> > > +    pci_info = object_resolve_path("/machine/i440fx", NULL);
> > > +    if (!pci_info) {
> > > +        pci_info = object_resolve_path("/machine/q35", NULL);
> > > +        if (!pci_info) {
> > > +            return;
> > > +        }
> > > +    }
> > 
> > 
> > So is the path /machine/i440fx? /machine/i440FX?
> > /machine/i440FX-pcihost?
> > There's no way to check this code is right without
> > actually running it.
> that drawback of dynamic lookup,
> QOM paths supposed to be stable.
> 
> > 
> > How about i44fx_get_pci_info so we can have this
> > knowledge of paths localized in specific chipset code?
> I've seen objections from afaerber about this approach, so dropped
> this idea.

Could we lookup TYPE_PCI_HOST? This will make pc code
independent of specific machine.
Andreas Färber July 28, 2013, 10:17 a.m. UTC | #5
Am 28.07.2013 11:11, schrieb Michael S. Tsirkin:
> On Sun, Jul 28, 2013 at 10:21:56AM +0200, Igor Mammedov wrote:
>> On Sun, 28 Jul 2013 10:57:12 +0300
>> "Michael S. Tsirkin" <mst@redhat.com> wrote:
>>
>>> On Sun, Jul 28, 2013 at 09:29:13AM +0200, Igor Mammedov wrote:
>>>>  
>>>>      info = g_malloc(sizeof *info);
>>>> -    info->w32_min = cpu_to_le64(guest_info->pci_info.w32.begin);
>>>> -    info->w32_max = cpu_to_le64(guest_info->pci_info.w32.end);
>>>> -    info->w64_min = cpu_to_le64(guest_info->pci_info.w64.begin);
>>>> -    info->w64_max = cpu_to_le64(guest_info->pci_info.w64.end);
>>>> +    info->w32_min = cpu_to_le64(object_property_get_int(pci_info,
>>>> +                                "pci_hole_start", NULL));
>>>> +    info->w32_max = cpu_to_le64(object_property_get_int(pci_info,
>>>> +                                "pci_hole_end", NULL));
>>>
>>> Looks wrong.
>>> object_property_get_int returns a signed int64.
>>> w32 is unsigned.
>>> Happens to work but I think we need an explicit API.

That's how QAPI works internally today for any uint64 visitor/property.
uint64_t is cast to int64_t and back in visitors.

So I'd hope something like
uint64_t val = (uint64_t) object_property_get_int()
would work equally well - CC'ing Michael.

>> I guess it's copy-past error s/cpu_to_le64/cpu_to_le32/
> 
> Not these are 64 bit values, but they need to be
> unsigned not signed.
> 
>> but not need for extra API, with fixed property definition
>> i.e. s/UINT64/UNIT32/ property set code will take care about limits.
> 
> If you replace these with UINT32 you won't be able to
> specify values >4G.
> 
>>> Property names are hard-coded string literals.
>>> Please add macros to set and get them
>>> so we can avoid duplicating code.
>>> E.g.
>> sure.
>>
>>>
>>> #define PCI_HOST_PROPS...
>>> static inline get_
[...]
>>>> @@ -629,6 +648,15 @@ static const char *i440fx_pcihost_root_bus_path(PCIHostState *host_bridge,
>>>>      return "0000";
>>>>  }
>>>>  
>>>> +static Property i440fx_props[] = {
>>>> +    DEFINE_PROP_UINT64("pci_hole64_start", I440FXState, pci_info.w64.begin, 0),
>>>> +    DEFINE_PROP_UINT64("pci_hole64_end", I440FXState, pci_info.w64.end, 0),
>>>> +    DEFINE_PROP_UINT64("pci_hole_start", I440FXState, pci_info.w32.begin, 0),
>>>> +    DEFINE_PROP_UINT64("pci_hole_end", I440FXState, pci_info.w32.end,
>>>> +                       IO_APIC_DEFAULT_ADDRESS),
>>>> +    DEFINE_PROP_END_OF_LIST(),
>>>> +};
>>>> +
>>>
>>> So we have 4 properties. One of them pci_hole64_end
>>> is supposed to be set to a value.
>>> Others should not be touched under any circuimstances.
>>> Of course if you query properties you have no way
>>> to know which is which and what are the legal values.
>>> Ouch.
>> read-only properties are possible but we would have to drop
>> usage DEFINE_PROP_UINT64 of and explicitly use only setter in PropertyInfo,
> 
> Or add DEFINE_PROP_UINT64_RO for this?
> 
>> user better not to touch these properties since they are mostly internal API.
>> but if we say it's internal properties then enforcing read-only might be
>> overkill.
>> For user friendly property "pci_hole64_size" would be nice to have.
> 
> So at the moment I do
> 
> qemu -device i440FX-pcihost,help
> 
> and this will get all properties.
> 
> If we add some properties that user can not set
> they should not appear in this output.
[snip]

Igor, you can simply use dynamic properties with NULL as setter argument
for object_property_add*() to achieve that effect.

Andreas
Igor Mammedov July 28, 2013, 5:33 p.m. UTC | #6
On Sun, 28 Jul 2013 12:11:42 +0300
"Michael S. Tsirkin" <mst@redhat.com> wrote:

> On Sun, Jul 28, 2013 at 10:21:56AM +0200, Igor Mammedov wrote:
> > On Sun, 28 Jul 2013 10:57:12 +0300
> > "Michael S. Tsirkin" <mst@redhat.com> wrote:
> > 
> > > On Sun, Jul 28, 2013 at 09:29:13AM +0200, Igor Mammedov wrote:
> > > > It turns out that some 32 bit windows guests crash
> > > > if 64 bit PCI hole size is >2G.
> > > > Limit it to 2G for piix and q35 by default.
> > > > User may override default boundaries by using
> > > > "pci_hole64_end " property.
> > > > 
> > > > Examples:
> > > > -global i440FX-pcihost.pci_hole64_end=0xffffffffffffffff
> > > > 
> > > > -global q35-pcihost.pci_hole64_end=0xffffffffffffffff
> > > 
> > > IMO that's pretty bad as user interfaces go.
> > > In particular if you are not careful you can make
> > > end < start.
> > > Why not set the size, and include a patch that
> > > let people write "1G" or "2G" for convenience?
> > sure as convenience why not, on top of this patches.
> 
> Well because you are specifying end as 0xffffffffffffffff
> so it's not a multiple of 1G?
> 
> > > 
> > > > Reported-by: Igor Mammedov <imammedo@redhat.com>,
> > > > Signed-off-by: Michael S. Tsirkin <mst@redhat.com>
> > > > Signed-off-by: Igor Mammedov <imammedo@redhat.com>
> > > > ---
> > > >  hw/i386/pc.c              | 59 +++++++++++++++++++++++++++++------------------
> > > >  hw/i386/pc_piix.c         | 14 +----------
> > > >  hw/pci-host/piix.c        | 37 +++++++++++++++++++++++++----
> > > >  hw/pci-host/q35.c         | 32 +++++++++++++++----------
> > > >  include/hw/i386/pc.h      |  5 ++--
> > > >  include/hw/pci-host/q35.h |  1 +
> > > >  6 files changed, 94 insertions(+), 54 deletions(-)
> > > > 
> > > > diff --git a/hw/i386/pc.c b/hw/i386/pc.c
> > > > index b0b98a8..acaeb6c 100644
> > > > --- a/hw/i386/pc.c
> > > > +++ b/hw/i386/pc.c
> > > > @@ -1003,15 +1003,28 @@ typedef struct PcRomPciInfo {
> > > >  static void pc_fw_cfg_guest_info(PcGuestInfo *guest_info)
> > > >  {
> > > >      PcRomPciInfo *info;
> > > > +    Object *pci_info;
> > > > +
> > > >      if (!guest_info->has_pci_info || !guest_info->fw_cfg) {
> > > >          return;
> > > >      }
> > > > +    pci_info = object_resolve_path("/machine/i440fx", NULL);
> > > > +    if (!pci_info) {
> > > > +        pci_info = object_resolve_path("/machine/q35", NULL);
> > > > +        if (!pci_info) {
> > > > +            return;
> > > > +        }
> > > > +    }
> > > 
> > > 
> > > So is the path /machine/i440fx? /machine/i440FX?
> > > /machine/i440FX-pcihost?
> > > There's no way to check this code is right without
> > > actually running it.
> > that drawback of dynamic lookup,
> > QOM paths supposed to be stable.
> > 
> > > 
> > > How about i44fx_get_pci_info so we can have this
> > > knowledge of paths localized in specific chipset code?
> > I've seen objections from afaerber about this approach, so dropped
> > this idea.
> > 
> > > 
> > > >  
> > > >      info = g_malloc(sizeof *info);
> > > > -    info->w32_min = cpu_to_le64(guest_info->pci_info.w32.begin);
> > > > -    info->w32_max = cpu_to_le64(guest_info->pci_info.w32.end);
> > > > -    info->w64_min = cpu_to_le64(guest_info->pci_info.w64.begin);
> > > > -    info->w64_max = cpu_to_le64(guest_info->pci_info.w64.end);
> > > > +    info->w32_min = cpu_to_le64(object_property_get_int(pci_info,
> > > > +                                "pci_hole_start", NULL));
> > > > +    info->w32_max = cpu_to_le64(object_property_get_int(pci_info,
> > > > +                                "pci_hole_end", NULL));
> > > 
> > > Looks wrong.
> > > object_property_get_int returns a signed int64.
> > > w32 is unsigned.
> > > Happens to work but I think we need an explicit API.
> > I guess it's copy-past error s/cpu_to_le64/cpu_to_le32/
> 
> Not these are 64 bit values, but they need to be
> unsigned not signed.
> 
> > but not need for extra API, with fixed property definition
> > i.e. s/UINT64/UNIT32/ property set code will take care about limits.
> 
> If you replace these with UINT32 you won't be able to
> specify values >4G.
does 32 bit PCI hole has right to be more than 4Gb?

> 
> > > 
> > > Property names are hard-coded string literals.
> > > Please add macros to set and get them
> > > so we can avoid duplicating code.
> > > E.g.
> > sure.
> > 
> > > 
> > > #define PCI_HOST_PROPS...
> > > static inline get_
> > > 
> > > 
> > > 
> > > > +    info->w64_min = cpu_to_le64(object_property_get_int(pci_info,
> > > > +                                "pci_hole64_start", NULL));
> > > > +    info->w64_max = cpu_to_le64(object_property_get_int(pci_info,
> > > > +                                "pci_hole64_end", NULL));
> > > >      /* Pass PCI hole info to guest via a side channel.
> > > >       * Required so guest PCI enumeration does the right thing. */
> > > >      fw_cfg_add_file(guest_info->fw_cfg, "etc/pci-info", info, sizeof *info);
> > > > @@ -1037,29 +1050,31 @@ PcGuestInfo *pc_guest_info_init(ram_addr_t below_4g_mem_size,
> > > >      PcGuestInfoState *guest_info_state = g_malloc0(sizeof *guest_info_state);
> > > >      PcGuestInfo *guest_info = &guest_info_state->info;
> > > >  
> > > > -    guest_info->pci_info.w32.end = IO_APIC_DEFAULT_ADDRESS;
> > > > -    if (sizeof(hwaddr) == 4) {
> > > > -        guest_info->pci_info.w64.begin = 0;
> > > > -        guest_info->pci_info.w64.end = 0;
> > > > -    } else {
> > > > -        /*
> > > > -         * BIOS does not set MTRR entries for the 64 bit window, so no need to
> > > > -         * align address to power of two.  Align address at 1G, this makes sure
> > > > -         * it can be exactly covered with a PAT entry even when using huge
> > > > -         * pages.
> > > > -         */
> > > > -        guest_info->pci_info.w64.begin =
> > > > -            ROUND_UP((0x1ULL << 32) + above_4g_mem_size, 0x1ULL << 30);
> > > > -        guest_info->pci_info.w64.end = guest_info->pci_info.w64.begin +
> > > > -            (0x1ULL << 62);
> > > > -        assert(guest_info->pci_info.w64.begin <= guest_info->pci_info.w64.end);
> > > > -    }
> > > > -
> > > >      guest_info_state->machine_done.notify = pc_guest_info_machine_done;
> > > >      qemu_add_machine_init_done_notifier(&guest_info_state->machine_done);
> > > >      return guest_info;
> > > >  }
> > > >  
> > > > +void pc_init_pci64_hole(PcPciInfo *pci_info, uint64_t pci_hole64_start)
> > > > +{
> > > > +    if (sizeof(hwaddr) == 4) {
> > > > +        memset(&pci_info->w64, sizeof(pci_info->w64), 0);
> > > > +        return;
> > > > +    }
> > > > +    /*
> > > > +     * BIOS does not set MTRR entries for the 64 bit window, so no need to
> > > > +     * align address to power of two.  Align address at 1G, this makes sure
> > > > +     * it can be exactly covered with a PAT entry even when using huge
> > > > +     * pages.
> > > > +     */
> > > > +    pci_info->w64.begin = ROUND_UP(pci_hole64_start, 0x1ULL << 30);
> > > > +    if (!pci_info->w64.end) {
> > > > +        /* set default end if it wasn't specified, + 2 Gb past start */
> > > > +        pci_info->w64.end = pci_info->w64.begin + (0x1ULL << 31);
> > > > +    }
> > > > +    assert(pci_info->w64.begin < pci_info->w64.end);
> > > > +}
> > > > +
> > > >  void pc_acpi_init(const char *default_dsdt)
> > > >  {
> > > >      char *filename;
> > > > diff --git a/hw/i386/pc_piix.c b/hw/i386/pc_piix.c
> > > > index b58c255..ab25458 100644
> > > > --- a/hw/i386/pc_piix.c
> > > > +++ b/hw/i386/pc_piix.c
> > > > @@ -129,15 +129,6 @@ static void pc_init1(MemoryRegion *system_memory,
> > > >      guest_info = pc_guest_info_init(below_4g_mem_size, above_4g_mem_size);
> > > >      guest_info->has_pci_info = has_pci_info;
> > > >  
> > > > -    /* Set PCI window size the way seabios has always done it. */
> > > > -    /* Power of 2 so bios can cover it with a single MTRR */
> > > > -    if (ram_size <= 0x80000000)
> > > > -        guest_info->pci_info.w32.begin = 0x80000000;
> > > > -    else if (ram_size <= 0xc0000000)
> > > > -        guest_info->pci_info.w32.begin = 0xc0000000;
> > > > -    else
> > > > -        guest_info->pci_info.w32.begin = 0xe0000000;
> > > > -
> > > >      /* allocate ram and load rom/bios */
> > > >      if (!xen_enabled()) {
> > > >          fw_cfg = pc_memory_init(system_memory,
> > > > @@ -160,10 +151,7 @@ static void pc_init1(MemoryRegion *system_memory,
> > > >                                system_memory, system_io, ram_size,
> > > >                                below_4g_mem_size,
> > > >                                0x100000000ULL - below_4g_mem_size,
> > > > -                              0x100000000ULL + above_4g_mem_size,
> > > > -                              (sizeof(hwaddr) == 4
> > > > -                               ? 0
> > > > -                               : ((uint64_t)1 << 62)),
> > > > +                              above_4g_mem_size,
> > > >                                pci_memory, ram_memory);
> > > >      } else {
> > > >          pci_bus = NULL;
> > > > diff --git a/hw/pci-host/piix.c b/hw/pci-host/piix.c
> > > > index 4624d04..59a42c5 100644
> > > > --- a/hw/pci-host/piix.c
> > > > +++ b/hw/pci-host/piix.c
> > > > @@ -32,6 +32,7 @@
> > > >  #include "hw/xen/xen.h"
> > > >  #include "hw/pci-host/pam.h"
> > > >  #include "sysemu/sysemu.h"
> > > > +#include "hw/i386/ioapic.h"
> > > >  
> > > >  /*
> > > >   * I440FX chipset data sheet.
> > > > @@ -44,6 +45,7 @@
> > > >  
> > > >  typedef struct I440FXState {
> > > >      PCIHostState parent_obj;
> > > > +    PcPciInfo pci_info;
> > > >  } I440FXState;
> > > >  
> > > >  #define PIIX_NUM_PIC_IRQS       16      /* i8259 * 2 */
> > > > @@ -247,8 +249,7 @@ PCIBus *i440fx_init(PCII440FXState **pi440fx_state,
> > > >                      ram_addr_t ram_size,
> > > >                      hwaddr pci_hole_start,
> > > >                      hwaddr pci_hole_size,
> > > > -                    hwaddr pci_hole64_start,
> > > > -                    hwaddr pci_hole64_size,
> > > > +                    ram_addr_t above_4g_mem_size,
> > > >                      MemoryRegion *pci_address_space,
> > > >                      MemoryRegion *ram_memory)
> > > >  {
> > > > @@ -259,6 +260,8 @@ PCIBus *i440fx_init(PCII440FXState **pi440fx_state,
> > > >      PIIX3State *piix3;
> > > >      PCII440FXState *f;
> > > >      unsigned i;
> > > > +    I440FXState *i440fx;
> > > > +    uint64_t pci_hole64_size;
> > > >  
> > > >      dev = qdev_create(NULL, TYPE_I440FX_PCI_HOST);
> > > >      s = PCI_HOST_BRIDGE(dev);
> > > > @@ -274,14 +277,30 @@ PCIBus *i440fx_init(PCII440FXState **pi440fx_state,
> > > >      f->system_memory = address_space_mem;
> > > >      f->pci_address_space = pci_address_space;
> > > >      f->ram_memory = ram_memory;
> > > > +
> > > > +    i440fx = I440FX_PCI_HOST(dev);
> > > > +    /* Set PCI window size the way seabios has always done it. */
> > > > +    /* Power of 2 so bios can cover it with a single MTRR */
> > > > +    if (ram_size <= 0x80000000) {
> > > > +        i440fx->pci_info.w32.begin = 0x80000000;
> > > > +    } else if (ram_size <= 0xc0000000) {
> > > > +        i440fx->pci_info.w32.begin = 0xc0000000;
> > > > +    } else {
> > > > +        i440fx->pci_info.w32.begin = 0xe0000000;
> > > > +    }
> > > > +
> > > >      memory_region_init_alias(&f->pci_hole, OBJECT(d), "pci-hole", f->pci_address_space,
> > > >                               pci_hole_start, pci_hole_size);
> > > >      memory_region_add_subregion(f->system_memory, pci_hole_start, &f->pci_hole);
> > > > +
> > > > +    pc_init_pci64_hole(&i440fx->pci_info, 0x100000000ULL + above_4g_mem_size);
> > > > +    pci_hole64_size = range_size(i440fx->pci_info.w64);
> > > >      memory_region_init_alias(&f->pci_hole_64bit, OBJECT(d), "pci-hole64",
> > > >                               f->pci_address_space,
> > > > -                             pci_hole64_start, pci_hole64_size);
> > > > +                             i440fx->pci_info.w64.begin, pci_hole64_size);
> > > >      if (pci_hole64_size) {
> > > > -        memory_region_add_subregion(f->system_memory, pci_hole64_start,
> > > > +        memory_region_add_subregion(f->system_memory,
> > > > +                                    i440fx->pci_info.w64.begin,
> > > >                                      &f->pci_hole_64bit);
> > > >      }
> > > >      memory_region_init_alias(&f->smram_region, OBJECT(d), "smram-region",
> > > > @@ -629,6 +648,15 @@ static const char *i440fx_pcihost_root_bus_path(PCIHostState *host_bridge,
> > > >      return "0000";
> > > >  }
> > > >  
> > > > +static Property i440fx_props[] = {
> > > > +    DEFINE_PROP_UINT64("pci_hole64_start", I440FXState, pci_info.w64.begin, 0),
> > > > +    DEFINE_PROP_UINT64("pci_hole64_end", I440FXState, pci_info.w64.end, 0),
> > > > +    DEFINE_PROP_UINT64("pci_hole_start", I440FXState, pci_info.w32.begin, 0),
> > > > +    DEFINE_PROP_UINT64("pci_hole_end", I440FXState, pci_info.w32.end,
> > > > +                       IO_APIC_DEFAULT_ADDRESS),
> > > > +    DEFINE_PROP_END_OF_LIST(),
> > > > +};
> > > > +
> > > 
> > > So we have 4 properties. One of them pci_hole64_end
> > > is supposed to be set to a value.
> > > Others should not be touched under any circuimstances.
> > > Of course if you query properties you have no way
> > > to know which is which and what are the legal values.
> > > Ouch.
> > read-only properties are possible but we would have to drop
> > usage DEFINE_PROP_UINT64 of and explicitly use only setter in PropertyInfo,
> 
> Or add DEFINE_PROP_UINT64_RO for this?
it might be the way to do it.

> 
> > user better not to touch these properties since they are mostly internal API.
> > but if we say it's internal properties then enforcing read-only might be
> > overkill.
> > For user friendly property "pci_hole64_size" would be nice to have.
> 
> So at the moment I do
> 
> qemu -device i440FX-pcihost,help
> 
> and this will get all properties.
> 
> If we add some properties that user can not set
> they should not appear in this output.
with QOM all around I wouldn't say so, it could be property only for internal
purposes and QOM properties do not care about whether they are visible or
not to user so far. I guess we could document it in code like do not
touch /internal or ...


> 
> 
> > > 
> > > >  static void i440fx_pcihost_class_init(ObjectClass *klass, void *data)
> > > >  {
> > > >      DeviceClass *dc = DEVICE_CLASS(klass);
> > > > @@ -638,6 +666,7 @@ static void i440fx_pcihost_class_init(ObjectClass *klass, void *data)
> > > >      dc->realize = i440fx_pcihost_realize;
> > > >      dc->fw_name = "pci";
> > > >      dc->no_user = 1;
> > > > +    dc->props = i440fx_props;
> > > >  }
> > > >  
> > > >  static const TypeInfo i440fx_pcihost_info = {
> > > > diff --git a/hw/pci-host/q35.c b/hw/pci-host/q35.c
> > > > index 6b1b3b7..a6936e6 100644
> > > > --- a/hw/pci-host/q35.c
> > > > +++ b/hw/pci-host/q35.c
> > > > @@ -67,6 +67,21 @@ static const char *q35_host_root_bus_path(PCIHostState *host_bridge,
> > > >  static Property mch_props[] = {
> > > >      DEFINE_PROP_UINT64("MCFG", Q35PCIHost, parent_obj.base_addr,
> > > >                          MCH_HOST_BRIDGE_PCIEXBAR_DEFAULT),
> > > > +    DEFINE_PROP_UINT64("pci_hole64_start", Q35PCIHost,
> > > > +                       mch.pci_info.w64.begin, 0),
> > > > +    DEFINE_PROP_UINT64("pci_hole64_end", Q35PCIHost,
> > > > +                       mch.pci_info.w64.end, 0),
> > > > +    /* Leave enough space for the biggest MCFG BAR */
> > > > +    /* TODO: this matches current bios behaviour, but
> > > > +     * it's not a power of two, which means an MTRR
> > > > +     * can't cover it exactly.
> > > > +     */
> > > > +    DEFINE_PROP_UINT64("pci_hole_start", Q35PCIHost,
> > > > +                       mch.pci_info.w32.begin,
> > > > +                       MCH_HOST_BRIDGE_PCIEXBAR_DEFAULT +
> > > > +                       MCH_HOST_BRIDGE_PCIEXBAR_MAX),
> > > > +    DEFINE_PROP_UINT64("pci_hole_end", Q35PCIHost,
> > > > +                       mch.pci_info.w32.end, IO_APIC_DEFAULT_ADDRESS),
> > > >      DEFINE_PROP_END_OF_LIST(),
> > > >  };
> > > >  
> > > > @@ -255,14 +270,6 @@ static int mch_init(PCIDevice *d)
> > > >      hwaddr pci_hole64_size;
> > > >      MCHPCIState *mch = MCH_PCI_DEVICE(d);
> > > >  
> > > > -    /* Leave enough space for the biggest MCFG BAR */
> > > > -    /* TODO: this matches current bios behaviour, but
> > > > -     * it's not a power of two, which means an MTRR
> > > > -     * can't cover it exactly.
> > > > -     */
> > > > -    mch->guest_info->pci_info.w32.begin = MCH_HOST_BRIDGE_PCIEXBAR_DEFAULT +
> > > > -        MCH_HOST_BRIDGE_PCIEXBAR_MAX;
> > > > -
> > > >      /* setup pci memory regions */
> > > >      memory_region_init_alias(&mch->pci_hole, OBJECT(mch), "pci-hole",
> > > >                               mch->pci_address_space,
> > > > @@ -270,15 +277,16 @@ static int mch_init(PCIDevice *d)
> > > >                               0x100000000ULL - mch->below_4g_mem_size);
> > > >      memory_region_add_subregion(mch->system_memory, mch->below_4g_mem_size,
> > > >                                  &mch->pci_hole);
> > > > -    pci_hole64_size = (sizeof(hwaddr) == 4 ? 0 :
> > > > -                       ((uint64_t)1 << 62));
> > > > +
> > > > +    pc_init_pci64_hole(&mch->pci_info, 0x100000000ULL + mch->above_4g_mem_size);
> > > > +    pci_hole64_size = range_size(mch->pci_info.w64);
> > > >      memory_region_init_alias(&mch->pci_hole_64bit, OBJECT(mch), "pci-hole64",
> > > >                               mch->pci_address_space,
> > > > -                             0x100000000ULL + mch->above_4g_mem_size,
> > > > +                             mch->pci_info.w64.begin,
> > > >                               pci_hole64_size);
> > > >      if (pci_hole64_size) {
> > > >          memory_region_add_subregion(mch->system_memory,
> > > > -                                    0x100000000ULL + mch->above_4g_mem_size,
> > > > +                                    mch->pci_info.w64.begin,
> > > >                                      &mch->pci_hole_64bit);
> > > >      }
> > > >      /* smram */
> > > > diff --git a/include/hw/i386/pc.h b/include/hw/i386/pc.h
> > > > index 7fb97b0..ab747b7 100644
> > > > --- a/include/hw/i386/pc.h
> > > > +++ b/include/hw/i386/pc.h
> > > > @@ -18,7 +18,6 @@ typedef struct PcPciInfo {
> > > >  } PcPciInfo;
> > > >  
> > > >  struct PcGuestInfo {
> > > > -    PcPciInfo pci_info;
> > > >      bool has_pci_info;
> > > >      FWCfgState *fw_cfg;
> > > >  };
> > > > @@ -100,6 +99,7 @@ void pc_acpi_init(const char *default_dsdt);
> > > >  
> > > >  PcGuestInfo *pc_guest_info_init(ram_addr_t below_4g_mem_size,
> > > >                                  ram_addr_t above_4g_mem_size);
> > > > +void pc_init_pci64_hole(PcPciInfo *pci_info, uint64_t pci_hole64_start);
> > > >  
> > > >  FWCfgState *pc_memory_init(MemoryRegion *system_memory,
> > > >                             const char *kernel_filename,
> > > > @@ -150,8 +150,7 @@ PCIBus *i440fx_init(PCII440FXState **pi440fx_state, int *piix_devfn,
> > > >                      ram_addr_t ram_size,
> > > >                      hwaddr pci_hole_start,
> > > >                      hwaddr pci_hole_size,
> > > > -                    hwaddr pci_hole64_start,
> > > > -                    hwaddr pci_hole64_size,
> > > > +                    ram_addr_t above_4g_mem_size,
> > > >                      MemoryRegion *pci_memory,
> > > >                      MemoryRegion *ram_memory);
> > > >  
> > > > diff --git a/include/hw/pci-host/q35.h b/include/hw/pci-host/q35.h
> > > > index 3cb631e..3a9e04b 100644
> > > > --- a/include/hw/pci-host/q35.h
> > > > +++ b/include/hw/pci-host/q35.h
> > > > @@ -55,6 +55,7 @@ typedef struct MCHPCIState {
> > > >      MemoryRegion smram_region;
> > > >      MemoryRegion pci_hole;
> > > >      MemoryRegion pci_hole_64bit;
> > > > +    PcPciInfo pci_info;
> > > >      uint8_t smm_enabled;
> > > >      ram_addr_t below_4g_mem_size;
> > > >      ram_addr_t above_4g_mem_size;
> > > > -- 
> > > > 1.8.3.1
> > > 
> > 
> > 
> > -- 
> > Regards,
> >   Igor
>
Igor Mammedov July 28, 2013, 5:34 p.m. UTC | #7
On Sun, 28 Jul 2013 12:13:17 +0300
"Michael S. Tsirkin" <mst@redhat.com> wrote:

> On Sun, Jul 28, 2013 at 10:21:56AM +0200, Igor Mammedov wrote:
> > On Sun, 28 Jul 2013 10:57:12 +0300
> > "Michael S. Tsirkin" <mst@redhat.com> wrote:
> > 
> > > On Sun, Jul 28, 2013 at 09:29:13AM +0200, Igor Mammedov wrote:
> > > > It turns out that some 32 bit windows guests crash
> > > > if 64 bit PCI hole size is >2G.
> > > > Limit it to 2G for piix and q35 by default.
> > > > User may override default boundaries by using
> > > > "pci_hole64_end " property.
> > > > 
> > > > Examples:
> > > > -global i440FX-pcihost.pci_hole64_end=0xffffffffffffffff
> > > > 
> > > > -global q35-pcihost.pci_hole64_end=0xffffffffffffffff
> > > 
> > > IMO that's pretty bad as user interfaces go.
> > > In particular if you are not careful you can make
> > > end < start.
> > > Why not set the size, and include a patch that
> > > let people write "1G" or "2G" for convenience?
> > sure as convenience why not, on top of this patches.
> >  
> > > 
> > > > Reported-by: Igor Mammedov <imammedo@redhat.com>,
> > > > Signed-off-by: Michael S. Tsirkin <mst@redhat.com>
> > > > Signed-off-by: Igor Mammedov <imammedo@redhat.com>
> > > > ---
> > > >  hw/i386/pc.c              | 59 +++++++++++++++++++++++++++++------------------
> > > >  hw/i386/pc_piix.c         | 14 +----------
> > > >  hw/pci-host/piix.c        | 37 +++++++++++++++++++++++++----
> > > >  hw/pci-host/q35.c         | 32 +++++++++++++++----------
> > > >  include/hw/i386/pc.h      |  5 ++--
> > > >  include/hw/pci-host/q35.h |  1 +
> > > >  6 files changed, 94 insertions(+), 54 deletions(-)
> > > > 
> > > > diff --git a/hw/i386/pc.c b/hw/i386/pc.c
> > > > index b0b98a8..acaeb6c 100644
> > > > --- a/hw/i386/pc.c
> > > > +++ b/hw/i386/pc.c
> > > > @@ -1003,15 +1003,28 @@ typedef struct PcRomPciInfo {
> > > >  static void pc_fw_cfg_guest_info(PcGuestInfo *guest_info)
> > > >  {
> > > >      PcRomPciInfo *info;
> > > > +    Object *pci_info;
> > > > +
> > > >      if (!guest_info->has_pci_info || !guest_info->fw_cfg) {
> > > >          return;
> > > >      }
> > > > +    pci_info = object_resolve_path("/machine/i440fx", NULL);
> > > > +    if (!pci_info) {
> > > > +        pci_info = object_resolve_path("/machine/q35", NULL);
> > > > +        if (!pci_info) {
> > > > +            return;
> > > > +        }
> > > > +    }
> > > 
> > > 
> > > So is the path /machine/i440fx? /machine/i440FX?
> > > /machine/i440FX-pcihost?
> > > There's no way to check this code is right without
> > > actually running it.
> > that drawback of dynamic lookup,
> > QOM paths supposed to be stable.
> > 
> > > 
> > > How about i44fx_get_pci_info so we can have this
> > > knowledge of paths localized in specific chipset code?
> > I've seen objections from afaerber about this approach, so dropped
> > this idea.
> 
> Could we lookup TYPE_PCI_HOST? This will make pc code
> independent of specific machine.
sure, thanks for idea.

> 
> -- 
> MST
Igor Mammedov July 28, 2013, 5:40 p.m. UTC | #8
On Sun, 28 Jul 2013 12:17:47 +0200
Andreas Färber <afaerber@suse.de> wrote:

> Am 28.07.2013 11:11, schrieb Michael S. Tsirkin:
> > On Sun, Jul 28, 2013 at 10:21:56AM +0200, Igor Mammedov wrote:
> >> On Sun, 28 Jul 2013 10:57:12 +0300
> >> "Michael S. Tsirkin" <mst@redhat.com> wrote:
> >>
> >>> On Sun, Jul 28, 2013 at 09:29:13AM +0200, Igor Mammedov wrote:
> >>>>  
> >>>>      info = g_malloc(sizeof *info);
> >>>> -    info->w32_min = cpu_to_le64(guest_info->pci_info.w32.begin);
> >>>> -    info->w32_max = cpu_to_le64(guest_info->pci_info.w32.end);
> >>>> -    info->w64_min = cpu_to_le64(guest_info->pci_info.w64.begin);
> >>>> -    info->w64_max = cpu_to_le64(guest_info->pci_info.w64.end);
> >>>> +    info->w32_min = cpu_to_le64(object_property_get_int(pci_info,
> >>>> +                                "pci_hole_start", NULL));
> >>>> +    info->w32_max = cpu_to_le64(object_property_get_int(pci_info,
> >>>> +                                "pci_hole_end", NULL));
> >>>
> >>> Looks wrong.
> >>> object_property_get_int returns a signed int64.
> >>> w32 is unsigned.
> >>> Happens to work but I think we need an explicit API.
> 
> That's how QAPI works internally today for any uint64 visitor/property.
> uint64_t is cast to int64_t and back in visitors.
> 
> So I'd hope something like
> uint64_t val = (uint64_t) object_property_get_int()
> would work equally well - CC'ing Michael.
> 
> >> I guess it's copy-past error s/cpu_to_le64/cpu_to_le32/
> > 
> > Not these are 64 bit values, but they need to be
> > unsigned not signed.
> > 
> >> but not need for extra API, with fixed property definition
> >> i.e. s/UINT64/UNIT32/ property set code will take care about limits.
> > 
> > If you replace these with UINT32 you won't be able to
> > specify values >4G.
> > 
> >>> Property names are hard-coded string literals.
> >>> Please add macros to set and get them
> >>> so we can avoid duplicating code.
> >>> E.g.
> >> sure.
> >>
> >>>
> >>> #define PCI_HOST_PROPS...
> >>> static inline get_
> [...]
> >>>> @@ -629,6 +648,15 @@ static const char *i440fx_pcihost_root_bus_path(PCIHostState *host_bridge,
> >>>>      return "0000";
> >>>>  }
> >>>>  
> >>>> +static Property i440fx_props[] = {
> >>>> +    DEFINE_PROP_UINT64("pci_hole64_start", I440FXState, pci_info.w64.begin, 0),
> >>>> +    DEFINE_PROP_UINT64("pci_hole64_end", I440FXState, pci_info.w64.end, 0),
> >>>> +    DEFINE_PROP_UINT64("pci_hole_start", I440FXState, pci_info.w32.begin, 0),
> >>>> +    DEFINE_PROP_UINT64("pci_hole_end", I440FXState, pci_info.w32.end,
> >>>> +                       IO_APIC_DEFAULT_ADDRESS),
> >>>> +    DEFINE_PROP_END_OF_LIST(),
> >>>> +};
> >>>> +
> >>>
> >>> So we have 4 properties. One of them pci_hole64_end
> >>> is supposed to be set to a value.
> >>> Others should not be touched under any circuimstances.
> >>> Of course if you query properties you have no way
> >>> to know which is which and what are the legal values.
> >>> Ouch.
> >> read-only properties are possible but we would have to drop
> >> usage DEFINE_PROP_UINT64 of and explicitly use only setter in PropertyInfo,
> > 
> > Or add DEFINE_PROP_UINT64_RO for this?
> > 
> >> user better not to touch these properties since they are mostly internal API.
> >> but if we say it's internal properties then enforcing read-only might be
> >> overkill.
> >> For user friendly property "pci_hole64_size" would be nice to have.
> > 
> > So at the moment I do
> > 
> > qemu -device i440FX-pcihost,help
> > 
> > and this will get all properties.
> > 
> > If we add some properties that user can not set
> > they should not appear in this output.
> [snip]
> 
> Igor, you can simply use dynamic properties with NULL as setter argument
> for object_property_add*() to achieve that effect.
Yes, I can but it's more boiler plate code just for restricting single
property. And if we have "pci_hole64_size"/default then user set
"pci_hole64_end" would not have any effect, since "pci_hole64_size"
would override it.

> Andreas
> 
> -- 
> SUSE LINUX Products GmbH, Maxfeldstr. 5, 90409 Nürnberg, Germany
> GF: Jeff Hawn, Jennifer Guild, Felix Imendörffer; HRB 16746 AG Nürnberg
Michael S. Tsirkin July 28, 2013, 7:48 p.m. UTC | #9
On Sun, Jul 28, 2013 at 07:40:32PM +0200, Igor Mammedov wrote:
> On Sun, 28 Jul 2013 12:17:47 +0200
> Andreas Färber <afaerber@suse.de> wrote:
> 
> > Am 28.07.2013 11:11, schrieb Michael S. Tsirkin:
> > > On Sun, Jul 28, 2013 at 10:21:56AM +0200, Igor Mammedov wrote:
> > >> On Sun, 28 Jul 2013 10:57:12 +0300
> > >> "Michael S. Tsirkin" <mst@redhat.com> wrote:
> > >>
> > >>> On Sun, Jul 28, 2013 at 09:29:13AM +0200, Igor Mammedov wrote:
> > >>>>  
> > >>>>      info = g_malloc(sizeof *info);
> > >>>> -    info->w32_min = cpu_to_le64(guest_info->pci_info.w32.begin);
> > >>>> -    info->w32_max = cpu_to_le64(guest_info->pci_info.w32.end);
> > >>>> -    info->w64_min = cpu_to_le64(guest_info->pci_info.w64.begin);
> > >>>> -    info->w64_max = cpu_to_le64(guest_info->pci_info.w64.end);
> > >>>> +    info->w32_min = cpu_to_le64(object_property_get_int(pci_info,
> > >>>> +                                "pci_hole_start", NULL));
> > >>>> +    info->w32_max = cpu_to_le64(object_property_get_int(pci_info,
> > >>>> +                                "pci_hole_end", NULL));
> > >>>
> > >>> Looks wrong.
> > >>> object_property_get_int returns a signed int64.
> > >>> w32 is unsigned.
> > >>> Happens to work but I think we need an explicit API.
> > 
> > That's how QAPI works internally today for any uint64 visitor/property.
> > uint64_t is cast to int64_t and back in visitors.
> > 
> > So I'd hope something like
> > uint64_t val = (uint64_t) object_property_get_int()
> > would work equally well - CC'ing Michael.
> > 
> > >> I guess it's copy-past error s/cpu_to_le64/cpu_to_le32/
> > > 
> > > Not these are 64 bit values, but they need to be
> > > unsigned not signed.
> > > 
> > >> but not need for extra API, with fixed property definition
> > >> i.e. s/UINT64/UNIT32/ property set code will take care about limits.
> > > 
> > > If you replace these with UINT32 you won't be able to
> > > specify values >4G.
> > > 
> > >>> Property names are hard-coded string literals.
> > >>> Please add macros to set and get them
> > >>> so we can avoid duplicating code.
> > >>> E.g.
> > >> sure.
> > >>
> > >>>
> > >>> #define PCI_HOST_PROPS...
> > >>> static inline get_
> > [...]
> > >>>> @@ -629,6 +648,15 @@ static const char *i440fx_pcihost_root_bus_path(PCIHostState *host_bridge,
> > >>>>      return "0000";
> > >>>>  }
> > >>>>  
> > >>>> +static Property i440fx_props[] = {
> > >>>> +    DEFINE_PROP_UINT64("pci_hole64_start", I440FXState, pci_info.w64.begin, 0),
> > >>>> +    DEFINE_PROP_UINT64("pci_hole64_end", I440FXState, pci_info.w64.end, 0),
> > >>>> +    DEFINE_PROP_UINT64("pci_hole_start", I440FXState, pci_info.w32.begin, 0),
> > >>>> +    DEFINE_PROP_UINT64("pci_hole_end", I440FXState, pci_info.w32.end,
> > >>>> +                       IO_APIC_DEFAULT_ADDRESS),
> > >>>> +    DEFINE_PROP_END_OF_LIST(),
> > >>>> +};
> > >>>> +
> > >>>
> > >>> So we have 4 properties. One of them pci_hole64_end
> > >>> is supposed to be set to a value.
> > >>> Others should not be touched under any circuimstances.
> > >>> Of course if you query properties you have no way
> > >>> to know which is which and what are the legal values.
> > >>> Ouch.
> > >> read-only properties are possible but we would have to drop
> > >> usage DEFINE_PROP_UINT64 of and explicitly use only setter in PropertyInfo,
> > > 
> > > Or add DEFINE_PROP_UINT64_RO for this?
> > > 
> > >> user better not to touch these properties since they are mostly internal API.
> > >> but if we say it's internal properties then enforcing read-only might be
> > >> overkill.
> > >> For user friendly property "pci_hole64_size" would be nice to have.
> > > 
> > > So at the moment I do
> > > 
> > > qemu -device i440FX-pcihost,help
> > > 
> > > and this will get all properties.
> > > 
> > > If we add some properties that user can not set
> > > they should not appear in this output.
> > [snip]
> > 
> > Igor, you can simply use dynamic properties with NULL as setter argument
> > for object_property_add*() to achieve that effect.
> Yes, I can but it's more boiler plate code just for restricting single
> property. And if we have "pci_hole64_size"/default then user set
> "pci_hole64_end" would not have any effect, since "pci_hole64_size"
> would override it.

I don't think we want user to control low level properties such and
_start and _end.  _size might be unavoidable but let's limit
it to that.


> > Andreas
> > 
> > -- 
> > SUSE LINUX Products GmbH, Maxfeldstr. 5, 90409 Nürnberg, Germany
> > GF: Jeff Hawn, Jennifer Guild, Felix Imendörffer; HRB 16746 AG Nürnberg
> 
> 
> -- 
> Regards,
>   Igor
Michael S. Tsirkin July 28, 2013, 7:51 p.m. UTC | #10
On Sun, Jul 28, 2013 at 07:33:27PM +0200, Igor Mammedov wrote:
> On Sun, 28 Jul 2013 12:11:42 +0300
> "Michael S. Tsirkin" <mst@redhat.com> wrote:
> 
> > On Sun, Jul 28, 2013 at 10:21:56AM +0200, Igor Mammedov wrote:
> > > On Sun, 28 Jul 2013 10:57:12 +0300
> > > "Michael S. Tsirkin" <mst@redhat.com> wrote:
> > > 
> > > > On Sun, Jul 28, 2013 at 09:29:13AM +0200, Igor Mammedov wrote:
> > > > > It turns out that some 32 bit windows guests crash
> > > > > if 64 bit PCI hole size is >2G.
> > > > > Limit it to 2G for piix and q35 by default.
> > > > > User may override default boundaries by using
> > > > > "pci_hole64_end " property.
> > > > > 
> > > > > Examples:
> > > > > -global i440FX-pcihost.pci_hole64_end=0xffffffffffffffff
> > > > > 
> > > > > -global q35-pcihost.pci_hole64_end=0xffffffffffffffff
> > > > 
> > > > IMO that's pretty bad as user interfaces go.
> > > > In particular if you are not careful you can make
> > > > end < start.
> > > > Why not set the size, and include a patch that
> > > > let people write "1G" or "2G" for convenience?
> > > sure as convenience why not, on top of this patches.
> > 
> > Well because you are specifying end as 0xffffffffffffffff
> > so it's not a multiple of 1G?
> > 
> > > > 
> > > > > Reported-by: Igor Mammedov <imammedo@redhat.com>,
> > > > > Signed-off-by: Michael S. Tsirkin <mst@redhat.com>
> > > > > Signed-off-by: Igor Mammedov <imammedo@redhat.com>
> > > > > ---
> > > > >  hw/i386/pc.c              | 59 +++++++++++++++++++++++++++++------------------
> > > > >  hw/i386/pc_piix.c         | 14 +----------
> > > > >  hw/pci-host/piix.c        | 37 +++++++++++++++++++++++++----
> > > > >  hw/pci-host/q35.c         | 32 +++++++++++++++----------
> > > > >  include/hw/i386/pc.h      |  5 ++--
> > > > >  include/hw/pci-host/q35.h |  1 +
> > > > >  6 files changed, 94 insertions(+), 54 deletions(-)
> > > > > 
> > > > > diff --git a/hw/i386/pc.c b/hw/i386/pc.c
> > > > > index b0b98a8..acaeb6c 100644
> > > > > --- a/hw/i386/pc.c
> > > > > +++ b/hw/i386/pc.c
> > > > > @@ -1003,15 +1003,28 @@ typedef struct PcRomPciInfo {
> > > > >  static void pc_fw_cfg_guest_info(PcGuestInfo *guest_info)
> > > > >  {
> > > > >      PcRomPciInfo *info;
> > > > > +    Object *pci_info;
> > > > > +
> > > > >      if (!guest_info->has_pci_info || !guest_info->fw_cfg) {
> > > > >          return;
> > > > >      }
> > > > > +    pci_info = object_resolve_path("/machine/i440fx", NULL);
> > > > > +    if (!pci_info) {
> > > > > +        pci_info = object_resolve_path("/machine/q35", NULL);
> > > > > +        if (!pci_info) {
> > > > > +            return;
> > > > > +        }
> > > > > +    }
> > > > 
> > > > 
> > > > So is the path /machine/i440fx? /machine/i440FX?
> > > > /machine/i440FX-pcihost?
> > > > There's no way to check this code is right without
> > > > actually running it.
> > > that drawback of dynamic lookup,
> > > QOM paths supposed to be stable.
> > > 
> > > > 
> > > > How about i44fx_get_pci_info so we can have this
> > > > knowledge of paths localized in specific chipset code?
> > > I've seen objections from afaerber about this approach, so dropped
> > > this idea.
> > > 
> > > > 
> > > > >  
> > > > >      info = g_malloc(sizeof *info);
> > > > > -    info->w32_min = cpu_to_le64(guest_info->pci_info.w32.begin);
> > > > > -    info->w32_max = cpu_to_le64(guest_info->pci_info.w32.end);
> > > > > -    info->w64_min = cpu_to_le64(guest_info->pci_info.w64.begin);
> > > > > -    info->w64_max = cpu_to_le64(guest_info->pci_info.w64.end);
> > > > > +    info->w32_min = cpu_to_le64(object_property_get_int(pci_info,
> > > > > +                                "pci_hole_start", NULL));
> > > > > +    info->w32_max = cpu_to_le64(object_property_get_int(pci_info,
> > > > > +                                "pci_hole_end", NULL));
> > > > 
> > > > Looks wrong.
> > > > object_property_get_int returns a signed int64.
> > > > w32 is unsigned.
> > > > Happens to work but I think we need an explicit API.
> > > I guess it's copy-past error s/cpu_to_le64/cpu_to_le32/
> > 
> > Not these are 64 bit values, but they need to be
> > unsigned not signed.
> > 
> > > but not need for extra API, with fixed property definition
> > > i.e. s/UINT64/UNIT32/ property set code will take care about limits.
> > 
> > If you replace these with UINT32 you won't be able to
> > specify values >4G.
> does 32 bit PCI hole has right to be more than 4Gb?

No but the 64 bit one does. 32 one shouldn't be user
controllable at all.

> > 
> > > > 
> > > > Property names are hard-coded string literals.
> > > > Please add macros to set and get them
> > > > so we can avoid duplicating code.
> > > > E.g.
> > > sure.
> > > 
> > > > 
> > > > #define PCI_HOST_PROPS...
> > > > static inline get_
> > > > 
> > > > 
> > > > 
> > > > > +    info->w64_min = cpu_to_le64(object_property_get_int(pci_info,
> > > > > +                                "pci_hole64_start", NULL));
> > > > > +    info->w64_max = cpu_to_le64(object_property_get_int(pci_info,
> > > > > +                                "pci_hole64_end", NULL));
> > > > >      /* Pass PCI hole info to guest via a side channel.
> > > > >       * Required so guest PCI enumeration does the right thing. */
> > > > >      fw_cfg_add_file(guest_info->fw_cfg, "etc/pci-info", info, sizeof *info);
> > > > > @@ -1037,29 +1050,31 @@ PcGuestInfo *pc_guest_info_init(ram_addr_t below_4g_mem_size,
> > > > >      PcGuestInfoState *guest_info_state = g_malloc0(sizeof *guest_info_state);
> > > > >      PcGuestInfo *guest_info = &guest_info_state->info;
> > > > >  
> > > > > -    guest_info->pci_info.w32.end = IO_APIC_DEFAULT_ADDRESS;
> > > > > -    if (sizeof(hwaddr) == 4) {
> > > > > -        guest_info->pci_info.w64.begin = 0;
> > > > > -        guest_info->pci_info.w64.end = 0;
> > > > > -    } else {
> > > > > -        /*
> > > > > -         * BIOS does not set MTRR entries for the 64 bit window, so no need to
> > > > > -         * align address to power of two.  Align address at 1G, this makes sure
> > > > > -         * it can be exactly covered with a PAT entry even when using huge
> > > > > -         * pages.
> > > > > -         */
> > > > > -        guest_info->pci_info.w64.begin =
> > > > > -            ROUND_UP((0x1ULL << 32) + above_4g_mem_size, 0x1ULL << 30);
> > > > > -        guest_info->pci_info.w64.end = guest_info->pci_info.w64.begin +
> > > > > -            (0x1ULL << 62);
> > > > > -        assert(guest_info->pci_info.w64.begin <= guest_info->pci_info.w64.end);
> > > > > -    }
> > > > > -
> > > > >      guest_info_state->machine_done.notify = pc_guest_info_machine_done;
> > > > >      qemu_add_machine_init_done_notifier(&guest_info_state->machine_done);
> > > > >      return guest_info;
> > > > >  }
> > > > >  
> > > > > +void pc_init_pci64_hole(PcPciInfo *pci_info, uint64_t pci_hole64_start)
> > > > > +{
> > > > > +    if (sizeof(hwaddr) == 4) {
> > > > > +        memset(&pci_info->w64, sizeof(pci_info->w64), 0);
> > > > > +        return;
> > > > > +    }
> > > > > +    /*
> > > > > +     * BIOS does not set MTRR entries for the 64 bit window, so no need to
> > > > > +     * align address to power of two.  Align address at 1G, this makes sure
> > > > > +     * it can be exactly covered with a PAT entry even when using huge
> > > > > +     * pages.
> > > > > +     */
> > > > > +    pci_info->w64.begin = ROUND_UP(pci_hole64_start, 0x1ULL << 30);
> > > > > +    if (!pci_info->w64.end) {
> > > > > +        /* set default end if it wasn't specified, + 2 Gb past start */
> > > > > +        pci_info->w64.end = pci_info->w64.begin + (0x1ULL << 31);
> > > > > +    }
> > > > > +    assert(pci_info->w64.begin < pci_info->w64.end);
> > > > > +}
> > > > > +
> > > > >  void pc_acpi_init(const char *default_dsdt)
> > > > >  {
> > > > >      char *filename;
> > > > > diff --git a/hw/i386/pc_piix.c b/hw/i386/pc_piix.c
> > > > > index b58c255..ab25458 100644
> > > > > --- a/hw/i386/pc_piix.c
> > > > > +++ b/hw/i386/pc_piix.c
> > > > > @@ -129,15 +129,6 @@ static void pc_init1(MemoryRegion *system_memory,
> > > > >      guest_info = pc_guest_info_init(below_4g_mem_size, above_4g_mem_size);
> > > > >      guest_info->has_pci_info = has_pci_info;
> > > > >  
> > > > > -    /* Set PCI window size the way seabios has always done it. */
> > > > > -    /* Power of 2 so bios can cover it with a single MTRR */
> > > > > -    if (ram_size <= 0x80000000)
> > > > > -        guest_info->pci_info.w32.begin = 0x80000000;
> > > > > -    else if (ram_size <= 0xc0000000)
> > > > > -        guest_info->pci_info.w32.begin = 0xc0000000;
> > > > > -    else
> > > > > -        guest_info->pci_info.w32.begin = 0xe0000000;
> > > > > -
> > > > >      /* allocate ram and load rom/bios */
> > > > >      if (!xen_enabled()) {
> > > > >          fw_cfg = pc_memory_init(system_memory,
> > > > > @@ -160,10 +151,7 @@ static void pc_init1(MemoryRegion *system_memory,
> > > > >                                system_memory, system_io, ram_size,
> > > > >                                below_4g_mem_size,
> > > > >                                0x100000000ULL - below_4g_mem_size,
> > > > > -                              0x100000000ULL + above_4g_mem_size,
> > > > > -                              (sizeof(hwaddr) == 4
> > > > > -                               ? 0
> > > > > -                               : ((uint64_t)1 << 62)),
> > > > > +                              above_4g_mem_size,
> > > > >                                pci_memory, ram_memory);
> > > > >      } else {
> > > > >          pci_bus = NULL;
> > > > > diff --git a/hw/pci-host/piix.c b/hw/pci-host/piix.c
> > > > > index 4624d04..59a42c5 100644
> > > > > --- a/hw/pci-host/piix.c
> > > > > +++ b/hw/pci-host/piix.c
> > > > > @@ -32,6 +32,7 @@
> > > > >  #include "hw/xen/xen.h"
> > > > >  #include "hw/pci-host/pam.h"
> > > > >  #include "sysemu/sysemu.h"
> > > > > +#include "hw/i386/ioapic.h"
> > > > >  
> > > > >  /*
> > > > >   * I440FX chipset data sheet.
> > > > > @@ -44,6 +45,7 @@
> > > > >  
> > > > >  typedef struct I440FXState {
> > > > >      PCIHostState parent_obj;
> > > > > +    PcPciInfo pci_info;
> > > > >  } I440FXState;
> > > > >  
> > > > >  #define PIIX_NUM_PIC_IRQS       16      /* i8259 * 2 */
> > > > > @@ -247,8 +249,7 @@ PCIBus *i440fx_init(PCII440FXState **pi440fx_state,
> > > > >                      ram_addr_t ram_size,
> > > > >                      hwaddr pci_hole_start,
> > > > >                      hwaddr pci_hole_size,
> > > > > -                    hwaddr pci_hole64_start,
> > > > > -                    hwaddr pci_hole64_size,
> > > > > +                    ram_addr_t above_4g_mem_size,
> > > > >                      MemoryRegion *pci_address_space,
> > > > >                      MemoryRegion *ram_memory)
> > > > >  {
> > > > > @@ -259,6 +260,8 @@ PCIBus *i440fx_init(PCII440FXState **pi440fx_state,
> > > > >      PIIX3State *piix3;
> > > > >      PCII440FXState *f;
> > > > >      unsigned i;
> > > > > +    I440FXState *i440fx;
> > > > > +    uint64_t pci_hole64_size;
> > > > >  
> > > > >      dev = qdev_create(NULL, TYPE_I440FX_PCI_HOST);
> > > > >      s = PCI_HOST_BRIDGE(dev);
> > > > > @@ -274,14 +277,30 @@ PCIBus *i440fx_init(PCII440FXState **pi440fx_state,
> > > > >      f->system_memory = address_space_mem;
> > > > >      f->pci_address_space = pci_address_space;
> > > > >      f->ram_memory = ram_memory;
> > > > > +
> > > > > +    i440fx = I440FX_PCI_HOST(dev);
> > > > > +    /* Set PCI window size the way seabios has always done it. */
> > > > > +    /* Power of 2 so bios can cover it with a single MTRR */
> > > > > +    if (ram_size <= 0x80000000) {
> > > > > +        i440fx->pci_info.w32.begin = 0x80000000;
> > > > > +    } else if (ram_size <= 0xc0000000) {
> > > > > +        i440fx->pci_info.w32.begin = 0xc0000000;
> > > > > +    } else {
> > > > > +        i440fx->pci_info.w32.begin = 0xe0000000;
> > > > > +    }
> > > > > +
> > > > >      memory_region_init_alias(&f->pci_hole, OBJECT(d), "pci-hole", f->pci_address_space,
> > > > >                               pci_hole_start, pci_hole_size);
> > > > >      memory_region_add_subregion(f->system_memory, pci_hole_start, &f->pci_hole);
> > > > > +
> > > > > +    pc_init_pci64_hole(&i440fx->pci_info, 0x100000000ULL + above_4g_mem_size);
> > > > > +    pci_hole64_size = range_size(i440fx->pci_info.w64);
> > > > >      memory_region_init_alias(&f->pci_hole_64bit, OBJECT(d), "pci-hole64",
> > > > >                               f->pci_address_space,
> > > > > -                             pci_hole64_start, pci_hole64_size);
> > > > > +                             i440fx->pci_info.w64.begin, pci_hole64_size);
> > > > >      if (pci_hole64_size) {
> > > > > -        memory_region_add_subregion(f->system_memory, pci_hole64_start,
> > > > > +        memory_region_add_subregion(f->system_memory,
> > > > > +                                    i440fx->pci_info.w64.begin,
> > > > >                                      &f->pci_hole_64bit);
> > > > >      }
> > > > >      memory_region_init_alias(&f->smram_region, OBJECT(d), "smram-region",
> > > > > @@ -629,6 +648,15 @@ static const char *i440fx_pcihost_root_bus_path(PCIHostState *host_bridge,
> > > > >      return "0000";
> > > > >  }
> > > > >  
> > > > > +static Property i440fx_props[] = {
> > > > > +    DEFINE_PROP_UINT64("pci_hole64_start", I440FXState, pci_info.w64.begin, 0),
> > > > > +    DEFINE_PROP_UINT64("pci_hole64_end", I440FXState, pci_info.w64.end, 0),
> > > > > +    DEFINE_PROP_UINT64("pci_hole_start", I440FXState, pci_info.w32.begin, 0),
> > > > > +    DEFINE_PROP_UINT64("pci_hole_end", I440FXState, pci_info.w32.end,
> > > > > +                       IO_APIC_DEFAULT_ADDRESS),
> > > > > +    DEFINE_PROP_END_OF_LIST(),
> > > > > +};
> > > > > +
> > > > 
> > > > So we have 4 properties. One of them pci_hole64_end
> > > > is supposed to be set to a value.
> > > > Others should not be touched under any circuimstances.
> > > > Of course if you query properties you have no way
> > > > to know which is which and what are the legal values.
> > > > Ouch.
> > > read-only properties are possible but we would have to drop
> > > usage DEFINE_PROP_UINT64 of and explicitly use only setter in PropertyInfo,
> > 
> > Or add DEFINE_PROP_UINT64_RO for this?
> it might be the way to do it.
> 
> > 
> > > user better not to touch these properties since they are mostly internal API.
> > > but if we say it's internal properties then enforcing read-only might be
> > > overkill.
> > > For user friendly property "pci_hole64_size" would be nice to have.
> > 
> > So at the moment I do
> > 
> > qemu -device i440FX-pcihost,help
> > 
> > and this will get all properties.
> > 
> > If we add some properties that user can not set
> > they should not appear in this output.
> with QOM all around I wouldn't say so, it could be property only for internal
> purposes and QOM properties do not care about whether they are visible or
> not to user so far. I guess we could document it in code like do not
> touch /internal or ...
> 
> 
> > 
> > 
> > > > 
> > > > >  static void i440fx_pcihost_class_init(ObjectClass *klass, void *data)
> > > > >  {
> > > > >      DeviceClass *dc = DEVICE_CLASS(klass);
> > > > > @@ -638,6 +666,7 @@ static void i440fx_pcihost_class_init(ObjectClass *klass, void *data)
> > > > >      dc->realize = i440fx_pcihost_realize;
> > > > >      dc->fw_name = "pci";
> > > > >      dc->no_user = 1;
> > > > > +    dc->props = i440fx_props;
> > > > >  }
> > > > >  
> > > > >  static const TypeInfo i440fx_pcihost_info = {
> > > > > diff --git a/hw/pci-host/q35.c b/hw/pci-host/q35.c
> > > > > index 6b1b3b7..a6936e6 100644
> > > > > --- a/hw/pci-host/q35.c
> > > > > +++ b/hw/pci-host/q35.c
> > > > > @@ -67,6 +67,21 @@ static const char *q35_host_root_bus_path(PCIHostState *host_bridge,
> > > > >  static Property mch_props[] = {
> > > > >      DEFINE_PROP_UINT64("MCFG", Q35PCIHost, parent_obj.base_addr,
> > > > >                          MCH_HOST_BRIDGE_PCIEXBAR_DEFAULT),
> > > > > +    DEFINE_PROP_UINT64("pci_hole64_start", Q35PCIHost,
> > > > > +                       mch.pci_info.w64.begin, 0),
> > > > > +    DEFINE_PROP_UINT64("pci_hole64_end", Q35PCIHost,
> > > > > +                       mch.pci_info.w64.end, 0),
> > > > > +    /* Leave enough space for the biggest MCFG BAR */
> > > > > +    /* TODO: this matches current bios behaviour, but
> > > > > +     * it's not a power of two, which means an MTRR
> > > > > +     * can't cover it exactly.
> > > > > +     */
> > > > > +    DEFINE_PROP_UINT64("pci_hole_start", Q35PCIHost,
> > > > > +                       mch.pci_info.w32.begin,
> > > > > +                       MCH_HOST_BRIDGE_PCIEXBAR_DEFAULT +
> > > > > +                       MCH_HOST_BRIDGE_PCIEXBAR_MAX),
> > > > > +    DEFINE_PROP_UINT64("pci_hole_end", Q35PCIHost,
> > > > > +                       mch.pci_info.w32.end, IO_APIC_DEFAULT_ADDRESS),
> > > > >      DEFINE_PROP_END_OF_LIST(),
> > > > >  };
> > > > >  
> > > > > @@ -255,14 +270,6 @@ static int mch_init(PCIDevice *d)
> > > > >      hwaddr pci_hole64_size;
> > > > >      MCHPCIState *mch = MCH_PCI_DEVICE(d);
> > > > >  
> > > > > -    /* Leave enough space for the biggest MCFG BAR */
> > > > > -    /* TODO: this matches current bios behaviour, but
> > > > > -     * it's not a power of two, which means an MTRR
> > > > > -     * can't cover it exactly.
> > > > > -     */
> > > > > -    mch->guest_info->pci_info.w32.begin = MCH_HOST_BRIDGE_PCIEXBAR_DEFAULT +
> > > > > -        MCH_HOST_BRIDGE_PCIEXBAR_MAX;
> > > > > -
> > > > >      /* setup pci memory regions */
> > > > >      memory_region_init_alias(&mch->pci_hole, OBJECT(mch), "pci-hole",
> > > > >                               mch->pci_address_space,
> > > > > @@ -270,15 +277,16 @@ static int mch_init(PCIDevice *d)
> > > > >                               0x100000000ULL - mch->below_4g_mem_size);
> > > > >      memory_region_add_subregion(mch->system_memory, mch->below_4g_mem_size,
> > > > >                                  &mch->pci_hole);
> > > > > -    pci_hole64_size = (sizeof(hwaddr) == 4 ? 0 :
> > > > > -                       ((uint64_t)1 << 62));
> > > > > +
> > > > > +    pc_init_pci64_hole(&mch->pci_info, 0x100000000ULL + mch->above_4g_mem_size);
> > > > > +    pci_hole64_size = range_size(mch->pci_info.w64);
> > > > >      memory_region_init_alias(&mch->pci_hole_64bit, OBJECT(mch), "pci-hole64",
> > > > >                               mch->pci_address_space,
> > > > > -                             0x100000000ULL + mch->above_4g_mem_size,
> > > > > +                             mch->pci_info.w64.begin,
> > > > >                               pci_hole64_size);
> > > > >      if (pci_hole64_size) {
> > > > >          memory_region_add_subregion(mch->system_memory,
> > > > > -                                    0x100000000ULL + mch->above_4g_mem_size,
> > > > > +                                    mch->pci_info.w64.begin,
> > > > >                                      &mch->pci_hole_64bit);
> > > > >      }
> > > > >      /* smram */
> > > > > diff --git a/include/hw/i386/pc.h b/include/hw/i386/pc.h
> > > > > index 7fb97b0..ab747b7 100644
> > > > > --- a/include/hw/i386/pc.h
> > > > > +++ b/include/hw/i386/pc.h
> > > > > @@ -18,7 +18,6 @@ typedef struct PcPciInfo {
> > > > >  } PcPciInfo;
> > > > >  
> > > > >  struct PcGuestInfo {
> > > > > -    PcPciInfo pci_info;
> > > > >      bool has_pci_info;
> > > > >      FWCfgState *fw_cfg;
> > > > >  };
> > > > > @@ -100,6 +99,7 @@ void pc_acpi_init(const char *default_dsdt);
> > > > >  
> > > > >  PcGuestInfo *pc_guest_info_init(ram_addr_t below_4g_mem_size,
> > > > >                                  ram_addr_t above_4g_mem_size);
> > > > > +void pc_init_pci64_hole(PcPciInfo *pci_info, uint64_t pci_hole64_start);
> > > > >  
> > > > >  FWCfgState *pc_memory_init(MemoryRegion *system_memory,
> > > > >                             const char *kernel_filename,
> > > > > @@ -150,8 +150,7 @@ PCIBus *i440fx_init(PCII440FXState **pi440fx_state, int *piix_devfn,
> > > > >                      ram_addr_t ram_size,
> > > > >                      hwaddr pci_hole_start,
> > > > >                      hwaddr pci_hole_size,
> > > > > -                    hwaddr pci_hole64_start,
> > > > > -                    hwaddr pci_hole64_size,
> > > > > +                    ram_addr_t above_4g_mem_size,
> > > > >                      MemoryRegion *pci_memory,
> > > > >                      MemoryRegion *ram_memory);
> > > > >  
> > > > > diff --git a/include/hw/pci-host/q35.h b/include/hw/pci-host/q35.h
> > > > > index 3cb631e..3a9e04b 100644
> > > > > --- a/include/hw/pci-host/q35.h
> > > > > +++ b/include/hw/pci-host/q35.h
> > > > > @@ -55,6 +55,7 @@ typedef struct MCHPCIState {
> > > > >      MemoryRegion smram_region;
> > > > >      MemoryRegion pci_hole;
> > > > >      MemoryRegion pci_hole_64bit;
> > > > > +    PcPciInfo pci_info;
> > > > >      uint8_t smm_enabled;
> > > > >      ram_addr_t below_4g_mem_size;
> > > > >      ram_addr_t above_4g_mem_size;
> > > > > -- 
> > > > > 1.8.3.1
> > > > 
> > > 
> > > 
> > > -- 
> > > Regards,
> > >   Igor
> > 
> 
> 
> -- 
> Regards,
>   Igor
Igor Mammedov July 29, 2013, 7:55 a.m. UTC | #11
On Sun, 28 Jul 2013 22:51:57 +0300
"Michael S. Tsirkin" <mst@redhat.com> wrote:

> On Sun, Jul 28, 2013 at 07:33:27PM +0200, Igor Mammedov wrote:
> > On Sun, 28 Jul 2013 12:11:42 +0300
> > "Michael S. Tsirkin" <mst@redhat.com> wrote:
> > 
> > > On Sun, Jul 28, 2013 at 10:21:56AM +0200, Igor Mammedov wrote:
> > > > On Sun, 28 Jul 2013 10:57:12 +0300
> > > > "Michael S. Tsirkin" <mst@redhat.com> wrote:
> > > > 
> > > > > On Sun, Jul 28, 2013 at 09:29:13AM +0200, Igor Mammedov wrote:
> > > > > > It turns out that some 32 bit windows guests crash
> > > > > > if 64 bit PCI hole size is >2G.
> > > > > > Limit it to 2G for piix and q35 by default.
> > > > > > User may override default boundaries by using
> > > > > > "pci_hole64_end " property.
> > > > > > 
> > > > > > Examples:
> > > > > > -global i440FX-pcihost.pci_hole64_end=0xffffffffffffffff
> > > > > > 
> > > > > > -global q35-pcihost.pci_hole64_end=0xffffffffffffffff
> > > > > 
> > > > > IMO that's pretty bad as user interfaces go.
> > > > > In particular if you are not careful you can make
> > > > > end < start.
> > > > > Why not set the size, and include a patch that
> > > > > let people write "1G" or "2G" for convenience?
> > > > sure as convenience why not, on top of this patches.
> > > 
> > > Well because you are specifying end as 0xffffffffffffffff
> > > so it's not a multiple of 1G?
> > > 
> > > > > 
> > > > > > Reported-by: Igor Mammedov <imammedo@redhat.com>,
> > > > > > Signed-off-by: Michael S. Tsirkin <mst@redhat.com>
> > > > > > Signed-off-by: Igor Mammedov <imammedo@redhat.com>
> > > > > > ---
> > > > > >  hw/i386/pc.c              | 59 +++++++++++++++++++++++++++++------------------
> > > > > >  hw/i386/pc_piix.c         | 14 +----------
> > > > > >  hw/pci-host/piix.c        | 37 +++++++++++++++++++++++++----
> > > > > >  hw/pci-host/q35.c         | 32 +++++++++++++++----------
> > > > > >  include/hw/i386/pc.h      |  5 ++--
> > > > > >  include/hw/pci-host/q35.h |  1 +
> > > > > >  6 files changed, 94 insertions(+), 54 deletions(-)
> > > > > > 
> > > > > > diff --git a/hw/i386/pc.c b/hw/i386/pc.c
> > > > > > index b0b98a8..acaeb6c 100644
> > > > > > --- a/hw/i386/pc.c
> > > > > > +++ b/hw/i386/pc.c
> > > > > > @@ -1003,15 +1003,28 @@ typedef struct PcRomPciInfo {
> > > > > >  static void pc_fw_cfg_guest_info(PcGuestInfo *guest_info)
> > > > > >  {
> > > > > >      PcRomPciInfo *info;
> > > > > > +    Object *pci_info;
> > > > > > +
> > > > > >      if (!guest_info->has_pci_info || !guest_info->fw_cfg) {
> > > > > >          return;
> > > > > >      }
> > > > > > +    pci_info = object_resolve_path("/machine/i440fx", NULL);
> > > > > > +    if (!pci_info) {
> > > > > > +        pci_info = object_resolve_path("/machine/q35", NULL);
> > > > > > +        if (!pci_info) {
> > > > > > +            return;
> > > > > > +        }
> > > > > > +    }
> > > > > 
> > > > > 
> > > > > So is the path /machine/i440fx? /machine/i440FX?
> > > > > /machine/i440FX-pcihost?
> > > > > There's no way to check this code is right without
> > > > > actually running it.
> > > > that drawback of dynamic lookup,
> > > > QOM paths supposed to be stable.
> > > > 
> > > > > 
> > > > > How about i44fx_get_pci_info so we can have this
> > > > > knowledge of paths localized in specific chipset code?
> > > > I've seen objections from afaerber about this approach, so dropped
> > > > this idea.
> > > > 
> > > > > 
> > > > > >  
> > > > > >      info = g_malloc(sizeof *info);
> > > > > > -    info->w32_min = cpu_to_le64(guest_info->pci_info.w32.begin);
> > > > > > -    info->w32_max = cpu_to_le64(guest_info->pci_info.w32.end);
> > > > > > -    info->w64_min = cpu_to_le64(guest_info->pci_info.w64.begin);
> > > > > > -    info->w64_max = cpu_to_le64(guest_info->pci_info.w64.end);
> > > > > > +    info->w32_min = cpu_to_le64(object_property_get_int(pci_info,
> > > > > > +                                "pci_hole_start", NULL));
> > > > > > +    info->w32_max = cpu_to_le64(object_property_get_int(pci_info,
> > > > > > +                                "pci_hole_end", NULL));
> > > > > 
> > > > > Looks wrong.
> > > > > object_property_get_int returns a signed int64.
> > > > > w32 is unsigned.
> > > > > Happens to work but I think we need an explicit API.
> > > > I guess it's copy-past error s/cpu_to_le64/cpu_to_le32/
> > > 
> > > Not these are 64 bit values, but they need to be
> > > unsigned not signed.
> > > 
> > > > but not need for extra API, with fixed property definition
> > > > i.e. s/UINT64/UNIT32/ property set code will take care about limits.
> > > 
> > > If you replace these with UINT32 you won't be able to
> > > specify values >4G.
> > does 32 bit PCI hole has right to be more than 4Gb?
> 
> No but the 64 bit one does. 32 one shouldn't be user
> controllable at all.
> 
> > > 
> > > > > 
> > > > > Property names are hard-coded string literals.
> > > > > Please add macros to set and get them
> > > > > so we can avoid duplicating code.
> > > > > E.g.
> > > > sure.
> > > > 
> > > > > 
> > > > > #define PCI_HOST_PROPS...
> > > > > static inline get_
> > > > > 
> > > > > 
> > > > > 
> > > > > > +    info->w64_min = cpu_to_le64(object_property_get_int(pci_info,
> > > > > > +                                "pci_hole64_start", NULL));
> > > > > > +    info->w64_max = cpu_to_le64(object_property_get_int(pci_info,
> > > > > > +                                "pci_hole64_end", NULL));
> > > > > >      /* Pass PCI hole info to guest via a side channel.
> > > > > >       * Required so guest PCI enumeration does the right thing. */
> > > > > >      fw_cfg_add_file(guest_info->fw_cfg, "etc/pci-info", info, sizeof *info);
> > > > > > @@ -1037,29 +1050,31 @@ PcGuestInfo *pc_guest_info_init(ram_addr_t below_4g_mem_size,
> > > > > >      PcGuestInfoState *guest_info_state = g_malloc0(sizeof *guest_info_state);
> > > > > >      PcGuestInfo *guest_info = &guest_info_state->info;
> > > > > >  
> > > > > > -    guest_info->pci_info.w32.end = IO_APIC_DEFAULT_ADDRESS;
> > > > > > -    if (sizeof(hwaddr) == 4) {
> > > > > > -        guest_info->pci_info.w64.begin = 0;
> > > > > > -        guest_info->pci_info.w64.end = 0;
> > > > > > -    } else {
> > > > > > -        /*
> > > > > > -         * BIOS does not set MTRR entries for the 64 bit window, so no need to
> > > > > > -         * align address to power of two.  Align address at 1G, this makes sure
> > > > > > -         * it can be exactly covered with a PAT entry even when using huge
> > > > > > -         * pages.
> > > > > > -         */
> > > > > > -        guest_info->pci_info.w64.begin =
> > > > > > -            ROUND_UP((0x1ULL << 32) + above_4g_mem_size, 0x1ULL << 30);
> > > > > > -        guest_info->pci_info.w64.end = guest_info->pci_info.w64.begin +
> > > > > > -            (0x1ULL << 62);
> > > > > > -        assert(guest_info->pci_info.w64.begin <= guest_info->pci_info.w64.end);
> > > > > > -    }
> > > > > > -
> > > > > >      guest_info_state->machine_done.notify = pc_guest_info_machine_done;
> > > > > >      qemu_add_machine_init_done_notifier(&guest_info_state->machine_done);
> > > > > >      return guest_info;
> > > > > >  }
> > > > > >  
> > > > > > +void pc_init_pci64_hole(PcPciInfo *pci_info, uint64_t pci_hole64_start)
> > > > > > +{
> > > > > > +    if (sizeof(hwaddr) == 4) {
> > > > > > +        memset(&pci_info->w64, sizeof(pci_info->w64), 0);
> > > > > > +        return;
> > > > > > +    }
> > > > > > +    /*
> > > > > > +     * BIOS does not set MTRR entries for the 64 bit window, so no need to
> > > > > > +     * align address to power of two.  Align address at 1G, this makes sure
> > > > > > +     * it can be exactly covered with a PAT entry even when using huge
> > > > > > +     * pages.
> > > > > > +     */
> > > > > > +    pci_info->w64.begin = ROUND_UP(pci_hole64_start, 0x1ULL << 30);
> > > > > > +    if (!pci_info->w64.end) {
> > > > > > +        /* set default end if it wasn't specified, + 2 Gb past start */
> > > > > > +        pci_info->w64.end = pci_info->w64.begin + (0x1ULL << 31);
> > > > > > +    }
> > > > > > +    assert(pci_info->w64.begin < pci_info->w64.end);
> > > > > > +}
> > > > > > +
> > > > > >  void pc_acpi_init(const char *default_dsdt)
> > > > > >  {
> > > > > >      char *filename;
> > > > > > diff --git a/hw/i386/pc_piix.c b/hw/i386/pc_piix.c
> > > > > > index b58c255..ab25458 100644
> > > > > > --- a/hw/i386/pc_piix.c
> > > > > > +++ b/hw/i386/pc_piix.c
> > > > > > @@ -129,15 +129,6 @@ static void pc_init1(MemoryRegion *system_memory,
> > > > > >      guest_info = pc_guest_info_init(below_4g_mem_size, above_4g_mem_size);
> > > > > >      guest_info->has_pci_info = has_pci_info;
> > > > > >  
> > > > > > -    /* Set PCI window size the way seabios has always done it. */
> > > > > > -    /* Power of 2 so bios can cover it with a single MTRR */
> > > > > > -    if (ram_size <= 0x80000000)
> > > > > > -        guest_info->pci_info.w32.begin = 0x80000000;
> > > > > > -    else if (ram_size <= 0xc0000000)
> > > > > > -        guest_info->pci_info.w32.begin = 0xc0000000;
> > > > > > -    else
> > > > > > -        guest_info->pci_info.w32.begin = 0xe0000000;
> > > > > > -
> > > > > >      /* allocate ram and load rom/bios */
> > > > > >      if (!xen_enabled()) {
> > > > > >          fw_cfg = pc_memory_init(system_memory,
> > > > > > @@ -160,10 +151,7 @@ static void pc_init1(MemoryRegion *system_memory,
> > > > > >                                system_memory, system_io, ram_size,
> > > > > >                                below_4g_mem_size,
> > > > > >                                0x100000000ULL - below_4g_mem_size,
> > > > > > -                              0x100000000ULL + above_4g_mem_size,
> > > > > > -                              (sizeof(hwaddr) == 4
> > > > > > -                               ? 0
> > > > > > -                               : ((uint64_t)1 << 62)),
> > > > > > +                              above_4g_mem_size,
> > > > > >                                pci_memory, ram_memory);
> > > > > >      } else {
> > > > > >          pci_bus = NULL;
> > > > > > diff --git a/hw/pci-host/piix.c b/hw/pci-host/piix.c
> > > > > > index 4624d04..59a42c5 100644
> > > > > > --- a/hw/pci-host/piix.c
> > > > > > +++ b/hw/pci-host/piix.c
> > > > > > @@ -32,6 +32,7 @@
> > > > > >  #include "hw/xen/xen.h"
> > > > > >  #include "hw/pci-host/pam.h"
> > > > > >  #include "sysemu/sysemu.h"
> > > > > > +#include "hw/i386/ioapic.h"
> > > > > >  
> > > > > >  /*
> > > > > >   * I440FX chipset data sheet.
> > > > > > @@ -44,6 +45,7 @@
> > > > > >  
> > > > > >  typedef struct I440FXState {
> > > > > >      PCIHostState parent_obj;
> > > > > > +    PcPciInfo pci_info;
> > > > > >  } I440FXState;
> > > > > >  
> > > > > >  #define PIIX_NUM_PIC_IRQS       16      /* i8259 * 2 */
> > > > > > @@ -247,8 +249,7 @@ PCIBus *i440fx_init(PCII440FXState **pi440fx_state,
> > > > > >                      ram_addr_t ram_size,
> > > > > >                      hwaddr pci_hole_start,
> > > > > >                      hwaddr pci_hole_size,
> > > > > > -                    hwaddr pci_hole64_start,
> > > > > > -                    hwaddr pci_hole64_size,
> > > > > > +                    ram_addr_t above_4g_mem_size,
> > > > > >                      MemoryRegion *pci_address_space,
> > > > > >                      MemoryRegion *ram_memory)
> > > > > >  {
> > > > > > @@ -259,6 +260,8 @@ PCIBus *i440fx_init(PCII440FXState **pi440fx_state,
> > > > > >      PIIX3State *piix3;
> > > > > >      PCII440FXState *f;
> > > > > >      unsigned i;
> > > > > > +    I440FXState *i440fx;
> > > > > > +    uint64_t pci_hole64_size;
> > > > > >  
> > > > > >      dev = qdev_create(NULL, TYPE_I440FX_PCI_HOST);
> > > > > >      s = PCI_HOST_BRIDGE(dev);
> > > > > > @@ -274,14 +277,30 @@ PCIBus *i440fx_init(PCII440FXState **pi440fx_state,
> > > > > >      f->system_memory = address_space_mem;
> > > > > >      f->pci_address_space = pci_address_space;
> > > > > >      f->ram_memory = ram_memory;
> > > > > > +
> > > > > > +    i440fx = I440FX_PCI_HOST(dev);
> > > > > > +    /* Set PCI window size the way seabios has always done it. */
> > > > > > +    /* Power of 2 so bios can cover it with a single MTRR */
> > > > > > +    if (ram_size <= 0x80000000) {
> > > > > > +        i440fx->pci_info.w32.begin = 0x80000000;
> > > > > > +    } else if (ram_size <= 0xc0000000) {
> > > > > > +        i440fx->pci_info.w32.begin = 0xc0000000;
> > > > > > +    } else {
> > > > > > +        i440fx->pci_info.w32.begin = 0xe0000000;
> > > > > > +    }
> > > > > > +
> > > > > >      memory_region_init_alias(&f->pci_hole, OBJECT(d), "pci-hole", f->pci_address_space,
> > > > > >                               pci_hole_start, pci_hole_size);
> > > > > >      memory_region_add_subregion(f->system_memory, pci_hole_start, &f->pci_hole);
> > > > > > +
> > > > > > +    pc_init_pci64_hole(&i440fx->pci_info, 0x100000000ULL + above_4g_mem_size);
> > > > > > +    pci_hole64_size = range_size(i440fx->pci_info.w64);
> > > > > >      memory_region_init_alias(&f->pci_hole_64bit, OBJECT(d), "pci-hole64",
> > > > > >                               f->pci_address_space,
> > > > > > -                             pci_hole64_start, pci_hole64_size);
> > > > > > +                             i440fx->pci_info.w64.begin, pci_hole64_size);
> > > > > >      if (pci_hole64_size) {
> > > > > > -        memory_region_add_subregion(f->system_memory, pci_hole64_start,
> > > > > > +        memory_region_add_subregion(f->system_memory,
> > > > > > +                                    i440fx->pci_info.w64.begin,
> > > > > >                                      &f->pci_hole_64bit);
> > > > > >      }
> > > > > >      memory_region_init_alias(&f->smram_region, OBJECT(d), "smram-region",
> > > > > > @@ -629,6 +648,15 @@ static const char *i440fx_pcihost_root_bus_path(PCIHostState *host_bridge,
> > > > > >      return "0000";
> > > > > >  }
> > > > > >  
> > > > > > +static Property i440fx_props[] = {
> > > > > > +    DEFINE_PROP_UINT64("pci_hole64_start", I440FXState, pci_info.w64.begin, 0),
> > > > > > +    DEFINE_PROP_UINT64("pci_hole64_end", I440FXState, pci_info.w64.end, 0),
> > > > > > +    DEFINE_PROP_UINT64("pci_hole_start", I440FXState, pci_info.w32.begin, 0),
> > > > > > +    DEFINE_PROP_UINT64("pci_hole_end", I440FXState, pci_info.w32.end,
> > > > > > +                       IO_APIC_DEFAULT_ADDRESS),
> > > > > > +    DEFINE_PROP_END_OF_LIST(),
> > > > > > +};
> > > > > > +
> > > > > 
> > > > > So we have 4 properties. One of them pci_hole64_end
> > > > > is supposed to be set to a value.
> > > > > Others should not be touched under any circuimstances.
> > > > > Of course if you query properties you have no way
> > > > > to know which is which and what are the legal values.
> > > > > Ouch.
> > > > read-only properties are possible but we would have to drop
> > > > usage DEFINE_PROP_UINT64 of and explicitly use only setter in PropertyInfo,
> > > 
> > > Or add DEFINE_PROP_UINT64_RO for this?
> > it might be the way to do it.
I've meant to do it only for 32-bit PCI hole, I'm sorry for being unclear.

[...]
Michael S. Tsirkin July 29, 2013, 8:16 a.m. UTC | #12
On Mon, Jul 29, 2013 at 09:55:32AM +0200, Igor Mammedov wrote:
> > > > > but not need for extra API, with fixed property definition
> > > > > i.e. s/UINT64/UNIT32/ property set code will take care about limits.
> > > > 
> > > > If you replace these with UINT32 you won't be able to
> > > > specify values >4G.
> > > does 32 bit PCI hole has right to be more than 4Gb?
> > 
> > No but the 64 bit one does. 32 one shouldn't be user
> > controllable at all.

...

> I've meant to do it only for 32-bit PCI hole, I'm sorry for being unclear.
> 
> [...]

That's OK then.
Michael Roth July 30, 2013, 9:34 p.m. UTC | #13
Quoting Andreas Färber (2013-07-28 05:17:47)
> Am 28.07.2013 11:11, schrieb Michael S. Tsirkin:
> > On Sun, Jul 28, 2013 at 10:21:56AM +0200, Igor Mammedov wrote:
> >> On Sun, 28 Jul 2013 10:57:12 +0300
> >> "Michael S. Tsirkin" <mst@redhat.com> wrote:
> >>
> >>> On Sun, Jul 28, 2013 at 09:29:13AM +0200, Igor Mammedov wrote:
> >>>>  
> >>>>      info = g_malloc(sizeof *info);
> >>>> -    info->w32_min = cpu_to_le64(guest_info->pci_info.w32.begin);
> >>>> -    info->w32_max = cpu_to_le64(guest_info->pci_info.w32.end);
> >>>> -    info->w64_min = cpu_to_le64(guest_info->pci_info.w64.begin);
> >>>> -    info->w64_max = cpu_to_le64(guest_info->pci_info.w64.end);
> >>>> +    info->w32_min = cpu_to_le64(object_property_get_int(pci_info,
> >>>> +                                "pci_hole_start", NULL));
> >>>> +    info->w32_max = cpu_to_le64(object_property_get_int(pci_info,
> >>>> +                                "pci_hole_end", NULL));
> >>>
> >>> Looks wrong.
> >>> object_property_get_int returns a signed int64.
> >>> w32 is unsigned.
> >>> Happens to work but I think we need an explicit API.
> 
> That's how QAPI works internally today for any uint64 visitor/property.
> uint64_t is cast to int64_t and back in visitors.
> 
> So I'd hope something like
> uint64_t val = (uint64_t) object_property_get_int()
> would work equally well - CC'ing Michael.

It actually depends on the 'wire'/'serialized' encoding of the underlying
visitor implementation. In this case we're 'serializing' into a QObject
via QmpOutputVisitor, where all integers are actually stored as a
QInt/int64 anyway, so this cast is unavoidable in this case regardless
of what QAPI interface we use: we'll always end up storing as an int64,
requiring us to re-cast to get back to original value. Best we can
achieve is burying the cast deeper (or significantly re-working how
QObject/QInt works)

There is additional bounds checking performed prior to serialization
if the serialized type is less than 64 bits though, so we'd probably
want to add fixed-width accessors if we found ourselves in a situation
where we needed to cast to a smaller datatype than the original.

It's also worth noting that visiting uint64 types using int64 visitor
interfaces isn't universally guaranteed to work: for certain visitor
implementations the serialized encodings may not be compatible with
one another. But in this case we should be good.

> 
> >> I guess it's copy-past error s/cpu_to_le64/cpu_to_le32/
> > 
> > Not these are 64 bit values, but they need to be
> > unsigned not signed.
> > > >> but not need for extra API, with fixed property definition
> >> i.e. s/UINT64/UNIT32/ property set code will take care about limits.
> > 
> > If you replace these with UINT32 you won't be able to
> > specify values >4G.
> > 
> >>> Property names are hard-coded string literals.
> >>> Please add macros to set and get them
> >>> so we can avoid duplicating code.
> >>> E.g.
> >> sure.
> >>
> >>>
> >>> #define PCI_HOST_PROPS...
> >>> static inline get_
> [...]
> >>>> @@ -629,6 +648,15 @@ static const char *i440fx_pcihost_root_bus_path(PCIHostState *host_bridge,
> >>>>      return "0000";
> >>>>  }
> >>>>  
> >>>> +static Property i440fx_props[] = {
> >>>> +    DEFINE_PROP_UINT64("pci_hole64_start", I440FXState, pci_info.w64.begin, 0),
> >>>> +    DEFINE_PROP_UINT64("pci_hole64_end", I440FXState, pci_info.w64.end, 0),
> >>>> +    DEFINE_PROP_UINT64("pci_hole_start", I440FXState, pci_info.w32.begin, 0),
> >>>> +    DEFINE_PROP_UINT64("pci_hole_end", I440FXState, pci_info.w32.end,
> >>>> +                       IO_APIC_DEFAULT_ADDRESS),
> >>>> +    DEFINE_PROP_END_OF_LIST(),
> >>>> +};
> >>>> +
> >>>
> >>> So we have 4 properties. One of them pci_hole64_end
> >>> is supposed to be set to a value.
> >>> Others should not be touched under any circuimstances.
> >>> Of course if you query properties you have no way
> >>> to know which is which and what are the legal values.
> >>> Ouch.
> >> read-only properties are possible but we would have to drop
> >> usage DEFINE_PROP_UINT64 of and explicitly use only setter in PropertyInfo,
> > 
> > Or add DEFINE_PROP_UINT64_RO for this?
> > 
> >> user better not to touch these properties since they are mostly internal API.
> >> but if we say it's internal properties then enforcing read-only might be
> >> overkill.
> >> For user friendly property "pci_hole64_size" would be nice to have.
> > 
> > So at the moment I do
> > 
> > qemu -device i440FX-pcihost,help
> > 
> > and this will get all properties.
> > 
> > If we add some properties that user can not set
> > they should not appear in this output.
> [snip]
> 
> Igor, you can simply use dynamic properties with NULL as setter argument
> for object_property_add*() to achieve that effect.
> 
> Andreas
> 
> -- 
> SUSE LINUX Products GmbH, Maxfeldstr. 5, 90409 Nürnberg, Germany
> GF: Jeff Hawn, Jennifer Guild, Felix Imendörffer; HRB 16746 AG Nürnberg
diff mbox

Patch

diff --git a/hw/i386/pc.c b/hw/i386/pc.c
index b0b98a8..acaeb6c 100644
--- a/hw/i386/pc.c
+++ b/hw/i386/pc.c
@@ -1003,15 +1003,28 @@  typedef struct PcRomPciInfo {
 static void pc_fw_cfg_guest_info(PcGuestInfo *guest_info)
 {
     PcRomPciInfo *info;
+    Object *pci_info;
+
     if (!guest_info->has_pci_info || !guest_info->fw_cfg) {
         return;
     }
+    pci_info = object_resolve_path("/machine/i440fx", NULL);
+    if (!pci_info) {
+        pci_info = object_resolve_path("/machine/q35", NULL);
+        if (!pci_info) {
+            return;
+        }
+    }
 
     info = g_malloc(sizeof *info);
-    info->w32_min = cpu_to_le64(guest_info->pci_info.w32.begin);
-    info->w32_max = cpu_to_le64(guest_info->pci_info.w32.end);
-    info->w64_min = cpu_to_le64(guest_info->pci_info.w64.begin);
-    info->w64_max = cpu_to_le64(guest_info->pci_info.w64.end);
+    info->w32_min = cpu_to_le64(object_property_get_int(pci_info,
+                                "pci_hole_start", NULL));
+    info->w32_max = cpu_to_le64(object_property_get_int(pci_info,
+                                "pci_hole_end", NULL));
+    info->w64_min = cpu_to_le64(object_property_get_int(pci_info,
+                                "pci_hole64_start", NULL));
+    info->w64_max = cpu_to_le64(object_property_get_int(pci_info,
+                                "pci_hole64_end", NULL));
     /* Pass PCI hole info to guest via a side channel.
      * Required so guest PCI enumeration does the right thing. */
     fw_cfg_add_file(guest_info->fw_cfg, "etc/pci-info", info, sizeof *info);
@@ -1037,29 +1050,31 @@  PcGuestInfo *pc_guest_info_init(ram_addr_t below_4g_mem_size,
     PcGuestInfoState *guest_info_state = g_malloc0(sizeof *guest_info_state);
     PcGuestInfo *guest_info = &guest_info_state->info;
 
-    guest_info->pci_info.w32.end = IO_APIC_DEFAULT_ADDRESS;
-    if (sizeof(hwaddr) == 4) {
-        guest_info->pci_info.w64.begin = 0;
-        guest_info->pci_info.w64.end = 0;
-    } else {
-        /*
-         * BIOS does not set MTRR entries for the 64 bit window, so no need to
-         * align address to power of two.  Align address at 1G, this makes sure
-         * it can be exactly covered with a PAT entry even when using huge
-         * pages.
-         */
-        guest_info->pci_info.w64.begin =
-            ROUND_UP((0x1ULL << 32) + above_4g_mem_size, 0x1ULL << 30);
-        guest_info->pci_info.w64.end = guest_info->pci_info.w64.begin +
-            (0x1ULL << 62);
-        assert(guest_info->pci_info.w64.begin <= guest_info->pci_info.w64.end);
-    }
-
     guest_info_state->machine_done.notify = pc_guest_info_machine_done;
     qemu_add_machine_init_done_notifier(&guest_info_state->machine_done);
     return guest_info;
 }
 
+void pc_init_pci64_hole(PcPciInfo *pci_info, uint64_t pci_hole64_start)
+{
+    if (sizeof(hwaddr) == 4) {
+        memset(&pci_info->w64, sizeof(pci_info->w64), 0);
+        return;
+    }
+    /*
+     * BIOS does not set MTRR entries for the 64 bit window, so no need to
+     * align address to power of two.  Align address at 1G, this makes sure
+     * it can be exactly covered with a PAT entry even when using huge
+     * pages.
+     */
+    pci_info->w64.begin = ROUND_UP(pci_hole64_start, 0x1ULL << 30);
+    if (!pci_info->w64.end) {
+        /* set default end if it wasn't specified, + 2 Gb past start */
+        pci_info->w64.end = pci_info->w64.begin + (0x1ULL << 31);
+    }
+    assert(pci_info->w64.begin < pci_info->w64.end);
+}
+
 void pc_acpi_init(const char *default_dsdt)
 {
     char *filename;
diff --git a/hw/i386/pc_piix.c b/hw/i386/pc_piix.c
index b58c255..ab25458 100644
--- a/hw/i386/pc_piix.c
+++ b/hw/i386/pc_piix.c
@@ -129,15 +129,6 @@  static void pc_init1(MemoryRegion *system_memory,
     guest_info = pc_guest_info_init(below_4g_mem_size, above_4g_mem_size);
     guest_info->has_pci_info = has_pci_info;
 
-    /* Set PCI window size the way seabios has always done it. */
-    /* Power of 2 so bios can cover it with a single MTRR */
-    if (ram_size <= 0x80000000)
-        guest_info->pci_info.w32.begin = 0x80000000;
-    else if (ram_size <= 0xc0000000)
-        guest_info->pci_info.w32.begin = 0xc0000000;
-    else
-        guest_info->pci_info.w32.begin = 0xe0000000;
-
     /* allocate ram and load rom/bios */
     if (!xen_enabled()) {
         fw_cfg = pc_memory_init(system_memory,
@@ -160,10 +151,7 @@  static void pc_init1(MemoryRegion *system_memory,
                               system_memory, system_io, ram_size,
                               below_4g_mem_size,
                               0x100000000ULL - below_4g_mem_size,
-                              0x100000000ULL + above_4g_mem_size,
-                              (sizeof(hwaddr) == 4
-                               ? 0
-                               : ((uint64_t)1 << 62)),
+                              above_4g_mem_size,
                               pci_memory, ram_memory);
     } else {
         pci_bus = NULL;
diff --git a/hw/pci-host/piix.c b/hw/pci-host/piix.c
index 4624d04..59a42c5 100644
--- a/hw/pci-host/piix.c
+++ b/hw/pci-host/piix.c
@@ -32,6 +32,7 @@ 
 #include "hw/xen/xen.h"
 #include "hw/pci-host/pam.h"
 #include "sysemu/sysemu.h"
+#include "hw/i386/ioapic.h"
 
 /*
  * I440FX chipset data sheet.
@@ -44,6 +45,7 @@ 
 
 typedef struct I440FXState {
     PCIHostState parent_obj;
+    PcPciInfo pci_info;
 } I440FXState;
 
 #define PIIX_NUM_PIC_IRQS       16      /* i8259 * 2 */
@@ -247,8 +249,7 @@  PCIBus *i440fx_init(PCII440FXState **pi440fx_state,
                     ram_addr_t ram_size,
                     hwaddr pci_hole_start,
                     hwaddr pci_hole_size,
-                    hwaddr pci_hole64_start,
-                    hwaddr pci_hole64_size,
+                    ram_addr_t above_4g_mem_size,
                     MemoryRegion *pci_address_space,
                     MemoryRegion *ram_memory)
 {
@@ -259,6 +260,8 @@  PCIBus *i440fx_init(PCII440FXState **pi440fx_state,
     PIIX3State *piix3;
     PCII440FXState *f;
     unsigned i;
+    I440FXState *i440fx;
+    uint64_t pci_hole64_size;
 
     dev = qdev_create(NULL, TYPE_I440FX_PCI_HOST);
     s = PCI_HOST_BRIDGE(dev);
@@ -274,14 +277,30 @@  PCIBus *i440fx_init(PCII440FXState **pi440fx_state,
     f->system_memory = address_space_mem;
     f->pci_address_space = pci_address_space;
     f->ram_memory = ram_memory;
+
+    i440fx = I440FX_PCI_HOST(dev);
+    /* Set PCI window size the way seabios has always done it. */
+    /* Power of 2 so bios can cover it with a single MTRR */
+    if (ram_size <= 0x80000000) {
+        i440fx->pci_info.w32.begin = 0x80000000;
+    } else if (ram_size <= 0xc0000000) {
+        i440fx->pci_info.w32.begin = 0xc0000000;
+    } else {
+        i440fx->pci_info.w32.begin = 0xe0000000;
+    }
+
     memory_region_init_alias(&f->pci_hole, OBJECT(d), "pci-hole", f->pci_address_space,
                              pci_hole_start, pci_hole_size);
     memory_region_add_subregion(f->system_memory, pci_hole_start, &f->pci_hole);
+
+    pc_init_pci64_hole(&i440fx->pci_info, 0x100000000ULL + above_4g_mem_size);
+    pci_hole64_size = range_size(i440fx->pci_info.w64);
     memory_region_init_alias(&f->pci_hole_64bit, OBJECT(d), "pci-hole64",
                              f->pci_address_space,
-                             pci_hole64_start, pci_hole64_size);
+                             i440fx->pci_info.w64.begin, pci_hole64_size);
     if (pci_hole64_size) {
-        memory_region_add_subregion(f->system_memory, pci_hole64_start,
+        memory_region_add_subregion(f->system_memory,
+                                    i440fx->pci_info.w64.begin,
                                     &f->pci_hole_64bit);
     }
     memory_region_init_alias(&f->smram_region, OBJECT(d), "smram-region",
@@ -629,6 +648,15 @@  static const char *i440fx_pcihost_root_bus_path(PCIHostState *host_bridge,
     return "0000";
 }
 
+static Property i440fx_props[] = {
+    DEFINE_PROP_UINT64("pci_hole64_start", I440FXState, pci_info.w64.begin, 0),
+    DEFINE_PROP_UINT64("pci_hole64_end", I440FXState, pci_info.w64.end, 0),
+    DEFINE_PROP_UINT64("pci_hole_start", I440FXState, pci_info.w32.begin, 0),
+    DEFINE_PROP_UINT64("pci_hole_end", I440FXState, pci_info.w32.end,
+                       IO_APIC_DEFAULT_ADDRESS),
+    DEFINE_PROP_END_OF_LIST(),
+};
+
 static void i440fx_pcihost_class_init(ObjectClass *klass, void *data)
 {
     DeviceClass *dc = DEVICE_CLASS(klass);
@@ -638,6 +666,7 @@  static void i440fx_pcihost_class_init(ObjectClass *klass, void *data)
     dc->realize = i440fx_pcihost_realize;
     dc->fw_name = "pci";
     dc->no_user = 1;
+    dc->props = i440fx_props;
 }
 
 static const TypeInfo i440fx_pcihost_info = {
diff --git a/hw/pci-host/q35.c b/hw/pci-host/q35.c
index 6b1b3b7..a6936e6 100644
--- a/hw/pci-host/q35.c
+++ b/hw/pci-host/q35.c
@@ -67,6 +67,21 @@  static const char *q35_host_root_bus_path(PCIHostState *host_bridge,
 static Property mch_props[] = {
     DEFINE_PROP_UINT64("MCFG", Q35PCIHost, parent_obj.base_addr,
                         MCH_HOST_BRIDGE_PCIEXBAR_DEFAULT),
+    DEFINE_PROP_UINT64("pci_hole64_start", Q35PCIHost,
+                       mch.pci_info.w64.begin, 0),
+    DEFINE_PROP_UINT64("pci_hole64_end", Q35PCIHost,
+                       mch.pci_info.w64.end, 0),
+    /* Leave enough space for the biggest MCFG BAR */
+    /* TODO: this matches current bios behaviour, but
+     * it's not a power of two, which means an MTRR
+     * can't cover it exactly.
+     */
+    DEFINE_PROP_UINT64("pci_hole_start", Q35PCIHost,
+                       mch.pci_info.w32.begin,
+                       MCH_HOST_BRIDGE_PCIEXBAR_DEFAULT +
+                       MCH_HOST_BRIDGE_PCIEXBAR_MAX),
+    DEFINE_PROP_UINT64("pci_hole_end", Q35PCIHost,
+                       mch.pci_info.w32.end, IO_APIC_DEFAULT_ADDRESS),
     DEFINE_PROP_END_OF_LIST(),
 };
 
@@ -255,14 +270,6 @@  static int mch_init(PCIDevice *d)
     hwaddr pci_hole64_size;
     MCHPCIState *mch = MCH_PCI_DEVICE(d);
 
-    /* Leave enough space for the biggest MCFG BAR */
-    /* TODO: this matches current bios behaviour, but
-     * it's not a power of two, which means an MTRR
-     * can't cover it exactly.
-     */
-    mch->guest_info->pci_info.w32.begin = MCH_HOST_BRIDGE_PCIEXBAR_DEFAULT +
-        MCH_HOST_BRIDGE_PCIEXBAR_MAX;
-
     /* setup pci memory regions */
     memory_region_init_alias(&mch->pci_hole, OBJECT(mch), "pci-hole",
                              mch->pci_address_space,
@@ -270,15 +277,16 @@  static int mch_init(PCIDevice *d)
                              0x100000000ULL - mch->below_4g_mem_size);
     memory_region_add_subregion(mch->system_memory, mch->below_4g_mem_size,
                                 &mch->pci_hole);
-    pci_hole64_size = (sizeof(hwaddr) == 4 ? 0 :
-                       ((uint64_t)1 << 62));
+
+    pc_init_pci64_hole(&mch->pci_info, 0x100000000ULL + mch->above_4g_mem_size);
+    pci_hole64_size = range_size(mch->pci_info.w64);
     memory_region_init_alias(&mch->pci_hole_64bit, OBJECT(mch), "pci-hole64",
                              mch->pci_address_space,
-                             0x100000000ULL + mch->above_4g_mem_size,
+                             mch->pci_info.w64.begin,
                              pci_hole64_size);
     if (pci_hole64_size) {
         memory_region_add_subregion(mch->system_memory,
-                                    0x100000000ULL + mch->above_4g_mem_size,
+                                    mch->pci_info.w64.begin,
                                     &mch->pci_hole_64bit);
     }
     /* smram */
diff --git a/include/hw/i386/pc.h b/include/hw/i386/pc.h
index 7fb97b0..ab747b7 100644
--- a/include/hw/i386/pc.h
+++ b/include/hw/i386/pc.h
@@ -18,7 +18,6 @@  typedef struct PcPciInfo {
 } PcPciInfo;
 
 struct PcGuestInfo {
-    PcPciInfo pci_info;
     bool has_pci_info;
     FWCfgState *fw_cfg;
 };
@@ -100,6 +99,7 @@  void pc_acpi_init(const char *default_dsdt);
 
 PcGuestInfo *pc_guest_info_init(ram_addr_t below_4g_mem_size,
                                 ram_addr_t above_4g_mem_size);
+void pc_init_pci64_hole(PcPciInfo *pci_info, uint64_t pci_hole64_start);
 
 FWCfgState *pc_memory_init(MemoryRegion *system_memory,
                            const char *kernel_filename,
@@ -150,8 +150,7 @@  PCIBus *i440fx_init(PCII440FXState **pi440fx_state, int *piix_devfn,
                     ram_addr_t ram_size,
                     hwaddr pci_hole_start,
                     hwaddr pci_hole_size,
-                    hwaddr pci_hole64_start,
-                    hwaddr pci_hole64_size,
+                    ram_addr_t above_4g_mem_size,
                     MemoryRegion *pci_memory,
                     MemoryRegion *ram_memory);
 
diff --git a/include/hw/pci-host/q35.h b/include/hw/pci-host/q35.h
index 3cb631e..3a9e04b 100644
--- a/include/hw/pci-host/q35.h
+++ b/include/hw/pci-host/q35.h
@@ -55,6 +55,7 @@  typedef struct MCHPCIState {
     MemoryRegion smram_region;
     MemoryRegion pci_hole;
     MemoryRegion pci_hole_64bit;
+    PcPciInfo pci_info;
     uint8_t smm_enabled;
     ram_addr_t below_4g_mem_size;
     ram_addr_t above_4g_mem_size;