diff mbox

[v4,5/5] x86: Set physical address bits based on host

Message ID 1467990099-27853-6-git-send-email-dgilbert@redhat.com
State New
Headers show

Commit Message

Dr. David Alan Gilbert July 8, 2016, 3:01 p.m. UTC
From: "Dr. David Alan Gilbert" <dgilbert@redhat.com>

Add the host-phys-bits boolean property, if true, take phys-bits
from the hosts physical bits value, overriding either the default
or the user specified value.

We can also use the value we read from the host to check the users
explicitly set value and warn them if it doesn't match.

Signed-off-by: Dr. David Alan Gilbert <dgilbert@redhat.com>
---
 target-i386/cpu.c | 73 ++++++++++++++++++++++++++++++++++++++++++++++++-------
 target-i386/cpu.h |  3 +++
 2 files changed, 67 insertions(+), 9 deletions(-)

Comments

Paolo Bonzini July 8, 2016, 4:34 p.m. UTC | #1
On 08/07/2016 17:01, Dr. David Alan Gilbert (git) wrote:
> +            uint32_t host_phys_bits = x86_host_phys_bits();
> +            static bool warned;
> +
> +            if (cpu->host_phys_bits) {
> +                /* The user asked for us to use the host physical bits */
> +                cpu->phys_bits = host_phys_bits;
> +            }
> +
> +            /* Print a warning if the user set it to a value that's not the
> +             * host value.
> +             */
> +            if (cpu->phys_bits != host_phys_bits && cpu->phys_bits != 0 &&
> +                !warned) {
> +                error_report("Warning: Host physical bits (%u)"
> +                                 " does not match phys-bits property (%u)",
> +                                 host_phys_bits, cpu->phys_bits);
> +                warned = true;
> +            }
> +
> +            if (cpu->phys_bits &&
> +                (cpu->phys_bits > TARGET_PHYS_ADDR_SPACE_BITS ||
> +                cpu->phys_bits < 32)) {
>                  error_setg(errp, "phys-bits should be between 32 and %u "
>                                   " (but is %u)",
>                                   TARGET_PHYS_ADDR_SPACE_BITS, cpu->phys_bits);

Michael Tsirkin suggested a way to support guest-phys-bits <
host-phys-bits in KVM.  I plan to implement it soonish.  In the
meanwhile I guess this patch is fine, we can refine it later.

Paolo
Eduardo Habkost July 8, 2016, 11:16 p.m. UTC | #2
On Fri, Jul 08, 2016 at 04:01:39PM +0100, Dr. David Alan Gilbert (git) wrote:
> From: "Dr. David Alan Gilbert" <dgilbert@redhat.com>
> 
> Add the host-phys-bits boolean property, if true, take phys-bits
> from the hosts physical bits value, overriding either the default
> or the user specified value.
> 
> We can also use the value we read from the host to check the users
> explicitly set value and warn them if it doesn't match.
> 
> Signed-off-by: Dr. David Alan Gilbert <dgilbert@redhat.com>
> ---
[...]
> @@ -2952,28 +2977,57 @@ static void x86_cpu_realizefn(DeviceState *dev, Error **errp)
>             & CPUID_EXT2_AMD_ALIASES);
>      }
>  
> +    /* For 64bit systems think about the number of physical bits to present.
> +     * ideally this should be the same as the host; anything other than matching
> +     * the host can cause incorrect guest behaviour.
> +     * QEMU used to pick the magic value of 40 bits that corresponds to
> +     * consumer AMD devices but nothing else.
> +     */
> +    if (cpu->host_phys_bits && !kvm_enabled()) {
> +        error_setg(errp, "phys-bits can not be read from the host in"
> +                         " TCG mode");
> +        return;
> +    }
> +
>      if (env->features[FEAT_8000_0001_EDX] & CPUID_EXT2_LM) {
> -        /* 0 is a special meaning 'use the old default', which matches
> -         * the value used by TCG (40).
> -         */
> -        if (cpu->phys_bits == 0) {
> -            cpu->phys_bits = TCG_PHYS_ADDR_BITS;
> -        }
>          if (kvm_enabled()) {
> -            if (cpu->phys_bits > TARGET_PHYS_ADDR_SPACE_BITS ||
> -                cpu->phys_bits < 32) {
> +            uint32_t host_phys_bits = x86_host_phys_bits();
> +            static bool warned;
> +
> +            if (cpu->host_phys_bits) {
> +                /* The user asked for us to use the host physical bits */
> +                cpu->phys_bits = host_phys_bits;
> +            }
> +
> +            /* Print a warning if the user set it to a value that's not the
> +             * host value.
> +             */
> +            if (cpu->phys_bits != host_phys_bits && cpu->phys_bits != 0 &&
> +                !warned) {
> +                error_report("Warning: Host physical bits (%u)"
> +                                 " does not match phys-bits property (%u)",
> +                                 host_phys_bits, cpu->phys_bits);
> +                warned = true;
> +            }
> +
> +            if (cpu->phys_bits &&
> +                (cpu->phys_bits > TARGET_PHYS_ADDR_SPACE_BITS ||
> +                cpu->phys_bits < 32)) {
>                  error_setg(errp, "phys-bits should be between 32 and %u "
>                                   " (but is %u)",
>                                   TARGET_PHYS_ADDR_SPACE_BITS, cpu->phys_bits);
>                  return;
>              }
>          } else {
> -            if (cpu->phys_bits != TCG_PHYS_ADDR_BITS) {
> +            if (cpu->phys_bits && cpu->phys_bits != TCG_PHYS_ADDR_BITS) {
>                  error_setg(errp, "TCG only supports phys-bits=%u",
>                                    TCG_PHYS_ADDR_BITS);
>                  return;
>              }
>          }
> +        if (cpu->phys_bits == 0 && !cpu->host_phys_bits) {

Why the !cpu->host_phys_bits check? It seems to be impossible to
have (cpu->host_phys_bits == true && cpu->phys_bits == 0) here.

> +            cpu->phys_bits = TCG_PHYS_ADDR_BITS;
> +        }
>      } else {
>          /* For 32 bit systems don't use the user set value, but keep
>           * phys_bits consistent with what we tell the guest.

Shouldn't we return error if host-phys-bits is set in 32-bit
mode?

> @@ -3290,6 +3344,7 @@ static Property x86_cpu_properties[] = {
>      DEFINE_PROP_BOOL("enforce", X86CPU, enforce_cpuid, false),
>      DEFINE_PROP_BOOL("kvm", X86CPU, expose_kvm, true),
>      DEFINE_PROP_UINT32("phys-bits", X86CPU, phys_bits, 0),
> +    DEFINE_PROP_BOOL("host-phys-bits", X86CPU, host_phys_bits, false),
>      DEFINE_PROP_BOOL("fill-mtrr-mask", X86CPU, fill_mtrr_mask, true),
>      DEFINE_PROP_UINT32("level", X86CPU, env.cpuid_level, 0),
>      DEFINE_PROP_UINT32("xlevel", X86CPU, env.cpuid_xlevel, 0),
> diff --git a/target-i386/cpu.h b/target-i386/cpu.h
> index 9d79146..3c4e64a 100644
> --- a/target-i386/cpu.h
> +++ b/target-i386/cpu.h
> @@ -1184,6 +1184,9 @@ struct X86CPU {
>      /* if true fill the top bits of the MTRR_PHYSMASKn variable range */
>      bool fill_mtrr_mask;
>  
> +    /* if true override the phys_bits value with a value read from the host */
> +    bool host_phys_bits;
> +
>      /* Number of physical address bits supported */
>      uint32_t phys_bits;
>  
> -- 
> 2.7.4
>
Dr. David Alan Gilbert July 11, 2016, 12:29 p.m. UTC | #3
* Eduardo Habkost (ehabkost@redhat.com) wrote:
> On Fri, Jul 08, 2016 at 04:01:39PM +0100, Dr. David Alan Gilbert (git) wrote:
> > From: "Dr. David Alan Gilbert" <dgilbert@redhat.com>
> > 
> > Add the host-phys-bits boolean property, if true, take phys-bits
> > from the hosts physical bits value, overriding either the default
> > or the user specified value.
> > 
> > We can also use the value we read from the host to check the users
> > explicitly set value and warn them if it doesn't match.
> > 
> > Signed-off-by: Dr. David Alan Gilbert <dgilbert@redhat.com>
> > ---
> [...]
> > @@ -2952,28 +2977,57 @@ static void x86_cpu_realizefn(DeviceState *dev, Error **errp)
> >             & CPUID_EXT2_AMD_ALIASES);
> >      }
> >  
> > +    /* For 64bit systems think about the number of physical bits to present.
> > +     * ideally this should be the same as the host; anything other than matching
> > +     * the host can cause incorrect guest behaviour.
> > +     * QEMU used to pick the magic value of 40 bits that corresponds to
> > +     * consumer AMD devices but nothing else.
> > +     */
> > +    if (cpu->host_phys_bits && !kvm_enabled()) {
> > +        error_setg(errp, "phys-bits can not be read from the host in"
> > +                         " TCG mode");
> > +        return;
> > +    }
> > +
> >      if (env->features[FEAT_8000_0001_EDX] & CPUID_EXT2_LM) {
> > -        /* 0 is a special meaning 'use the old default', which matches
> > -         * the value used by TCG (40).
> > -         */
> > -        if (cpu->phys_bits == 0) {
> > -            cpu->phys_bits = TCG_PHYS_ADDR_BITS;
> > -        }
> >          if (kvm_enabled()) {
> > -            if (cpu->phys_bits > TARGET_PHYS_ADDR_SPACE_BITS ||
> > -                cpu->phys_bits < 32) {
> > +            uint32_t host_phys_bits = x86_host_phys_bits();
> > +            static bool warned;
> > +
> > +            if (cpu->host_phys_bits) {
> > +                /* The user asked for us to use the host physical bits */
> > +                cpu->phys_bits = host_phys_bits;
> > +            }
> > +
> > +            /* Print a warning if the user set it to a value that's not the
> > +             * host value.
> > +             */
> > +            if (cpu->phys_bits != host_phys_bits && cpu->phys_bits != 0 &&
> > +                !warned) {
> > +                error_report("Warning: Host physical bits (%u)"
> > +                                 " does not match phys-bits property (%u)",
> > +                                 host_phys_bits, cpu->phys_bits);
> > +                warned = true;
> > +            }
> > +
> > +            if (cpu->phys_bits &&
> > +                (cpu->phys_bits > TARGET_PHYS_ADDR_SPACE_BITS ||
> > +                cpu->phys_bits < 32)) {
> >                  error_setg(errp, "phys-bits should be between 32 and %u "
> >                                   " (but is %u)",
> >                                   TARGET_PHYS_ADDR_SPACE_BITS, cpu->phys_bits);
> >                  return;
> >              }
> >          } else {
> > -            if (cpu->phys_bits != TCG_PHYS_ADDR_BITS) {
> > +            if (cpu->phys_bits && cpu->phys_bits != TCG_PHYS_ADDR_BITS) {
> >                  error_setg(errp, "TCG only supports phys-bits=%u",
> >                                    TCG_PHYS_ADDR_BITS);
> >                  return;
> >              }
> >          }
> > +        if (cpu->phys_bits == 0 && !cpu->host_phys_bits) {
> 
> Why the !cpu->host_phys_bits check? It seems to be impossible to
> have (cpu->host_phys_bits == true && cpu->phys_bits == 0) here.

Right; I think I added that before I added the TCG check at the top.

> > +            cpu->phys_bits = TCG_PHYS_ADDR_BITS;
> > +        }
> >      } else {
> >          /* For 32 bit systems don't use the user set value, but keep
> >           * phys_bits consistent with what we tell the guest.
> 
> Shouldn't we return error if host-phys-bits is set in 32-bit
> mode?

Yes; easy to add.

Do you want me to repost just this patch or the whole set?

Dave

> > @@ -3290,6 +3344,7 @@ static Property x86_cpu_properties[] = {
> >      DEFINE_PROP_BOOL("enforce", X86CPU, enforce_cpuid, false),
> >      DEFINE_PROP_BOOL("kvm", X86CPU, expose_kvm, true),
> >      DEFINE_PROP_UINT32("phys-bits", X86CPU, phys_bits, 0),
> > +    DEFINE_PROP_BOOL("host-phys-bits", X86CPU, host_phys_bits, false),
> >      DEFINE_PROP_BOOL("fill-mtrr-mask", X86CPU, fill_mtrr_mask, true),
> >      DEFINE_PROP_UINT32("level", X86CPU, env.cpuid_level, 0),
> >      DEFINE_PROP_UINT32("xlevel", X86CPU, env.cpuid_xlevel, 0),
> > diff --git a/target-i386/cpu.h b/target-i386/cpu.h
> > index 9d79146..3c4e64a 100644
> > --- a/target-i386/cpu.h
> > +++ b/target-i386/cpu.h
> > @@ -1184,6 +1184,9 @@ struct X86CPU {
> >      /* if true fill the top bits of the MTRR_PHYSMASKn variable range */
> >      bool fill_mtrr_mask;
> >  
> > +    /* if true override the phys_bits value with a value read from the host */
> > +    bool host_phys_bits;
> > +
> >      /* Number of physical address bits supported */
> >      uint32_t phys_bits;
> >  
> > -- 
> > 2.7.4
> > 
> 
> -- 
> Eduardo
--
Dr. David Alan Gilbert / dgilbert@redhat.com / Manchester, UK
Eduardo Habkost July 11, 2016, 1:01 p.m. UTC | #4
On Mon, Jul 11, 2016 at 01:29:56PM +0100, Dr. David Alan Gilbert wrote:
[...]
> 
> Do you want me to repost just this patch or the whole set?

You can use my x86-next branch as base (it already includes the
other patches), so you can repost just this patch.

 git://github.com/ehabkost/qemu.git x86-next
Dr. David Alan Gilbert July 11, 2016, 3:39 p.m. UTC | #5
* Eduardo Habkost (ehabkost@redhat.com) wrote:
> On Fri, Jul 08, 2016 at 04:01:39PM +0100, Dr. David Alan Gilbert (git) wrote:
> > From: "Dr. David Alan Gilbert" <dgilbert@redhat.com>
> > 
> > Add the host-phys-bits boolean property, if true, take phys-bits
> > from the hosts physical bits value, overriding either the default
> > or the user specified value.
> > 
> > We can also use the value we read from the host to check the users
> > explicitly set value and warn them if it doesn't match.
> > 
> > Signed-off-by: Dr. David Alan Gilbert <dgilbert@redhat.com>
> > ---
> [...]
> > @@ -2952,28 +2977,57 @@ static void x86_cpu_realizefn(DeviceState *dev, Error **errp)
> >             & CPUID_EXT2_AMD_ALIASES);
> >      }
> >  
> > +    /* For 64bit systems think about the number of physical bits to present.
> > +     * ideally this should be the same as the host; anything other than matching
> > +     * the host can cause incorrect guest behaviour.
> > +     * QEMU used to pick the magic value of 40 bits that corresponds to
> > +     * consumer AMD devices but nothing else.
> > +     */
> > +    if (cpu->host_phys_bits && !kvm_enabled()) {
> > +        error_setg(errp, "phys-bits can not be read from the host in"
> > +                         " TCG mode");
> > +        return;
> > +    }
> > +
> >      if (env->features[FEAT_8000_0001_EDX] & CPUID_EXT2_LM) {
> > -        /* 0 is a special meaning 'use the old default', which matches
> > -         * the value used by TCG (40).
> > -         */
> > -        if (cpu->phys_bits == 0) {
> > -            cpu->phys_bits = TCG_PHYS_ADDR_BITS;
> > -        }
> >          if (kvm_enabled()) {
> > -            if (cpu->phys_bits > TARGET_PHYS_ADDR_SPACE_BITS ||
> > -                cpu->phys_bits < 32) {
> > +            uint32_t host_phys_bits = x86_host_phys_bits();
> > +            static bool warned;
> > +
> > +            if (cpu->host_phys_bits) {
> > +                /* The user asked for us to use the host physical bits */
> > +                cpu->phys_bits = host_phys_bits;
> > +            }
> > +
> > +            /* Print a warning if the user set it to a value that's not the
> > +             * host value.
> > +             */
> > +            if (cpu->phys_bits != host_phys_bits && cpu->phys_bits != 0 &&
> > +                !warned) {
> > +                error_report("Warning: Host physical bits (%u)"
> > +                                 " does not match phys-bits property (%u)",
> > +                                 host_phys_bits, cpu->phys_bits);
> > +                warned = true;
> > +            }
> > +
> > +            if (cpu->phys_bits &&
> > +                (cpu->phys_bits > TARGET_PHYS_ADDR_SPACE_BITS ||
> > +                cpu->phys_bits < 32)) {
> >                  error_setg(errp, "phys-bits should be between 32 and %u "
> >                                   " (but is %u)",
> >                                   TARGET_PHYS_ADDR_SPACE_BITS, cpu->phys_bits);
> >                  return;
> >              }
> >          } else {
> > -            if (cpu->phys_bits != TCG_PHYS_ADDR_BITS) {
> > +            if (cpu->phys_bits && cpu->phys_bits != TCG_PHYS_ADDR_BITS) {
> >                  error_setg(errp, "TCG only supports phys-bits=%u",
> >                                    TCG_PHYS_ADDR_BITS);
> >                  return;
> >              }
> >          }
> > +        if (cpu->phys_bits == 0 && !cpu->host_phys_bits) {
> 
> Why the !cpu->host_phys_bits check? It seems to be impossible to
> have (cpu->host_phys_bits == true && cpu->phys_bits == 0) here.
> 
> > +            cpu->phys_bits = TCG_PHYS_ADDR_BITS;
> > +        }
> >      } else {
> >          /* For 32 bit systems don't use the user set value, but keep
> >           * phys_bits consistent with what we tell the guest.
> 
> Shouldn't we return error if host-phys-bits is set in 32-bit
> mode?

I've just realised there's a reason that erroring in this case is a problem.
Imagine a future (or downstream) machine type that made host-phys-bits the default;
how would it run with a 32bit CPU?

Dave

> > @@ -3290,6 +3344,7 @@ static Property x86_cpu_properties[] = {
> >      DEFINE_PROP_BOOL("enforce", X86CPU, enforce_cpuid, false),
> >      DEFINE_PROP_BOOL("kvm", X86CPU, expose_kvm, true),
> >      DEFINE_PROP_UINT32("phys-bits", X86CPU, phys_bits, 0),
> > +    DEFINE_PROP_BOOL("host-phys-bits", X86CPU, host_phys_bits, false),
> >      DEFINE_PROP_BOOL("fill-mtrr-mask", X86CPU, fill_mtrr_mask, true),
> >      DEFINE_PROP_UINT32("level", X86CPU, env.cpuid_level, 0),
> >      DEFINE_PROP_UINT32("xlevel", X86CPU, env.cpuid_xlevel, 0),
> > diff --git a/target-i386/cpu.h b/target-i386/cpu.h
> > index 9d79146..3c4e64a 100644
> > --- a/target-i386/cpu.h
> > +++ b/target-i386/cpu.h
> > @@ -1184,6 +1184,9 @@ struct X86CPU {
> >      /* if true fill the top bits of the MTRR_PHYSMASKn variable range */
> >      bool fill_mtrr_mask;
> >  
> > +    /* if true override the phys_bits value with a value read from the host */
> > +    bool host_phys_bits;
> > +
> >      /* Number of physical address bits supported */
> >      uint32_t phys_bits;
> >  
> > -- 
> > 2.7.4
> > 
> 
> -- 
> Eduardo
--
Dr. David Alan Gilbert / dgilbert@redhat.com / Manchester, UK
Eduardo Habkost July 11, 2016, 6:42 p.m. UTC | #6
On Mon, Jul 11, 2016 at 04:39:22PM +0100, Dr. David Alan Gilbert wrote:
> * Eduardo Habkost (ehabkost@redhat.com) wrote:
[...]
> > > +            cpu->phys_bits = TCG_PHYS_ADDR_BITS;
> > > +        }
> > >      } else {
> > >          /* For 32 bit systems don't use the user set value, but keep
> > >           * phys_bits consistent with what we tell the guest.
> > 
> > Shouldn't we return error if host-phys-bits is set in 32-bit
> > mode?
> 
> I've just realised there's a reason that erroring in this case is a problem.
> Imagine a future (or downstream) machine type that made host-phys-bits the default;
> how would it run with a 32bit CPU?

Oh, that's right. Ignoring it when explicitly set isn't
intuitive, but we need to be able to ignore it when set by a
machine compat_props (or if it's enabled by default). And
creating two separate properties sounds like overkill...

I'm reluctant, but I think it's OK to ignore it if LM is not set.
But we need to make sure it's documented somewhere.
Dr. David Alan Gilbert July 11, 2016, 6:47 p.m. UTC | #7
* Eduardo Habkost (ehabkost@redhat.com) wrote:
> On Mon, Jul 11, 2016 at 04:39:22PM +0100, Dr. David Alan Gilbert wrote:
> > * Eduardo Habkost (ehabkost@redhat.com) wrote:
> [...]
> > > > +            cpu->phys_bits = TCG_PHYS_ADDR_BITS;
> > > > +        }
> > > >      } else {
> > > >          /* For 32 bit systems don't use the user set value, but keep
> > > >           * phys_bits consistent with what we tell the guest.
> > > 
> > > Shouldn't we return error if host-phys-bits is set in 32-bit
> > > mode?
> > 
> > I've just realised there's a reason that erroring in this case is a problem.
> > Imagine a future (or downstream) machine type that made host-phys-bits the default;
> > how would it run with a 32bit CPU?
> 
> Oh, that's right. Ignoring it when explicitly set isn't
> intuitive, but we need to be able to ignore it when set by a
> machine compat_props (or if it's enabled by default). And
> creating two separate properties sounds like overkill...
> I'm reluctant, but I think it's OK to ignore it if LM is not set.
> But we need to make sure it's documented somewhere.

OK, I'll add a comment and a note in the commit message.

Dave

> 
> -- 
> Eduardo
--
Dr. David Alan Gilbert / dgilbert@redhat.com / Manchester, UK
diff mbox

Patch

diff --git a/target-i386/cpu.c b/target-i386/cpu.c
index 641c38b..72ca7fb 100644
--- a/target-i386/cpu.c
+++ b/target-i386/cpu.c
@@ -2885,6 +2885,31 @@  static void x86_cpu_apic_realize(X86CPU *cpu, Error **errp)
 }
 #endif
 
+/* Note: Only safe for use on x86(-64) hosts */
+static uint32_t x86_host_phys_bits(void)
+{
+    uint32_t eax;
+    uint32_t host_phys_bits;
+
+    host_cpuid(0x80000000, 0, &eax, NULL, NULL, NULL);
+    if (eax >= 0x80000008) {
+        host_cpuid(0x80000008, 0, &eax, NULL, NULL, NULL);
+        /* Note: According to AMD doc 25481 rev 2.34 they have a field
+         * at 23:16 that can specify a maximum physical address bits for
+         * the guest that can override this value; but I've not seen
+         * anything with that set.
+         */
+        host_phys_bits = eax & 0xff;
+    } else {
+        /* It's an odd 64 bit machine that doesn't have the leaf for
+         * physical address bits; fall back to 36 that's most older
+         * Intel.
+         */
+        host_phys_bits = 36;
+    }
+
+    return host_phys_bits;
+}
 
 #define IS_INTEL_CPU(env) ((env)->cpuid_vendor1 == CPUID_VENDOR_INTEL_1 && \
                            (env)->cpuid_vendor2 == CPUID_VENDOR_INTEL_2 && \
@@ -2952,28 +2977,57 @@  static void x86_cpu_realizefn(DeviceState *dev, Error **errp)
            & CPUID_EXT2_AMD_ALIASES);
     }
 
+    /* For 64bit systems think about the number of physical bits to present.
+     * ideally this should be the same as the host; anything other than matching
+     * the host can cause incorrect guest behaviour.
+     * QEMU used to pick the magic value of 40 bits that corresponds to
+     * consumer AMD devices but nothing else.
+     */
+    if (cpu->host_phys_bits && !kvm_enabled()) {
+        error_setg(errp, "phys-bits can not be read from the host in"
+                         " TCG mode");
+        return;
+    }
+
     if (env->features[FEAT_8000_0001_EDX] & CPUID_EXT2_LM) {
-        /* 0 is a special meaning 'use the old default', which matches
-         * the value used by TCG (40).
-         */
-        if (cpu->phys_bits == 0) {
-            cpu->phys_bits = TCG_PHYS_ADDR_BITS;
-        }
         if (kvm_enabled()) {
-            if (cpu->phys_bits > TARGET_PHYS_ADDR_SPACE_BITS ||
-                cpu->phys_bits < 32) {
+            uint32_t host_phys_bits = x86_host_phys_bits();
+            static bool warned;
+
+            if (cpu->host_phys_bits) {
+                /* The user asked for us to use the host physical bits */
+                cpu->phys_bits = host_phys_bits;
+            }
+
+            /* Print a warning if the user set it to a value that's not the
+             * host value.
+             */
+            if (cpu->phys_bits != host_phys_bits && cpu->phys_bits != 0 &&
+                !warned) {
+                error_report("Warning: Host physical bits (%u)"
+                                 " does not match phys-bits property (%u)",
+                                 host_phys_bits, cpu->phys_bits);
+                warned = true;
+            }
+
+            if (cpu->phys_bits &&
+                (cpu->phys_bits > TARGET_PHYS_ADDR_SPACE_BITS ||
+                cpu->phys_bits < 32)) {
                 error_setg(errp, "phys-bits should be between 32 and %u "
                                  " (but is %u)",
                                  TARGET_PHYS_ADDR_SPACE_BITS, cpu->phys_bits);
                 return;
             }
         } else {
-            if (cpu->phys_bits != TCG_PHYS_ADDR_BITS) {
+            if (cpu->phys_bits && cpu->phys_bits != TCG_PHYS_ADDR_BITS) {
                 error_setg(errp, "TCG only supports phys-bits=%u",
                                   TCG_PHYS_ADDR_BITS);
                 return;
             }
         }
+        if (cpu->phys_bits == 0 && !cpu->host_phys_bits) {
+            cpu->phys_bits = TCG_PHYS_ADDR_BITS;
+        }
     } else {
         /* For 32 bit systems don't use the user set value, but keep
          * phys_bits consistent with what we tell the guest.
@@ -3290,6 +3344,7 @@  static Property x86_cpu_properties[] = {
     DEFINE_PROP_BOOL("enforce", X86CPU, enforce_cpuid, false),
     DEFINE_PROP_BOOL("kvm", X86CPU, expose_kvm, true),
     DEFINE_PROP_UINT32("phys-bits", X86CPU, phys_bits, 0),
+    DEFINE_PROP_BOOL("host-phys-bits", X86CPU, host_phys_bits, false),
     DEFINE_PROP_BOOL("fill-mtrr-mask", X86CPU, fill_mtrr_mask, true),
     DEFINE_PROP_UINT32("level", X86CPU, env.cpuid_level, 0),
     DEFINE_PROP_UINT32("xlevel", X86CPU, env.cpuid_xlevel, 0),
diff --git a/target-i386/cpu.h b/target-i386/cpu.h
index 9d79146..3c4e64a 100644
--- a/target-i386/cpu.h
+++ b/target-i386/cpu.h
@@ -1184,6 +1184,9 @@  struct X86CPU {
     /* if true fill the top bits of the MTRR_PHYSMASKn variable range */
     bool fill_mtrr_mask;
 
+    /* if true override the phys_bits value with a value read from the host */
+    bool host_phys_bits;
+
     /* Number of physical address bits supported */
     uint32_t phys_bits;