diff mbox series

[for-4.19?,v5,07/10] xen: Make the maximum number of altp2m views configurable for x86

Message ID 84794f97bc738add96a66790425a3aa5f5084a25.1717356829.git.w1benny@gmail.com (mailing list archive)
State Superseded
Headers show
Series x86: Make MAX_ALTP2M configurable | expand

Commit Message

Petr Beneš June 2, 2024, 8:04 p.m. UTC
From: Petr Beneš <w1benny@gmail.com>

x86: Make the maximum number of altp2m views configurable

This commit introduces the ability to configure the maximum number of altp2m
views for the domain during its creation. Previously, the limits were hardcoded
to a maximum of 10. This change allows for greater flexibility in environments
that require more or fewer altp2m views.

The maximum configurable limit for nr_altp2m on x86 is now set to
MAX_NR_ALTP2M (which currently holds the MAX_EPTP value - 512). This cap is
linked to the architectural limit of the EPTP-switching VMFUNC, which supports
up to 512 entries. Despite there being no inherent need for limiting nr_altp2m
in scenarios not utilizing VMFUNC, decoupling these components would necessitate
substantial code changes.

xen_domctl_createdomain::altp2m is extended for a new field `nr`, that will
configure this limit for a domain. Additionally, previous altp2m.opts value
has been reduced from uint32_t to uint16_t so that both of these fields occupy
as little space as possible.

altp2m_get_p2m() function is modified to respect the new nr_altp2m value.
Accessor functions that operate on EPT arrays are unmodified, since these
arrays always have fixed size of MAX_EPTP.

A dummy hvm_altp2m_supported() function is introduced for non-HVM builds, so
that the compilation won't fail for them.

Additional sanitization is introduced in the x86/arch_sanitise_domain_config
to fix the altp2m.nr value to 10 if it is previously set to 0. This behavior
is only temporary and immediately removed in the upcoming commit (which will
disallow creating a domain with enabled altp2m with zero nr_altp2m).

The reason for this temporary workaround is to retain the legacy behavior
until the feature is fully activated in libxl.

Also, arm/arch_sanitise_domain_config is extended to not allow requesting
non-zero altp2ms.

Signed-off-by: Petr Beneš <w1benny@gmail.com>
---
 xen/arch/arm/domain.c              |  2 +-
 xen/arch/x86/domain.c              | 40 ++++++++++++++++++----
 xen/arch/x86/hvm/hvm.c             |  8 ++++-
 xen/arch/x86/hvm/vmx/vmx.c         |  2 +-
 xen/arch/x86/include/asm/altp2m.h  |  2 +-
 xen/arch/x86/include/asm/domain.h  |  9 ++---
 xen/arch/x86/include/asm/hvm/hvm.h |  5 +++
 xen/arch/x86/include/asm/p2m.h     |  4 +--
 xen/arch/x86/mm/altp2m.c           | 54 ++++++++++++++++++++----------
 xen/arch/x86/mm/hap/hap.c          |  8 ++---
 xen/arch/x86/mm/mem_access.c       |  8 ++---
 xen/arch/x86/mm/mem_sharing.c      |  2 +-
 xen/arch/x86/mm/p2m-ept.c          |  4 +--
 xen/arch/x86/mm/p2m.c              |  8 ++---
 xen/common/domain.c                |  1 +
 xen/include/public/domctl.h        |  5 ++-
 xen/include/xen/sched.h            |  2 ++
 17 files changed, 113 insertions(+), 51 deletions(-)

--
2.34.1

Comments

Julien Grall June 4, 2024, 11:20 a.m. UTC | #1
Hi Petr,

On 02/06/2024 21:04, Petr Beneš wrote:
> From: Petr Beneš <w1benny@gmail.com>
> 
> x86: Make the maximum number of altp2m views configurable
> 
> This commit introduces the ability to configure the maximum number of altp2m
> views for the domain during its creation. Previously, the limits were hardcoded
> to a maximum of 10. This change allows for greater flexibility in environments
> that require more or fewer altp2m views.
> 
> The maximum configurable limit for nr_altp2m on x86 is now set to
> MAX_NR_ALTP2M (which currently holds the MAX_EPTP value - 512). This cap is
> linked to the architectural limit of the EPTP-switching VMFUNC, which supports
> up to 512 entries. Despite there being no inherent need for limiting nr_altp2m
> in scenarios not utilizing VMFUNC, decoupling these components would necessitate
> substantial code changes.
> 
> xen_domctl_createdomain::altp2m is extended for a new field `nr`, that will
> configure this limit for a domain. Additionally, previous altp2m.opts value
> has been reduced from uint32_t to uint16_t so that both of these fields occupy
> as little space as possible.
> 
> altp2m_get_p2m() function is modified to respect the new nr_altp2m value.
> Accessor functions that operate on EPT arrays are unmodified, since these
> arrays always have fixed size of MAX_EPTP.
> 
> A dummy hvm_altp2m_supported() function is introduced for non-HVM builds, so
> that the compilation won't fail for them.
> 
> Additional sanitization is introduced in the x86/arch_sanitise_domain_config
> to fix the altp2m.nr value to 10 if it is previously set to 0. This behavior
> is only temporary and immediately removed in the upcoming commit (which will
> disallow creating a domain with enabled altp2m with zero nr_altp2m).
> 
> The reason for this temporary workaround is to retain the legacy behavior
> until the feature is fully activated in libxl.
> 
> Also, arm/arch_sanitise_domain_config is extended to not allow requesting
> non-zero altp2ms.
> 
> Signed-off-by: Petr Beneš <w1benny@gmail.com>

For the small change in Arm:

Acked-by: Julien Grall <jgrall@amazon.com> # arm

Cheers,
Jan Beulich June 6, 2024, 7:24 a.m. UTC | #2
On 02.06.2024 22:04, Petr Beneš wrote:
> @@ -5245,7 +5251,7 @@ void hvm_fast_singlestep(struct vcpu *v, uint16_t p2midx)
>      if ( !hvm_is_singlestep_supported() )
>          return;
> 
> -    if ( p2midx >= MAX_ALTP2M )
> +    if ( p2midx >= v->domain->nr_altp2m )
>          return;

As (iirc) indicated before, just like you don't add a d local variable
here or ...

> --- a/xen/arch/x86/include/asm/p2m.h
> +++ b/xen/arch/x86/include/asm/p2m.h
> @@ -887,7 +887,7 @@ static inline struct p2m_domain *p2m_get_altp2m(struct vcpu *v)
>      if ( index == INVALID_ALTP2M )
>          return NULL;
> 
> -    BUG_ON(index >= MAX_ALTP2M);
> +    BUG_ON(index >= v->domain->nr_altp2m);
> 
>      return altp2m_get_p2m(v->domain, index);
>  }
> @@ -898,7 +898,7 @@ static inline bool p2m_set_altp2m(struct vcpu *v, unsigned int idx)
>      struct p2m_domain *orig;
>      struct p2m_domain *ap2m;
> 
> -    BUG_ON(idx >= MAX_ALTP2M);
> +    BUG_ON(idx >= v->domain->nr_altp2m);
> 
>      if ( idx == vcpu_altp2m(v).p2midx )
>          return false;

... in either of these, I see little reason to have such ...

> --- a/xen/arch/x86/mm/altp2m.c
> +++ b/xen/arch/x86/mm/altp2m.c
> @@ -15,6 +15,11 @@
>  void
>  altp2m_vcpu_initialise(struct vcpu *v)
>  {
> +    struct domain *d = v->domain;
> +
> +    if ( d->nr_altp2m == 0 )
> +        return;
> +
>      if ( v != current )
>          vcpu_pause(v);
> 
> @@ -30,8 +35,12 @@ altp2m_vcpu_initialise(struct vcpu *v)
>  void
>  altp2m_vcpu_destroy(struct vcpu *v)
>  {
> +    struct domain *d = v->domain;
>      struct p2m_domain *p2m;
> 
> +    if ( d->nr_altp2m == 0 )
> +        return;
> +
>      if ( v != current )
>          vcpu_pause(v);

... in both of these.

> @@ -122,7 +131,12 @@ int p2m_init_altp2m(struct domain *d)
>      struct p2m_domain *hostp2m = p2m_get_hostp2m(d);
> 
>      mm_lock_init(&d->arch.altp2m_list_lock);
> -    for ( i = 0; i < MAX_ALTP2M; i++ )
> +    d->arch.altp2m_p2m = xzalloc_array(struct p2m_domain *, d->nr_altp2m);
> +
> +    if ( !d->arch.altp2m_p2m )
> +        return -ENOMEM;

This isn't really needed, is it? Both ...

> +    for ( i = 0; i < d->nr_altp2m; i++ )

... this and ...

>      {
>          d->arch.altp2m_p2m[i] = p2m = p2m_init_one(d);
>          if ( p2m == NULL )
> @@ -143,7 +157,10 @@ void p2m_teardown_altp2m(struct domain *d)
>      unsigned int i;
>      struct p2m_domain *p2m;
> 
> -    for ( i = 0; i < MAX_ALTP2M; i++ )
> +    if ( !d->arch.altp2m_p2m )
> +        return;
> +
> +    for ( i = 0; i < d->nr_altp2m; i++ )
>      {
>          if ( !d->arch.altp2m_p2m[i] )
>              continue;
> @@ -151,6 +168,8 @@ void p2m_teardown_altp2m(struct domain *d)
>          d->arch.altp2m_p2m[i] = NULL;
>          p2m_free_one(p2m);
>      }
> +
> +    XFREE(d->arch.altp2m_p2m);
>  }

... this ought to be fine without?

> @@ -538,8 +538,8 @@ void hap_final_teardown(struct domain *d)
>      unsigned int i;
> 
>      if ( hvm_altp2m_supported() )
> -        for ( i = 0; i < MAX_ALTP2M; i++ )
> -            p2m_teardown(d->arch.altp2m_p2m[i], true, NULL);
> +        for ( i = 0; i < d->nr_altp2m; i++ )
> +            p2m_teardown(altp2m_get_p2m(d, i), true, NULL);

Shouldn't the switch to altp2m_get_p2m() be part of the respective
earlier patch?

Jan
Petr Beneš June 8, 2024, 11:06 p.m. UTC | #3
On Thu, Jun 6, 2024 at 9:24 AM Jan Beulich <jbeulich@suse.com> wrote:
> > @@ -122,7 +131,12 @@ int p2m_init_altp2m(struct domain *d)
> >      struct p2m_domain *hostp2m = p2m_get_hostp2m(d);
> >
> >      mm_lock_init(&d->arch.altp2m_list_lock);
> > -    for ( i = 0; i < MAX_ALTP2M; i++ )
> > +    d->arch.altp2m_p2m = xzalloc_array(struct p2m_domain *, d->nr_altp2m);
> > +
> > +    if ( !d->arch.altp2m_p2m )
> > +        return -ENOMEM;
>
> This isn't really needed, is it? Both ...
>
> > +    for ( i = 0; i < d->nr_altp2m; i++ )
>
> ... this and ...
>
> >      {
> >          d->arch.altp2m_p2m[i] = p2m = p2m_init_one(d);
> >          if ( p2m == NULL )
> > @@ -143,7 +157,10 @@ void p2m_teardown_altp2m(struct domain *d)
> >      unsigned int i;
> >      struct p2m_domain *p2m;
> >
> > -    for ( i = 0; i < MAX_ALTP2M; i++ )
> > +    if ( !d->arch.altp2m_p2m )
> > +        return;
> > +
> > +    for ( i = 0; i < d->nr_altp2m; i++ )
> >      {
> >          if ( !d->arch.altp2m_p2m[i] )
> >              continue;
> > @@ -151,6 +168,8 @@ void p2m_teardown_altp2m(struct domain *d)
> >          d->arch.altp2m_p2m[i] = NULL;
> >          p2m_free_one(p2m);
> >      }
> > +
> > +    XFREE(d->arch.altp2m_p2m);
> >  }
>
> ... this ought to be fine without?

Could you, please, elaborate? I honestly don't know what you mean here
(by "this isn't needed").

P.
Jan Beulich June 10, 2024, 7:30 a.m. UTC | #4
On 09.06.2024 01:06, Petr Beneš wrote:
> On Thu, Jun 6, 2024 at 9:24 AM Jan Beulich <jbeulich@suse.com> wrote:
>>> @@ -122,7 +131,12 @@ int p2m_init_altp2m(struct domain *d)
>>>      struct p2m_domain *hostp2m = p2m_get_hostp2m(d);
>>>
>>>      mm_lock_init(&d->arch.altp2m_list_lock);
>>> -    for ( i = 0; i < MAX_ALTP2M; i++ )
>>> +    d->arch.altp2m_p2m = xzalloc_array(struct p2m_domain *, d->nr_altp2m);
>>> +
>>> +    if ( !d->arch.altp2m_p2m )
>>> +        return -ENOMEM;
>>
>> This isn't really needed, is it? Both ...
>>
>>> +    for ( i = 0; i < d->nr_altp2m; i++ )
>>
>> ... this and ...
>>
>>>      {
>>>          d->arch.altp2m_p2m[i] = p2m = p2m_init_one(d);
>>>          if ( p2m == NULL )
>>> @@ -143,7 +157,10 @@ void p2m_teardown_altp2m(struct domain *d)
>>>      unsigned int i;
>>>      struct p2m_domain *p2m;
>>>
>>> -    for ( i = 0; i < MAX_ALTP2M; i++ )
>>> +    if ( !d->arch.altp2m_p2m )
>>> +        return;

I'm sorry, the question was meant to be on this if() instead.

>>> +    for ( i = 0; i < d->nr_altp2m; i++ )
>>>      {
>>>          if ( !d->arch.altp2m_p2m[i] )
>>>              continue;
>>> @@ -151,6 +168,8 @@ void p2m_teardown_altp2m(struct domain *d)
>>>          d->arch.altp2m_p2m[i] = NULL;
>>>          p2m_free_one(p2m);
>>>      }
>>> +
>>> +    XFREE(d->arch.altp2m_p2m);
>>>  }
>>
>> ... this ought to be fine without?
> 
> Could you, please, elaborate? I honestly don't know what you mean here
> (by "this isn't needed").

I hope the above correction is enough?

Jan
Petr Beneš June 10, 2024, 9:10 a.m. UTC | #5
On Mon, Jun 10, 2024 at 9:30 AM Jan Beulich <jbeulich@suse.com> wrote:
>
> On 09.06.2024 01:06, Petr Beneš wrote:
> > On Thu, Jun 6, 2024 at 9:24 AM Jan Beulich <jbeulich@suse.com> wrote:
> >>> @@ -122,7 +131,12 @@ int p2m_init_altp2m(struct domain *d)
> >>>      struct p2m_domain *hostp2m = p2m_get_hostp2m(d);
> >>>
> >>>      mm_lock_init(&d->arch.altp2m_list_lock);
> >>> -    for ( i = 0; i < MAX_ALTP2M; i++ )
> >>> +    d->arch.altp2m_p2m = xzalloc_array(struct p2m_domain *, d->nr_altp2m);
> >>> +
> >>> +    if ( !d->arch.altp2m_p2m )
> >>> +        return -ENOMEM;
> >>
> >> This isn't really needed, is it? Both ...
> >>
> >>> +    for ( i = 0; i < d->nr_altp2m; i++ )
> >>
> >> ... this and ...
> >>
> >>>      {
> >>>          d->arch.altp2m_p2m[i] = p2m = p2m_init_one(d);
> >>>          if ( p2m == NULL )
> >>> @@ -143,7 +157,10 @@ void p2m_teardown_altp2m(struct domain *d)
> >>>      unsigned int i;
> >>>      struct p2m_domain *p2m;
> >>>
> >>> -    for ( i = 0; i < MAX_ALTP2M; i++ )
> >>> +    if ( !d->arch.altp2m_p2m )
> >>> +        return;
>
> I'm sorry, the question was meant to be on this if() instead.
>
> >>> +    for ( i = 0; i < d->nr_altp2m; i++ )
> >>>      {
> >>>          if ( !d->arch.altp2m_p2m[i] )
> >>>              continue;
> >>> @@ -151,6 +168,8 @@ void p2m_teardown_altp2m(struct domain *d)
> >>>          d->arch.altp2m_p2m[i] = NULL;
> >>>          p2m_free_one(p2m);
> >>>      }
> >>> +
> >>> +    XFREE(d->arch.altp2m_p2m);
> >>>  }
> >>
> >> ... this ought to be fine without?
> >
> > Could you, please, elaborate? I honestly don't know what you mean here
> > (by "this isn't needed").
>
> I hope the above correction is enough?

I'm sorry, but not really? I feel like I'm blind but I can't see
anything I could remove without causing (or risking) crash.

P.
Jan Beulich June 10, 2024, 10:16 a.m. UTC | #6
On 10.06.2024 11:10, Petr Beneš wrote:
> On Mon, Jun 10, 2024 at 9:30 AM Jan Beulich <jbeulich@suse.com> wrote:
>>
>> On 09.06.2024 01:06, Petr Beneš wrote:
>>> On Thu, Jun 6, 2024 at 9:24 AM Jan Beulich <jbeulich@suse.com> wrote:
>>>>> @@ -122,7 +131,12 @@ int p2m_init_altp2m(struct domain *d)
>>>>>      struct p2m_domain *hostp2m = p2m_get_hostp2m(d);
>>>>>
>>>>>      mm_lock_init(&d->arch.altp2m_list_lock);
>>>>> -    for ( i = 0; i < MAX_ALTP2M; i++ )
>>>>> +    d->arch.altp2m_p2m = xzalloc_array(struct p2m_domain *, d->nr_altp2m);
>>>>> +
>>>>> +    if ( !d->arch.altp2m_p2m )
>>>>> +        return -ENOMEM;
>>>>
>>>> This isn't really needed, is it? Both ...
>>>>
>>>>> +    for ( i = 0; i < d->nr_altp2m; i++ )
>>>>
>>>> ... this and ...
>>>>
>>>>>      {
>>>>>          d->arch.altp2m_p2m[i] = p2m = p2m_init_one(d);
>>>>>          if ( p2m == NULL )
>>>>> @@ -143,7 +157,10 @@ void p2m_teardown_altp2m(struct domain *d)
>>>>>      unsigned int i;
>>>>>      struct p2m_domain *p2m;
>>>>>
>>>>> -    for ( i = 0; i < MAX_ALTP2M; i++ )
>>>>> +    if ( !d->arch.altp2m_p2m )
>>>>> +        return;
>>
>> I'm sorry, the question was meant to be on this if() instead.
>>
>>>>> +    for ( i = 0; i < d->nr_altp2m; i++ )
>>>>>      {
>>>>>          if ( !d->arch.altp2m_p2m[i] )
>>>>>              continue;
>>>>> @@ -151,6 +168,8 @@ void p2m_teardown_altp2m(struct domain *d)
>>>>>          d->arch.altp2m_p2m[i] = NULL;
>>>>>          p2m_free_one(p2m);
>>>>>      }
>>>>> +
>>>>> +    XFREE(d->arch.altp2m_p2m);
>>>>>  }
>>>>
>>>> ... this ought to be fine without?
>>>
>>> Could you, please, elaborate? I honestly don't know what you mean here
>>> (by "this isn't needed").
>>
>> I hope the above correction is enough?
> 
> I'm sorry, but not really? I feel like I'm blind but I can't see
> anything I could remove without causing (or risking) crash.

The loop is going to do nothing when d->nr_altp2m == 0, and the XFREE() is
going to do nothing when d->arch.altp2m_p2m == NULL. Hence what does the
if() guard against? IOW what possible crashes are you seeing that I don't
see?

Jan
Petr Beneš June 10, 2024, 10:34 a.m. UTC | #7
On Mon, Jun 10, 2024 at 12:16 PM Jan Beulich <jbeulich@suse.com> wrote:
>
> On 10.06.2024 11:10, Petr Beneš wrote:
> > On Mon, Jun 10, 2024 at 9:30 AM Jan Beulich <jbeulich@suse.com> wrote:
> >>
> >> On 09.06.2024 01:06, Petr Beneš wrote:
> >>> On Thu, Jun 6, 2024 at 9:24 AM Jan Beulich <jbeulich@suse.com> wrote:
> >>>>> @@ -122,7 +131,12 @@ int p2m_init_altp2m(struct domain *d)
> >>>>>      struct p2m_domain *hostp2m = p2m_get_hostp2m(d);
> >>>>>
> >>>>>      mm_lock_init(&d->arch.altp2m_list_lock);
> >>>>> -    for ( i = 0; i < MAX_ALTP2M; i++ )
> >>>>> +    d->arch.altp2m_p2m = xzalloc_array(struct p2m_domain *, d->nr_altp2m);
> >>>>> +
> >>>>> +    if ( !d->arch.altp2m_p2m )
> >>>>> +        return -ENOMEM;
> >>>>
> >>>> This isn't really needed, is it? Both ...
> >>>>
> >>>>> +    for ( i = 0; i < d->nr_altp2m; i++ )
> >>>>
> >>>> ... this and ...
> >>>>
> >>>>>      {
> >>>>>          d->arch.altp2m_p2m[i] = p2m = p2m_init_one(d);
> >>>>>          if ( p2m == NULL )
> >>>>> @@ -143,7 +157,10 @@ void p2m_teardown_altp2m(struct domain *d)
> >>>>>      unsigned int i;
> >>>>>      struct p2m_domain *p2m;
> >>>>>
> >>>>> -    for ( i = 0; i < MAX_ALTP2M; i++ )
> >>>>> +    if ( !d->arch.altp2m_p2m )
> >>>>> +        return;
> >>
> >> I'm sorry, the question was meant to be on this if() instead.
> >>
> >>>>> +    for ( i = 0; i < d->nr_altp2m; i++ )
> >>>>>      {
> >>>>>          if ( !d->arch.altp2m_p2m[i] )
> >>>>>              continue;
> >>>>> @@ -151,6 +168,8 @@ void p2m_teardown_altp2m(struct domain *d)
> >>>>>          d->arch.altp2m_p2m[i] = NULL;
> >>>>>          p2m_free_one(p2m);
> >>>>>      }
> >>>>> +
> >>>>> +    XFREE(d->arch.altp2m_p2m);
> >>>>>  }
> >>>>
> >>>> ... this ought to be fine without?
> >>>
> >>> Could you, please, elaborate? I honestly don't know what you mean here
> >>> (by "this isn't needed").
> >>
> >> I hope the above correction is enough?
> >
> > I'm sorry, but not really? I feel like I'm blind but I can't see
> > anything I could remove without causing (or risking) crash.
>
> The loop is going to do nothing when d->nr_altp2m == 0, and the XFREE() is
> going to do nothing when d->arch.altp2m_p2m == NULL. Hence what does the
> if() guard against? IOW what possible crashes are you seeing that I don't
> see?

I see now. I was seeing ghosts - my thinking was that if
p2m_init_altp2m fails to allocate altp2m_p2m, it will call
p2m_teardown_altp2m - which, without the if(), would start to iterate
through an array that is not allocated. It doesn't happen, it just
returns -ENOMEM.

So to reiterate:

    if ( !d->arch.altp2m_p2m )
        return;

... are we talking that this condition inside p2m_teardown_altp2m
isn't needed? Or is there anything else?

P.
Jan Beulich June 10, 2024, 11:21 a.m. UTC | #8
On 10.06.2024 12:34, Petr Beneš wrote:
> On Mon, Jun 10, 2024 at 12:16 PM Jan Beulich <jbeulich@suse.com> wrote:
>>
>> On 10.06.2024 11:10, Petr Beneš wrote:
>>> On Mon, Jun 10, 2024 at 9:30 AM Jan Beulich <jbeulich@suse.com> wrote:
>>>>
>>>> On 09.06.2024 01:06, Petr Beneš wrote:
>>>>> On Thu, Jun 6, 2024 at 9:24 AM Jan Beulich <jbeulich@suse.com> wrote:
>>>>>>> @@ -122,7 +131,12 @@ int p2m_init_altp2m(struct domain *d)
>>>>>>>      struct p2m_domain *hostp2m = p2m_get_hostp2m(d);
>>>>>>>
>>>>>>>      mm_lock_init(&d->arch.altp2m_list_lock);
>>>>>>> -    for ( i = 0; i < MAX_ALTP2M; i++ )
>>>>>>> +    d->arch.altp2m_p2m = xzalloc_array(struct p2m_domain *, d->nr_altp2m);
>>>>>>> +
>>>>>>> +    if ( !d->arch.altp2m_p2m )
>>>>>>> +        return -ENOMEM;
>>>>>>
>>>>>> This isn't really needed, is it? Both ...
>>>>>>
>>>>>>> +    for ( i = 0; i < d->nr_altp2m; i++ )
>>>>>>
>>>>>> ... this and ...
>>>>>>
>>>>>>>      {
>>>>>>>          d->arch.altp2m_p2m[i] = p2m = p2m_init_one(d);
>>>>>>>          if ( p2m == NULL )
>>>>>>> @@ -143,7 +157,10 @@ void p2m_teardown_altp2m(struct domain *d)
>>>>>>>      unsigned int i;
>>>>>>>      struct p2m_domain *p2m;
>>>>>>>
>>>>>>> -    for ( i = 0; i < MAX_ALTP2M; i++ )
>>>>>>> +    if ( !d->arch.altp2m_p2m )
>>>>>>> +        return;
>>>>
>>>> I'm sorry, the question was meant to be on this if() instead.
>>>>
>>>>>>> +    for ( i = 0; i < d->nr_altp2m; i++ )
>>>>>>>      {
>>>>>>>          if ( !d->arch.altp2m_p2m[i] )
>>>>>>>              continue;
>>>>>>> @@ -151,6 +168,8 @@ void p2m_teardown_altp2m(struct domain *d)
>>>>>>>          d->arch.altp2m_p2m[i] = NULL;
>>>>>>>          p2m_free_one(p2m);
>>>>>>>      }
>>>>>>> +
>>>>>>> +    XFREE(d->arch.altp2m_p2m);
>>>>>>>  }
>>>>>>
>>>>>> ... this ought to be fine without?
>>>>>
>>>>> Could you, please, elaborate? I honestly don't know what you mean here
>>>>> (by "this isn't needed").
>>>>
>>>> I hope the above correction is enough?
>>>
>>> I'm sorry, but not really? I feel like I'm blind but I can't see
>>> anything I could remove without causing (or risking) crash.
>>
>> The loop is going to do nothing when d->nr_altp2m == 0, and the XFREE() is
>> going to do nothing when d->arch.altp2m_p2m == NULL. Hence what does the
>> if() guard against? IOW what possible crashes are you seeing that I don't
>> see?
> 
> I see now. I was seeing ghosts - my thinking was that if
> p2m_init_altp2m fails to allocate altp2m_p2m, it will call
> p2m_teardown_altp2m - which, without the if(), would start to iterate
> through an array that is not allocated. It doesn't happen, it just
> returns -ENOMEM.
> 
> So to reiterate:
> 
>     if ( !d->arch.altp2m_p2m )
>         return;
> 
> ... are we talking that this condition inside p2m_teardown_altp2m
> isn't needed?

I'm not sure about "isn't" vs "shouldn't". The call from p2m_final_teardown()
also needs to remain safe to make. Which may require that upon allocation
failure you zap d->nr_altp2m. Or which alternatively may mean that the if()
actually needs to stay.

> Or is there anything else?

There was also the question of whether to guard the allocation, to avoid a
de-generate xmalloc_array() of zero size. Yet in the interest of avoiding
not strictly necessary conditionals, that may well want to remain as is.

Jan
Petr Beneš June 10, 2024, 12:21 p.m. UTC | #9
On Mon, Jun 10, 2024 at 1:21 PM Jan Beulich <jbeulich@suse.com> wrote:
>
> On 10.06.2024 12:34, Petr Beneš wrote:
> > On Mon, Jun 10, 2024 at 12:16 PM Jan Beulich <jbeulich@suse.com> wrote:
> >>
> >> On 10.06.2024 11:10, Petr Beneš wrote:
> >>> On Mon, Jun 10, 2024 at 9:30 AM Jan Beulich <jbeulich@suse.com> wrote:
> >>>>
> >>>> On 09.06.2024 01:06, Petr Beneš wrote:
> >>>>> On Thu, Jun 6, 2024 at 9:24 AM Jan Beulich <jbeulich@suse.com> wrote:
> >>>>>>> @@ -122,7 +131,12 @@ int p2m_init_altp2m(struct domain *d)
> >>>>>>>      struct p2m_domain *hostp2m = p2m_get_hostp2m(d);
> >>>>>>>
> >>>>>>>      mm_lock_init(&d->arch.altp2m_list_lock);
> >>>>>>> -    for ( i = 0; i < MAX_ALTP2M; i++ )
> >>>>>>> +    d->arch.altp2m_p2m = xzalloc_array(struct p2m_domain *, d->nr_altp2m);
> >>>>>>> +
> >>>>>>> +    if ( !d->arch.altp2m_p2m )
> >>>>>>> +        return -ENOMEM;
> >>>>>>
> >>>>>> This isn't really needed, is it? Both ...
> >>>>>>
> >>>>>>> +    for ( i = 0; i < d->nr_altp2m; i++ )
> >>>>>>
> >>>>>> ... this and ...
> >>>>>>
> >>>>>>>      {
> >>>>>>>          d->arch.altp2m_p2m[i] = p2m = p2m_init_one(d);
> >>>>>>>          if ( p2m == NULL )
> >>>>>>> @@ -143,7 +157,10 @@ void p2m_teardown_altp2m(struct domain *d)
> >>>>>>>      unsigned int i;
> >>>>>>>      struct p2m_domain *p2m;
> >>>>>>>
> >>>>>>> -    for ( i = 0; i < MAX_ALTP2M; i++ )
> >>>>>>> +    if ( !d->arch.altp2m_p2m )
> >>>>>>> +        return;
> >>>>
> >>>> I'm sorry, the question was meant to be on this if() instead.
> >>>>
> >>>>>>> +    for ( i = 0; i < d->nr_altp2m; i++ )
> >>>>>>>      {
> >>>>>>>          if ( !d->arch.altp2m_p2m[i] )
> >>>>>>>              continue;
> >>>>>>> @@ -151,6 +168,8 @@ void p2m_teardown_altp2m(struct domain *d)
> >>>>>>>          d->arch.altp2m_p2m[i] = NULL;
> >>>>>>>          p2m_free_one(p2m);
> >>>>>>>      }
> >>>>>>> +
> >>>>>>> +    XFREE(d->arch.altp2m_p2m);
> >>>>>>>  }
> >>>>>>
> >>>>>> ... this ought to be fine without?
> >>>>>
> >>>>> Could you, please, elaborate? I honestly don't know what you mean here
> >>>>> (by "this isn't needed").
> >>>>
> >>>> I hope the above correction is enough?
> >>>
> >>> I'm sorry, but not really? I feel like I'm blind but I can't see
> >>> anything I could remove without causing (or risking) crash.
> >>
> >> The loop is going to do nothing when d->nr_altp2m == 0, and the XFREE() is
> >> going to do nothing when d->arch.altp2m_p2m == NULL. Hence what does the
> >> if() guard against? IOW what possible crashes are you seeing that I don't
> >> see?
> >
> > I see now. I was seeing ghosts - my thinking was that if
> > p2m_init_altp2m fails to allocate altp2m_p2m, it will call
> > p2m_teardown_altp2m - which, without the if(), would start to iterate
> > through an array that is not allocated. It doesn't happen, it just
> > returns -ENOMEM.
> >
> > So to reiterate:
> >
> >     if ( !d->arch.altp2m_p2m )
> >         return;
> >
> > ... are we talking that this condition inside p2m_teardown_altp2m
> > isn't needed?
>
> I'm not sure about "isn't" vs "shouldn't". The call from p2m_final_teardown()
> also needs to remain safe to make. Which may require that upon allocation
> failure you zap d->nr_altp2m. Or which alternatively may mean that the if()
> actually needs to stay.

True, p2m_final_teardown is called whenever p2m_init (and by extension
p2m_init_altp2m) fails. Which means that condition must stay - or, as
you suggested, reset nr_altp2m to 0.

I would rather leave the code as is. Modifying nr_altp2m would (in my
opinion) feel semantically incorrect, as that value should behave more
or less as const, that is initialized once.

> > Or is there anything else?
>
> There was also the question of whether to guard the allocation, to avoid a
> de-generate xmalloc_array() of zero size. Yet in the interest of avoiding
> not strictly necessary conditionals, that may well want to remain as is.

True, nr_altp2m would mean zero-sized allocation, as p2m_init_altp2m
is called unconditionally (when booted with altp2m=1). Is it a
problem, though?

P.
Petr Beneš June 10, 2024, 12:28 p.m. UTC | #10
> (when booted with altp2m=1)

Sorry, forgot to remove this statement before sending the email, it's
not really true.

I wanted to add that as I wrote in a previous email exchange - altp2m
should be ideally initialized only when it's requested - as opposed to
the current situation, when it's initialized in the domain_create.
However, that is more suited for 4.20.

P.
Jan Beulich June 10, 2024, 3:05 p.m. UTC | #11
On 10.06.2024 14:21, Petr Beneš wrote:
> On Mon, Jun 10, 2024 at 1:21 PM Jan Beulich <jbeulich@suse.com> wrote:
>>
>> On 10.06.2024 12:34, Petr Beneš wrote:
>>> On Mon, Jun 10, 2024 at 12:16 PM Jan Beulich <jbeulich@suse.com> wrote:
>>>>
>>>> On 10.06.2024 11:10, Petr Beneš wrote:
>>>>> On Mon, Jun 10, 2024 at 9:30 AM Jan Beulich <jbeulich@suse.com> wrote:
>>>>>>
>>>>>> On 09.06.2024 01:06, Petr Beneš wrote:
>>>>>>> On Thu, Jun 6, 2024 at 9:24 AM Jan Beulich <jbeulich@suse.com> wrote:
>>>>>>>>> @@ -122,7 +131,12 @@ int p2m_init_altp2m(struct domain *d)
>>>>>>>>>      struct p2m_domain *hostp2m = p2m_get_hostp2m(d);
>>>>>>>>>
>>>>>>>>>      mm_lock_init(&d->arch.altp2m_list_lock);
>>>>>>>>> -    for ( i = 0; i < MAX_ALTP2M; i++ )
>>>>>>>>> +    d->arch.altp2m_p2m = xzalloc_array(struct p2m_domain *, d->nr_altp2m);
>>>>>>>>> +
>>>>>>>>> +    if ( !d->arch.altp2m_p2m )
>>>>>>>>> +        return -ENOMEM;
>>>>>>>>
>>>>>>>> This isn't really needed, is it? Both ...
>>>>>>>>
>>>>>>>>> +    for ( i = 0; i < d->nr_altp2m; i++ )
>>>>>>>>
>>>>>>>> ... this and ...
>>>>>>>>
>>>>>>>>>      {
>>>>>>>>>          d->arch.altp2m_p2m[i] = p2m = p2m_init_one(d);
>>>>>>>>>          if ( p2m == NULL )
>>>>>>>>> @@ -143,7 +157,10 @@ void p2m_teardown_altp2m(struct domain *d)
>>>>>>>>>      unsigned int i;
>>>>>>>>>      struct p2m_domain *p2m;
>>>>>>>>>
>>>>>>>>> -    for ( i = 0; i < MAX_ALTP2M; i++ )
>>>>>>>>> +    if ( !d->arch.altp2m_p2m )
>>>>>>>>> +        return;
>>>>>>
>>>>>> I'm sorry, the question was meant to be on this if() instead.
>>>>>>
>>>>>>>>> +    for ( i = 0; i < d->nr_altp2m; i++ )
>>>>>>>>>      {
>>>>>>>>>          if ( !d->arch.altp2m_p2m[i] )
>>>>>>>>>              continue;
>>>>>>>>> @@ -151,6 +168,8 @@ void p2m_teardown_altp2m(struct domain *d)
>>>>>>>>>          d->arch.altp2m_p2m[i] = NULL;
>>>>>>>>>          p2m_free_one(p2m);
>>>>>>>>>      }
>>>>>>>>> +
>>>>>>>>> +    XFREE(d->arch.altp2m_p2m);
>>>>>>>>>  }
>>>>>>>>
>>>>>>>> ... this ought to be fine without?
>>>>>>>
>>>>>>> Could you, please, elaborate? I honestly don't know what you mean here
>>>>>>> (by "this isn't needed").
>>>>>>
>>>>>> I hope the above correction is enough?
>>>>>
>>>>> I'm sorry, but not really? I feel like I'm blind but I can't see
>>>>> anything I could remove without causing (or risking) crash.
>>>>
>>>> The loop is going to do nothing when d->nr_altp2m == 0, and the XFREE() is
>>>> going to do nothing when d->arch.altp2m_p2m == NULL. Hence what does the
>>>> if() guard against? IOW what possible crashes are you seeing that I don't
>>>> see?
>>>
>>> I see now. I was seeing ghosts - my thinking was that if
>>> p2m_init_altp2m fails to allocate altp2m_p2m, it will call
>>> p2m_teardown_altp2m - which, without the if(), would start to iterate
>>> through an array that is not allocated. It doesn't happen, it just
>>> returns -ENOMEM.
>>>
>>> So to reiterate:
>>>
>>>     if ( !d->arch.altp2m_p2m )
>>>         return;
>>>
>>> ... are we talking that this condition inside p2m_teardown_altp2m
>>> isn't needed?
>>
>> I'm not sure about "isn't" vs "shouldn't". The call from p2m_final_teardown()
>> also needs to remain safe to make. Which may require that upon allocation
>> failure you zap d->nr_altp2m. Or which alternatively may mean that the if()
>> actually needs to stay.
> 
> True, p2m_final_teardown is called whenever p2m_init (and by extension
> p2m_init_altp2m) fails. Which means that condition must stay - or, as
> you suggested, reset nr_altp2m to 0.
> 
> I would rather leave the code as is. Modifying nr_altp2m would (in my
> opinion) feel semantically incorrect, as that value should behave more
> or less as const, that is initialized once.
> 
>>> Or is there anything else?
>>
>> There was also the question of whether to guard the allocation, to avoid a
>> de-generate xmalloc_array() of zero size. Yet in the interest of avoiding
>> not strictly necessary conditionals, that may well want to remain as is.
> 
> True, nr_altp2m would mean zero-sized allocation, as p2m_init_altp2m
> is called unconditionally (when booted with altp2m=1). Is it a
> problem, though?

Not a significant one. Initially I thought this would end up being a non-
zero-size allocation, which we might like to avoid. But as it's a zero-
size one, I think that's okay to leave as is.

Jan
diff mbox series

Patch

diff --git a/xen/arch/arm/domain.c b/xen/arch/arm/domain.c
index 5234b627d0..e5785d2d96 100644
--- a/xen/arch/arm/domain.c
+++ b/xen/arch/arm/domain.c
@@ -688,7 +688,7 @@  int arch_sanitise_domain_config(struct xen_domctl_createdomain *config)
         return -EINVAL;
     }

-    if ( config->altp2m.opts )
+    if ( config->altp2m.opts || config->altp2m.nr )
     {
         dprintk(XENLOG_INFO, "Altp2m not supported\n");
         return -EINVAL;
diff --git a/xen/arch/x86/domain.c b/xen/arch/x86/domain.c
index bb5ba8fc1e..011cffb07e 100644
--- a/xen/arch/x86/domain.c
+++ b/xen/arch/x86/domain.c
@@ -724,16 +724,42 @@  int arch_sanitise_domain_config(struct xen_domctl_createdomain *config)
         return -EINVAL;
     }

-    if ( altp2m_mode && nested_virt )
+    if ( altp2m_mode )
     {
-        dprintk(XENLOG_INFO,
-                "Nested virt and altp2m are not supported together\n");
-        return -EINVAL;
-    }
+        if ( nested_virt )
+        {
+            dprintk(XENLOG_INFO,
+                    "Nested virt and altp2m are not supported together\n");
+            return -EINVAL;
+        }
+
+        if ( !hap )
+        {
+            dprintk(XENLOG_INFO, "altp2m is only supported with HAP\n");
+            return -EINVAL;
+        }
+
+        if ( !hvm_altp2m_supported() )
+        {
+            dprintk(XENLOG_INFO, "altp2m is not supported\n");
+            return -EINVAL;
+        }
+
+        if ( !config->altp2m.nr )
+        {
+            /* Fix the value to the legacy default */
+            config->altp2m.nr = 10;
+        }

-    if ( altp2m_mode && !hap )
+        if ( config->altp2m.nr > MAX_NR_ALTP2M )
+        {
+            dprintk(XENLOG_INFO, "altp2m.nr must be <= %lu\n", MAX_NR_ALTP2M);
+            return -EINVAL;
+        }
+    }
+    else if ( config->altp2m.nr )
     {
-        dprintk(XENLOG_INFO, "altp2m is only supported with HAP\n");
+        dprintk(XENLOG_INFO, "altp2m.nr must be zero when altp2m is off\n");
         return -EINVAL;
     }

diff --git a/xen/arch/x86/hvm/hvm.c b/xen/arch/x86/hvm/hvm.c
index a66ebaaceb..3d0357a0f8 100644
--- a/xen/arch/x86/hvm/hvm.c
+++ b/xen/arch/x86/hvm/hvm.c
@@ -4657,6 +4657,12 @@  static int do_altp2m_op(
         goto out;
     }

+    if ( d->nr_altp2m == 0 )
+    {
+        rc = -EINVAL;
+        goto out;
+    }
+
     if ( (rc = xsm_hvm_altp2mhvm_op(XSM_OTHER, d, mode, a.cmd)) )
         goto out;

@@ -5245,7 +5251,7 @@  void hvm_fast_singlestep(struct vcpu *v, uint16_t p2midx)
     if ( !hvm_is_singlestep_supported() )
         return;

-    if ( p2midx >= MAX_ALTP2M )
+    if ( p2midx >= v->domain->nr_altp2m )
         return;

     v->arch.hvm.single_step = true;
diff --git a/xen/arch/x86/hvm/vmx/vmx.c b/xen/arch/x86/hvm/vmx/vmx.c
index a420d452b3..9292a2c8d8 100644
--- a/xen/arch/x86/hvm/vmx/vmx.c
+++ b/xen/arch/x86/hvm/vmx/vmx.c
@@ -4885,7 +4885,7 @@  bool asmlinkage vmx_vmenter_helper(const struct cpu_user_regs *regs)
         {
             unsigned int i;

-            for ( i = 0; i < MAX_ALTP2M; ++i )
+            for ( i = 0; i < currd->nr_altp2m; ++i )
             {
                 if ( altp2m_get_eptp(currd, i) == mfn_x(INVALID_MFN) )
                     continue;
diff --git a/xen/arch/x86/include/asm/altp2m.h b/xen/arch/x86/include/asm/altp2m.h
index 2f064c61a2..a4cc3d3ffc 100644
--- a/xen/arch/x86/include/asm/altp2m.h
+++ b/xen/arch/x86/include/asm/altp2m.h
@@ -22,7 +22,7 @@  static inline bool altp2m_active(const struct domain *d)
 static inline struct p2m_domain *altp2m_get_p2m(const struct domain* d,
                                                 unsigned int idx)
 {
-    return d->arch.altp2m_p2m[array_index_nospec(idx, MAX_ALTP2M)];
+    return d->arch.altp2m_p2m[array_index_nospec(idx, d->nr_altp2m)];
 }

 static inline uint64_t altp2m_get_eptp(const struct domain* d,
diff --git a/xen/arch/x86/include/asm/domain.h b/xen/arch/x86/include/asm/domain.h
index f5daeb182b..855e844bed 100644
--- a/xen/arch/x86/include/asm/domain.h
+++ b/xen/arch/x86/include/asm/domain.h
@@ -258,11 +258,12 @@  struct paging_vcpu {
     struct shadow_vcpu shadow;
 };

-#define MAX_NESTEDP2M 10
+#define MAX_EPTP        (PAGE_SIZE / sizeof(uint64_t))
+#define MAX_NR_ALTP2M   MAX_EPTP
+#define MAX_NESTEDP2M   10

-#define MAX_ALTP2M      10 /* arbitrary */
 #define INVALID_ALTP2M  0xffff
-#define MAX_EPTP        (PAGE_SIZE / sizeof(uint64_t))
+
 struct p2m_domain;
 struct time_scale {
     int shift;
@@ -353,7 +354,7 @@  struct arch_domain

     /* altp2m: allow multiple copies of host p2m */
     bool altp2m_active;
-    struct p2m_domain *altp2m_p2m[MAX_ALTP2M];
+    struct p2m_domain **altp2m_p2m;
     mm_lock_t altp2m_list_lock;
     uint64_t *altp2m_eptp;
     uint64_t *altp2m_visible_eptp;
diff --git a/xen/arch/x86/include/asm/hvm/hvm.h b/xen/arch/x86/include/asm/hvm/hvm.h
index 1c01e22c8e..277648dd18 100644
--- a/xen/arch/x86/include/asm/hvm/hvm.h
+++ b/xen/arch/x86/include/asm/hvm/hvm.h
@@ -828,6 +828,11 @@  static inline bool hvm_hap_supported(void)
     return false;
 }

+static inline bool hvm_altp2m_supported(void)
+{
+    return false;
+}
+
 static inline bool hvm_nested_virt_supported(void)
 {
     return false;
diff --git a/xen/arch/x86/include/asm/p2m.h b/xen/arch/x86/include/asm/p2m.h
index e6f7764f9f..a1094fc7b3 100644
--- a/xen/arch/x86/include/asm/p2m.h
+++ b/xen/arch/x86/include/asm/p2m.h
@@ -887,7 +887,7 @@  static inline struct p2m_domain *p2m_get_altp2m(struct vcpu *v)
     if ( index == INVALID_ALTP2M )
         return NULL;

-    BUG_ON(index >= MAX_ALTP2M);
+    BUG_ON(index >= v->domain->nr_altp2m);

     return altp2m_get_p2m(v->domain, index);
 }
@@ -898,7 +898,7 @@  static inline bool p2m_set_altp2m(struct vcpu *v, unsigned int idx)
     struct p2m_domain *orig;
     struct p2m_domain *ap2m;

-    BUG_ON(idx >= MAX_ALTP2M);
+    BUG_ON(idx >= v->domain->nr_altp2m);

     if ( idx == vcpu_altp2m(v).p2midx )
         return false;
diff --git a/xen/arch/x86/mm/altp2m.c b/xen/arch/x86/mm/altp2m.c
index 7fb1738376..d47277e5e5 100644
--- a/xen/arch/x86/mm/altp2m.c
+++ b/xen/arch/x86/mm/altp2m.c
@@ -15,6 +15,11 @@ 
 void
 altp2m_vcpu_initialise(struct vcpu *v)
 {
+    struct domain *d = v->domain;
+
+    if ( d->nr_altp2m == 0 )
+        return;
+
     if ( v != current )
         vcpu_pause(v);

@@ -30,8 +35,12 @@  altp2m_vcpu_initialise(struct vcpu *v)
 void
 altp2m_vcpu_destroy(struct vcpu *v)
 {
+    struct domain *d = v->domain;
     struct p2m_domain *p2m;

+    if ( d->nr_altp2m == 0 )
+        return;
+
     if ( v != current )
         vcpu_pause(v);

@@ -122,7 +131,12 @@  int p2m_init_altp2m(struct domain *d)
     struct p2m_domain *hostp2m = p2m_get_hostp2m(d);

     mm_lock_init(&d->arch.altp2m_list_lock);
-    for ( i = 0; i < MAX_ALTP2M; i++ )
+    d->arch.altp2m_p2m = xzalloc_array(struct p2m_domain *, d->nr_altp2m);
+
+    if ( !d->arch.altp2m_p2m )
+        return -ENOMEM;
+
+    for ( i = 0; i < d->nr_altp2m; i++ )
     {
         d->arch.altp2m_p2m[i] = p2m = p2m_init_one(d);
         if ( p2m == NULL )
@@ -143,7 +157,10 @@  void p2m_teardown_altp2m(struct domain *d)
     unsigned int i;
     struct p2m_domain *p2m;

-    for ( i = 0; i < MAX_ALTP2M; i++ )
+    if ( !d->arch.altp2m_p2m )
+        return;
+
+    for ( i = 0; i < d->nr_altp2m; i++ )
     {
         if ( !d->arch.altp2m_p2m[i] )
             continue;
@@ -151,6 +168,8 @@  void p2m_teardown_altp2m(struct domain *d)
         d->arch.altp2m_p2m[i] = NULL;
         p2m_free_one(p2m);
     }
+
+    XFREE(d->arch.altp2m_p2m);
 }

 int altp2m_get_effective_entry(struct p2m_domain *ap2m, gfn_t gfn, mfn_t *mfn,
@@ -200,7 +219,7 @@  bool p2m_switch_vcpu_altp2m_by_id(struct vcpu *v, unsigned int idx)
     struct domain *d = v->domain;
     bool rc = false;

-    if ( idx >= MAX_ALTP2M )
+    if ( idx >= d->nr_altp2m )
         return rc;

     altp2m_list_lock(d);
@@ -306,7 +325,7 @@  static void p2m_reset_altp2m(struct domain *d, unsigned int idx,
 {
     struct p2m_domain *p2m;

-    ASSERT(idx < MAX_ALTP2M);
+    ASSERT(idx < d->nr_altp2m);
     p2m = altp2m_get_p2m(d, idx);

     p2m_lock(p2m);
@@ -332,7 +351,7 @@  void p2m_flush_altp2m(struct domain *d)

     altp2m_list_lock(d);

-    for ( i = 0; i < MAX_ALTP2M; i++ )
+    for ( i = 0; i < d->nr_altp2m; i++ )
     {
         p2m_reset_altp2m(d, i, ALTP2M_DEACTIVATE);
         altp2m_set_eptp(d, i, mfn_x(INVALID_MFN));
@@ -348,7 +367,7 @@  static int p2m_activate_altp2m(struct domain *d, unsigned int idx,
     struct p2m_domain *hostp2m, *p2m;
     int rc;

-    ASSERT(idx < MAX_ALTP2M);
+    ASSERT(idx < d->nr_altp2m);

     p2m = altp2m_get_p2m(d, idx);
     hostp2m = p2m_get_hostp2m(d);
@@ -388,7 +407,7 @@  int p2m_init_altp2m_by_id(struct domain *d, unsigned int idx)
     int rc = -EINVAL;
     struct p2m_domain *hostp2m = p2m_get_hostp2m(d);

-    if ( idx >= min(ARRAY_SIZE(d->arch.altp2m_p2m), MAX_EPTP) )
+    if ( idx >= d->nr_altp2m )
         return rc;

     altp2m_list_lock(d);
@@ -414,7 +433,7 @@  int p2m_init_next_altp2m(struct domain *d, uint16_t *idx,

     altp2m_list_lock(d);

-    for ( i = 0; i < MAX_ALTP2M; i++ )
+    for ( i = 0; i < d->nr_altp2m; i++ )
     {
         if ( altp2m_get_eptp(d, i) != mfn_x(INVALID_MFN) )
             continue;
@@ -436,7 +455,7 @@  int p2m_destroy_altp2m_by_id(struct domain *d, unsigned int idx)
     struct p2m_domain *p2m;
     int rc = -EBUSY;

-    if ( !idx || idx >= min(ARRAY_SIZE(d->arch.altp2m_p2m), MAX_EPTP) )
+    if ( !idx || idx >= d->nr_altp2m )
         return rc;

     rc = domain_pause_except_self(d);
@@ -471,7 +490,7 @@  int p2m_switch_domain_altp2m_by_id(struct domain *d, unsigned int idx)
     struct vcpu *v;
     int rc = -EINVAL;

-    if ( idx >= MAX_ALTP2M )
+    if ( idx >= d->nr_altp2m )
         return rc;

     rc = domain_pause_except_self(d);
@@ -506,8 +525,7 @@  int p2m_change_altp2m_gfn(struct domain *d, unsigned int idx,
     mfn_t mfn;
     int rc = -EINVAL;

-    if ( idx >= min(ARRAY_SIZE(d->arch.altp2m_p2m), MAX_EPTP) ||
-         altp2m_get_eptp(d, idx) == mfn_x(INVALID_MFN) )
+    if ( idx >= d->nr_altp2m || altp2m_get_eptp(d, idx) == mfn_x(INVALID_MFN) )
         return rc;

     hp2m = p2m_get_hostp2m(d);
@@ -567,7 +585,7 @@  int p2m_altp2m_propagate_change(struct domain *d, gfn_t gfn,

     altp2m_list_lock(d);

-    for ( i = 0; i < MAX_ALTP2M; i++ )
+    for ( i = 0; i < d->nr_altp2m; i++ )
     {
         p2m_type_t t;
         p2m_access_t a;
@@ -590,7 +608,7 @@  int p2m_altp2m_propagate_change(struct domain *d, gfn_t gfn,
             else
             {
                 /* At least 2 altp2m's impacted, so reset everything */
-                for ( i = 0; i < MAX_ALTP2M; i++ )
+                for ( i = 0; i < d->nr_altp2m; i++ )
                 {
                     if ( i == last_reset_idx ||
                          altp2m_get_eptp(d, i) == mfn_x(INVALID_MFN) )
@@ -654,7 +672,7 @@  int p2m_set_suppress_ve_multi(struct domain *d,

     if ( sve->view > 0 )
     {
-        if ( sve->view >= min(ARRAY_SIZE(d->arch.altp2m_p2m), MAX_EPTP) ||
+        if ( sve->view >= d->nr_altp2m ||
              altp2m_get_eptp(d, sve->view) == mfn_x(INVALID_MFN) )
             return -EINVAL;

@@ -721,7 +739,7 @@  int p2m_get_suppress_ve(struct domain *d, gfn_t gfn, bool *suppress_ve,

     if ( altp2m_idx > 0 )
     {
-        if ( altp2m_idx >= min(ARRAY_SIZE(d->arch.altp2m_p2m), MAX_EPTP) ||
+        if ( altp2m_idx >= d->nr_altp2m ||
              altp2m_get_eptp(d, altp2m_idx) == mfn_x(INVALID_MFN) )
             return -EINVAL;

@@ -756,9 +774,9 @@  int p2m_set_altp2m_view_visibility(struct domain *d, unsigned int altp2m_idx,

     /*
      * Eptp index is correlated with altp2m index and should not exceed
-     * min(MAX_ALTP2M, MAX_EPTP).
+     * d->nr_altp2m.
      */
-    if ( altp2m_idx >= min(ARRAY_SIZE(d->arch.altp2m_p2m), MAX_EPTP) ||
+    if ( altp2m_idx >= d->nr_altp2m ||
          altp2m_get_eptp(d, altp2m_idx) == mfn_x(INVALID_MFN) )
         rc = -EINVAL;
     else if ( visible )
diff --git a/xen/arch/x86/mm/hap/hap.c b/xen/arch/x86/mm/hap/hap.c
index 8fc8348152..8b23792a0d 100644
--- a/xen/arch/x86/mm/hap/hap.c
+++ b/xen/arch/x86/mm/hap/hap.c
@@ -515,7 +515,7 @@  int hap_enable(struct domain *d, u32 mode)
             altp2m_set_visible_eptp(d, i, mfn_x(INVALID_MFN));
         }

-        for ( i = 0; i < MAX_ALTP2M; i++ )
+        for ( i = 0; i < d->nr_altp2m; i++ )
         {
             rv = p2m_alloc_table(altp2m_get_p2m(d, i));
             if ( rv != 0 )
@@ -538,8 +538,8 @@  void hap_final_teardown(struct domain *d)
     unsigned int i;

     if ( hvm_altp2m_supported() )
-        for ( i = 0; i < MAX_ALTP2M; i++ )
-            p2m_teardown(d->arch.altp2m_p2m[i], true, NULL);
+        for ( i = 0; i < d->nr_altp2m; i++ )
+            p2m_teardown(altp2m_get_p2m(d, i), true, NULL);

     /* Destroy nestedp2m's first */
     for (i = 0; i < MAX_NESTEDP2M; i++) {
@@ -590,7 +590,7 @@  void hap_teardown(struct domain *d, bool *preempted)
         FREE_XENHEAP_PAGE(d->arch.altp2m_eptp);
         FREE_XENHEAP_PAGE(d->arch.altp2m_visible_eptp);

-        for ( i = 0; i < MAX_ALTP2M; i++ )
+        for ( i = 0; i < d->nr_altp2m; i++ )
         {
             p2m_teardown(altp2m_get_p2m(d, i), false, preempted);
             if ( preempted && *preempted )
diff --git a/xen/arch/x86/mm/mem_access.c b/xen/arch/x86/mm/mem_access.c
index 43f3a8c2aa..669a0d0a54 100644
--- a/xen/arch/x86/mm/mem_access.c
+++ b/xen/arch/x86/mm/mem_access.c
@@ -347,7 +347,7 @@  long p2m_set_mem_access(struct domain *d, gfn_t gfn, uint32_t nr,
     /* altp2m view 0 is treated as the hostp2m */
     if ( altp2m_idx )
     {
-        if ( altp2m_idx >= min(ARRAY_SIZE(d->arch.altp2m_p2m), MAX_EPTP) ||
+        if ( altp2m_idx >= d->nr_altp2m ||
              altp2m_get_eptp(d, altp2m_idx) == mfn_x(INVALID_MFN) )
             return -EINVAL;

@@ -402,7 +402,7 @@  long p2m_set_mem_access_multi(struct domain *d,
     /* altp2m view 0 is treated as the hostp2m */
     if ( altp2m_idx )
     {
-        if ( altp2m_idx >= min(ARRAY_SIZE(d->arch.altp2m_p2m), MAX_EPTP) ||
+        if ( altp2m_idx >= d->nr_altp2m ||
              altp2m_get_eptp(d, altp2m_idx) == mfn_x(INVALID_MFN) )
             return -EINVAL;

@@ -464,7 +464,7 @@  int p2m_get_mem_access(struct domain *d, gfn_t gfn, xenmem_access_t *access,
     }
     else if ( altp2m_idx ) /* altp2m view 0 is treated as the hostp2m */
     {
-        if ( altp2m_idx >= min(ARRAY_SIZE(d->arch.altp2m_p2m), MAX_EPTP) ||
+        if ( altp2m_idx >= d->nr_altp2m ||
              altp2m_get_eptp(d, altp2m_idx) == mfn_x(INVALID_MFN) )
             return -EINVAL;

@@ -483,7 +483,7 @@  void arch_p2m_set_access_required(struct domain *d, bool access_required)
     if ( altp2m_active(d) )
     {
         unsigned int i;
-        for ( i = 0; i < MAX_ALTP2M; i++ )
+        for ( i = 0; i < d->nr_altp2m; i++ )
         {
             struct p2m_domain *p2m = altp2m_get_p2m(d, i);

diff --git a/xen/arch/x86/mm/mem_sharing.c b/xen/arch/x86/mm/mem_sharing.c
index 21ac361111..2139d13009 100644
--- a/xen/arch/x86/mm/mem_sharing.c
+++ b/xen/arch/x86/mm/mem_sharing.c
@@ -912,7 +912,7 @@  static int nominate_page(struct domain *d, gfn_t gfn,

         altp2m_list_lock(d);

-        for ( i = 0; i < MAX_ALTP2M; i++ )
+        for ( i = 0; i < d->nr_altp2m; i++ )
         {
             ap2m = altp2m_get_p2m(d, i);
             if ( !ap2m )
diff --git a/xen/arch/x86/mm/p2m-ept.c b/xen/arch/x86/mm/p2m-ept.c
index ed4252822e..f90c82f89b 100644
--- a/xen/arch/x86/mm/p2m-ept.c
+++ b/xen/arch/x86/mm/p2m-ept.c
@@ -1293,7 +1293,7 @@  static void ept_set_ad_sync(struct domain *d, bool value)
     {
         unsigned int i;

-        for ( i = 0; i < MAX_ALTP2M; i++ )
+        for ( i = 0; i < d->nr_altp2m; i++ )
         {
             struct p2m_domain *p2m;

@@ -1519,7 +1519,7 @@  unsigned int p2m_find_altp2m_by_eptp(struct domain *d, uint64_t eptp)

     altp2m_list_lock(d);

-    for ( i = 0; i < MAX_ALTP2M; i++ )
+    for ( i = 0; i < d->nr_altp2m; i++ )
     {
         if ( altp2m_get_eptp(d, i) == mfn_x(INVALID_MFN) )
             continue;
diff --git a/xen/arch/x86/mm/p2m.c b/xen/arch/x86/mm/p2m.c
index 30a44441ba..380b7ece9c 100644
--- a/xen/arch/x86/mm/p2m.c
+++ b/xen/arch/x86/mm/p2m.c
@@ -105,7 +105,7 @@  void p2m_change_entry_type_global(struct domain *d,
     {
         unsigned int i;

-        for ( i = 0; i < MAX_ALTP2M; i++ )
+        for ( i = 0; i < d->nr_altp2m; i++ )
         {
             if ( altp2m_get_eptp(d, i) != mfn_x(INVALID_MFN) )
             {
@@ -140,7 +140,7 @@  void p2m_memory_type_changed(struct domain *d)
     {
         unsigned int i;

-        for ( i = 0; i < MAX_ALTP2M; i++ )
+        for ( i = 0; i < d->nr_altp2m; i++ )
         {
             if ( altp2m_get_eptp(d, i) != mfn_x(INVALID_MFN) )
             {
@@ -913,7 +913,7 @@  void p2m_change_type_range(struct domain *d,
     {
         unsigned int i;

-        for ( i = 0; i < MAX_ALTP2M; i++ )
+        for ( i = 0; i < d->nr_altp2m; i++ )
         {
             if ( altp2m_get_eptp(d, i) != mfn_x(INVALID_MFN) )
             {
@@ -986,7 +986,7 @@  int p2m_finish_type_change(struct domain *d,
     {
         unsigned int i;

-        for ( i = 0; i < MAX_ALTP2M; i++ )
+        for ( i = 0; i < d->nr_altp2m; i++ )
         {
             if ( altp2m_get_eptp(d, i) != mfn_x(INVALID_MFN) )
             {
diff --git a/xen/common/domain.c b/xen/common/domain.c
index 67cadb7c3f..776442cec0 100644
--- a/xen/common/domain.c
+++ b/xen/common/domain.c
@@ -610,6 +610,7 @@  struct domain *domain_create(domid_t domid,
     if ( config )
     {
         d->options = config->flags;
+        d->nr_altp2m = config->altp2m.nr;
         d->vmtrace_size = config->vmtrace_size;
     }

diff --git a/xen/include/public/domctl.h b/xen/include/public/domctl.h
index dea399aa8e..056bbc82a2 100644
--- a/xen/include/public/domctl.h
+++ b/xen/include/public/domctl.h
@@ -103,7 +103,10 @@  struct xen_domctl_createdomain {
 /* Altp2m mode signaling uses bits [0, 1]. */
 #define XEN_DOMCTL_ALTP2M_mode_mask  (0x3U)
 #define XEN_DOMCTL_ALTP2M_mode(m)    ((m) & XEN_DOMCTL_ALTP2M_mode_mask)
-        uint32_t opts;
+        uint16_t opts;
+
+        /* Number of altp2ms to allocate. */
+        uint16_t nr;
     } altp2m;

     /* Per-vCPU buffer size in bytes.  0 to disable. */
diff --git a/xen/include/xen/sched.h b/xen/include/xen/sched.h
index 2dcd1d1a4f..7119f3c44f 100644
--- a/xen/include/xen/sched.h
+++ b/xen/include/xen/sched.h
@@ -610,6 +610,8 @@  struct domain
         unsigned int guest_request_sync          : 1;
     } monitor;

+    unsigned int nr_altp2m;    /* Number of altp2m tables */
+
     unsigned int vmtrace_size; /* Buffer size in bytes, or 0 to disable. */

 #ifdef CONFIG_ARGO