diff mbox series

[v2,01/20] x86: make hvm_{get/set}_param accessible

Message ID 0987641ced136706961cf419eb5ed83d1302357b.1576697796.git.tamas.lengyel@intel.com (mailing list archive)
State Superseded
Headers show
Series VM forking | expand

Commit Message

Tamas K Lengyel Dec. 18, 2019, 7:40 p.m. UTC
Currently the hvm parameters are only accessible via the HVMOP hypercalls. By
exposing hvm_{get/set}_param it will be possible for VM forking to copy the
parameters directly into the clone domain.

Signed-off-by: Tamas K Lengyel <tamas.lengyel@intel.com>
---
 xen/arch/x86/hvm/hvm.c        | 169 ++++++++++++++++++++--------------
 xen/include/asm-x86/hvm/hvm.h |   4 +
 2 files changed, 106 insertions(+), 67 deletions(-)

Comments

Andrew Cooper Dec. 19, 2019, 7:07 p.m. UTC | #1
On 18/12/2019 19:40, Tamas K Lengyel wrote:
> diff --git a/xen/arch/x86/hvm/hvm.c b/xen/arch/x86/hvm/hvm.c
> index 614ed60fe4..5a3a962fbb 100644
> --- a/xen/arch/x86/hvm/hvm.c
> +++ b/xen/arch/x86/hvm/hvm.c
> @@ -4072,16 +4072,17 @@ static int hvmop_set_evtchn_upcall_vector(
>  }
>  
>  static int hvm_allow_set_param(struct domain *d,
> -                               const struct xen_hvm_param *a)
> +                               uint32_t index,
> +                               uint64_t new_value)

These two lines can be folded together.

> @@ -4134,13 +4135,11 @@ static int hvm_allow_set_param(struct domain *d,
>      return rc;
>  }
>  
> -static int hvmop_set_param(
> +int hvmop_set_param(
>      XEN_GUEST_HANDLE_PARAM(xen_hvm_param_t) arg)

and here.

> @@ -4160,23 +4159,42 @@ static int hvmop_set_param(
>      if ( !is_hvm_domain(d) )
>          goto out;
>  
> -    rc = hvm_allow_set_param(d, &a);
> +    rc = hvm_set_param(d, a.index, a.value);
> +
> + out:
> +    rcu_unlock_domain(d);
> +    return rc;
> +}
> +
> +int hvm_set_param(
> +    struct domain *d,
> +    uint32_t index,
> +    uint64_t value)

and again.

> @@ -4340,34 +4358,33 @@ static int hvmop_set_param(
>           * 256 bits interrupt redirection bitmap + 64k bits I/O bitmap
>           * plus one padding byte).
>           */
> -        if ( (a.value >> 32) > sizeof(struct tss32) +
> +        if ( (value >> 32) > sizeof(struct tss32) +
>                                 (0x100 / 8) + (0x10000 / 8) + 1 )
> -            a.value = (uint32_t)a.value |
> +            value = (uint32_t)value |
>                        ((sizeof(struct tss32) + (0x100 / 8) +
>                                                 (0x10000 / 8) + 1) << 32);

Can you reindent the surrounding lines so they line up again?

> @@ -4429,42 +4446,60 @@ static int hvmop_get_param(
>      if ( !is_hvm_domain(d) )
>          goto out;
>  
> -    rc = hvm_allow_get_param(d, &a);
> +    rc = hvm_get_param(d, a.index, &a.value);
>      if ( rc )
>          goto out;
>  
> -    switch ( a.index )
> +    rc = __copy_to_guest(arg, &a, 1) ? -EFAULT : 0;
> +
> +    HVM_DBG_LOG(DBG_LEVEL_HCALL, "get param %u = %"PRIx64,
> +                a.index, a.value);
> +
> + out:
> +    rcu_unlock_domain(d);
> +    return rc;
> +}
> +
> +int hvm_get_param(
> +    struct domain *d,
> +    uint32_t index,
> +    uint64_t *value)

Fold.

> +{
> +    int rc;
> +
> +    if ( index >= HVM_NR_PARAMS || !value )

No need for !value.  It had better only ever point onto the hypervisor
stack now that this is an internal function.

> diff --git a/xen/include/asm-x86/hvm/hvm.h b/xen/include/asm-x86/hvm/hvm.h
> index 1d7b66f927..a6f4ae76a1 100644
> --- a/xen/include/asm-x86/hvm/hvm.h
> +++ b/xen/include/asm-x86/hvm/hvm.h
> @@ -335,6 +335,10 @@ unsigned long hvm_cr4_guest_valid_bits(const struct domain *d, bool restore);
>  bool hvm_flush_vcpu_tlb(bool (*flush_vcpu)(void *ctxt, struct vcpu *v),
>                          void *ctxt);
>  
> +/* Caller must hold domain locks */

How about asserting so?

No major problems so Acked-by: Andrew Cooper <andrew.cooper3@citrix.com>
Tamas K Lengyel Dec. 19, 2019, 7:38 p.m. UTC | #2
> > --- a/xen/include/asm-x86/hvm/hvm.h
> > +++ b/xen/include/asm-x86/hvm/hvm.h
> > @@ -335,6 +335,10 @@ unsigned long hvm_cr4_guest_valid_bits(const struct domain *d, bool restore);
> >  bool hvm_flush_vcpu_tlb(bool (*flush_vcpu)(void *ctxt, struct vcpu *v),
> >                          void *ctxt);
> >
> > +/* Caller must hold domain locks */
>
> How about asserting so?

AFAICT there is no "domain_locked_by_me" function, only
paging/p2m/gfn_locked_by_me. So any suggestion on how to achieve that?

Thanks!
Tamas
Andrew Cooper Dec. 19, 2019, 7:40 p.m. UTC | #3
On 19/12/2019 19:38, Tamas K Lengyel wrote:
>>> --- a/xen/include/asm-x86/hvm/hvm.h
>>> +++ b/xen/include/asm-x86/hvm/hvm.h
>>> @@ -335,6 +335,10 @@ unsigned long hvm_cr4_guest_valid_bits(const struct domain *d, bool restore);
>>>  bool hvm_flush_vcpu_tlb(bool (*flush_vcpu)(void *ctxt, struct vcpu *v),
>>>                          void *ctxt);
>>>
>>> +/* Caller must hold domain locks */
>> How about asserting so?
> AFAICT there is no "domain_locked_by_me" function, only
> paging/p2m/gfn_locked_by_me. So any suggestion on how to achieve that?

spin_is_locked() gets you most of the way, and would be a start.

But yes - now you say this, I remember that we don't currently have
suitable infrastructure.

~Andrew
Tamas K Lengyel Dec. 19, 2019, 7:49 p.m. UTC | #4
On Thu, Dec 19, 2019 at 12:41 PM Andrew Cooper
<andrew.cooper3@citrix.com> wrote:
>
> On 19/12/2019 19:38, Tamas K Lengyel wrote:
> >>> --- a/xen/include/asm-x86/hvm/hvm.h
> >>> +++ b/xen/include/asm-x86/hvm/hvm.h
> >>> @@ -335,6 +335,10 @@ unsigned long hvm_cr4_guest_valid_bits(const struct domain *d, bool restore);
> >>>  bool hvm_flush_vcpu_tlb(bool (*flush_vcpu)(void *ctxt, struct vcpu *v),
> >>>                          void *ctxt);
> >>>
> >>> +/* Caller must hold domain locks */
> >> How about asserting so?
> > AFAICT there is no "domain_locked_by_me" function, only
> > paging/p2m/gfn_locked_by_me. So any suggestion on how to achieve that?
>
> spin_is_locked() gets you most of the way, and would be a start.
>
> But yes - now you say this, I remember that we don't currently have
> suitable infrastructure.

Is the domain lock even a spin lock (the on you use by
rcu_lock_live_remote_domain_by_id)? Looks to me it just goes down to
"rcu_read_lock" which only does a preempt_disable() call o.O

Tamas
Andrew Cooper Dec. 19, 2019, 7:57 p.m. UTC | #5
On 19/12/2019 19:49, Tamas K Lengyel wrote:
> On Thu, Dec 19, 2019 at 12:41 PM Andrew Cooper
> <andrew.cooper3@citrix.com> wrote:
>> On 19/12/2019 19:38, Tamas K Lengyel wrote:
>>>>> --- a/xen/include/asm-x86/hvm/hvm.h
>>>>> +++ b/xen/include/asm-x86/hvm/hvm.h
>>>>> @@ -335,6 +335,10 @@ unsigned long hvm_cr4_guest_valid_bits(const struct domain *d, bool restore);
>>>>>  bool hvm_flush_vcpu_tlb(bool (*flush_vcpu)(void *ctxt, struct vcpu *v),
>>>>>                          void *ctxt);
>>>>>
>>>>> +/* Caller must hold domain locks */
>>>> How about asserting so?
>>> AFAICT there is no "domain_locked_by_me" function, only
>>> paging/p2m/gfn_locked_by_me. So any suggestion on how to achieve that?
>> spin_is_locked() gets you most of the way, and would be a start.
>>
>> But yes - now you say this, I remember that we don't currently have
>> suitable infrastructure.
> Is the domain lock even a spin lock (the on you use by
> rcu_lock_live_remote_domain_by_id)? Looks to me it just goes down to
> "rcu_read_lock" which only does a preempt_disable() call o.O

/sigh - of course we have two things called the domain lock.

The RCU one is to ensure that the struct domain can't get freed behind
our back, and shouldn't be interesting in this context (obtaining the d
pointer in the first place causes it to be safe).  If that is the only
one which matters, I would drop the comment.

~Andrew
Tamas K Lengyel Dec. 19, 2019, 8:09 p.m. UTC | #6
On Thu, Dec 19, 2019 at 12:57 PM Andrew Cooper
<andrew.cooper3@citrix.com> wrote:
>
> On 19/12/2019 19:49, Tamas K Lengyel wrote:
> > On Thu, Dec 19, 2019 at 12:41 PM Andrew Cooper
> > <andrew.cooper3@citrix.com> wrote:
> >> On 19/12/2019 19:38, Tamas K Lengyel wrote:
> >>>>> --- a/xen/include/asm-x86/hvm/hvm.h
> >>>>> +++ b/xen/include/asm-x86/hvm/hvm.h
> >>>>> @@ -335,6 +335,10 @@ unsigned long hvm_cr4_guest_valid_bits(const struct domain *d, bool restore);
> >>>>>  bool hvm_flush_vcpu_tlb(bool (*flush_vcpu)(void *ctxt, struct vcpu *v),
> >>>>>                          void *ctxt);
> >>>>>
> >>>>> +/* Caller must hold domain locks */
> >>>> How about asserting so?
> >>> AFAICT there is no "domain_locked_by_me" function, only
> >>> paging/p2m/gfn_locked_by_me. So any suggestion on how to achieve that?
> >> spin_is_locked() gets you most of the way, and would be a start.
> >>
> >> But yes - now you say this, I remember that we don't currently have
> >> suitable infrastructure.
> > Is the domain lock even a spin lock (the on you use by
> > rcu_lock_live_remote_domain_by_id)? Looks to me it just goes down to
> > "rcu_read_lock" which only does a preempt_disable() call o.O
>
> /sigh - of course we have two things called the domain lock.
>
> The RCU one is to ensure that the struct domain can't get freed behind
> our back, and shouldn't be interesting in this context (obtaining the d
> pointer in the first place causes it to be safe).  If that is the only
> one which matters, I would drop the comment.

Yes, only the RCU lock gets taken right now by all callers, so I'll
drop the comment.

Thanks,
Tamas
Jan Beulich Dec. 20, 2019, 4:46 p.m. UTC | #7
On 18.12.2019 20:40, Tamas K Lengyel wrote:
> Currently the hvm parameters are only accessible via the HVMOP hypercalls. By
> exposing hvm_{get/set}_param it will be possible for VM forking to copy the
> parameters directly into the clone domain.

Having peeked ahead at patch 17, where this gets used, I wonder why
you want a pair of one-by-one functions, rather than a copy-all one.
This then wouldn't require exposure of the functions you touch here.

> @@ -4429,42 +4446,60 @@ static int hvmop_get_param(
>      if ( !is_hvm_domain(d) )
>          goto out;
>  
> -    rc = hvm_allow_get_param(d, &a);
> +    rc = hvm_get_param(d, a.index, &a.value);
>      if ( rc )
>          goto out;
>  
> -    switch ( a.index )
> +    rc = __copy_to_guest(arg, &a, 1) ? -EFAULT : 0;
> +
> +    HVM_DBG_LOG(DBG_LEVEL_HCALL, "get param %u = %"PRIx64,
> +                a.index, a.value);
> +
> + out:
> +    rcu_unlock_domain(d);
> +    return rc;
> +}
> +
> +int hvm_get_param(
> +    struct domain *d,

If this is to be non-static, I think it would be quite nice if
this parameter was const. This will take a prereq patch to
constify the XSM path involved, but other than this I can't
see anything getting in the way.

Jan
Tamas K Lengyel Dec. 20, 2019, 5:27 p.m. UTC | #8
On Fri, Dec 20, 2019 at 9:47 AM Jan Beulich <jbeulich@suse.com> wrote:
>
> On 18.12.2019 20:40, Tamas K Lengyel wrote:
> > Currently the hvm parameters are only accessible via the HVMOP hypercalls. By
> > exposing hvm_{get/set}_param it will be possible for VM forking to copy the
> > parameters directly into the clone domain.
>
> Having peeked ahead at patch 17, where this gets used, I wonder why
> you want a pair of one-by-one functions, rather than a copy-all one.
> This then wouldn't require exposure of the functions you touch here.

Well, provided there is no such function in existence today it was
just easier to use what's already available. I still wouldn't want to
implement a one-shot function like that because this same code-path is
shared by the save-restore operations on the toolstack side, so at
least I have a reasonable assumption that it won't break on me in the
future.

> > @@ -4429,42 +4446,60 @@ static int hvmop_get_param(
> >      if ( !is_hvm_domain(d) )
> >          goto out;
> >
> > -    rc = hvm_allow_get_param(d, &a);
> > +    rc = hvm_get_param(d, a.index, &a.value);
> >      if ( rc )
> >          goto out;
> >
> > -    switch ( a.index )
> > +    rc = __copy_to_guest(arg, &a, 1) ? -EFAULT : 0;
> > +
> > +    HVM_DBG_LOG(DBG_LEVEL_HCALL, "get param %u = %"PRIx64,
> > +                a.index, a.value);
> > +
> > + out:
> > +    rcu_unlock_domain(d);
> > +    return rc;
> > +}
> > +
> > +int hvm_get_param(
> > +    struct domain *d,
>
> If this is to be non-static, I think it would be quite nice if
> this parameter was const. This will take a prereq patch to
> constify the XSM path involved, but other than this I can't
> see anything getting in the way.

Sure.
Andrew Cooper Dec. 20, 2019, 5:32 p.m. UTC | #9
On 20/12/2019 17:27, Tamas K Lengyel wrote:
> On Fri, Dec 20, 2019 at 9:47 AM Jan Beulich <jbeulich@suse.com> wrote:
>> On 18.12.2019 20:40, Tamas K Lengyel wrote:
>>> Currently the hvm parameters are only accessible via the HVMOP hypercalls. By
>>> exposing hvm_{get/set}_param it will be possible for VM forking to copy the
>>> parameters directly into the clone domain.
>> Having peeked ahead at patch 17, where this gets used, I wonder why
>> you want a pair of one-by-one functions, rather than a copy-all one.
>> This then wouldn't require exposure of the functions you touch here.
> Well, provided there is no such function in existence today it was
> just easier to use what's already available. I still wouldn't want to
> implement a one-shot function like that because this same code-path is
> shared by the save-restore operations on the toolstack side, so at
> least I have a reasonable assumption that it won't break on me in the
> future.

In particular, a number of the set operations are distinctly
non-trivial.  (OTOH, those are not long for this world, and should be
creation X86_EMU_* constants instead).

~Andrew
Tamas K Lengyel Dec. 20, 2019, 5:36 p.m. UTC | #10
On Fri, Dec 20, 2019 at 10:32 AM Andrew Cooper
<andrew.cooper3@citrix.com> wrote:
>
> On 20/12/2019 17:27, Tamas K Lengyel wrote:
> > On Fri, Dec 20, 2019 at 9:47 AM Jan Beulich <jbeulich@suse.com> wrote:
> >> On 18.12.2019 20:40, Tamas K Lengyel wrote:
> >>> Currently the hvm parameters are only accessible via the HVMOP hypercalls. By
> >>> exposing hvm_{get/set}_param it will be possible for VM forking to copy the
> >>> parameters directly into the clone domain.
> >> Having peeked ahead at patch 17, where this gets used, I wonder why
> >> you want a pair of one-by-one functions, rather than a copy-all one.
> >> This then wouldn't require exposure of the functions you touch here.
> > Well, provided there is no such function in existence today it was
> > just easier to use what's already available. I still wouldn't want to
> > implement a one-shot function like that because this same code-path is
> > shared by the save-restore operations on the toolstack side, so at
> > least I have a reasonable assumption that it won't break on me in the
> > future.
>
> In particular, a number of the set operations are distinctly
> non-trivial.  (OTOH, those are not long for this world, and should be
> creation X86_EMU_* constants instead).
>

I actually wanted to ask about that. In
https://xenbits.xen.org/gitweb/?p=xen.git;a=blob;f=tools/libxc/xc_sr_save_x86_hvm.c;h=97a8c49807f192c47209525f51e4d79a50c66cec;hb=HEAD#l61
the toolstack only selects certain HVM params to be saved (and
restored later). I originally followed the same logic in the fork
code-path but after a lot of experiments it looks like it's actually
OK to grab all params but only call set_param on the ones that have a
non-zero value. So setting some params with a zero value has certainly
lead to crashes, but otherwise it seems to "just work" to copy all the
rest.

Tamas
Andrew Cooper Dec. 20, 2019, 5:46 p.m. UTC | #11
On 20/12/2019 17:36, Tamas K Lengyel wrote:
> On Fri, Dec 20, 2019 at 10:32 AM Andrew Cooper
> <andrew.cooper3@citrix.com> wrote:
>> On 20/12/2019 17:27, Tamas K Lengyel wrote:
>>> On Fri, Dec 20, 2019 at 9:47 AM Jan Beulich <jbeulich@suse.com> wrote:
>>>> On 18.12.2019 20:40, Tamas K Lengyel wrote:
>>>>> Currently the hvm parameters are only accessible via the HVMOP hypercalls. By
>>>>> exposing hvm_{get/set}_param it will be possible for VM forking to copy the
>>>>> parameters directly into the clone domain.
>>>> Having peeked ahead at patch 17, where this gets used, I wonder why
>>>> you want a pair of one-by-one functions, rather than a copy-all one.
>>>> This then wouldn't require exposure of the functions you touch here.
>>> Well, provided there is no such function in existence today it was
>>> just easier to use what's already available. I still wouldn't want to
>>> implement a one-shot function like that because this same code-path is
>>> shared by the save-restore operations on the toolstack side, so at
>>> least I have a reasonable assumption that it won't break on me in the
>>> future.
>> In particular, a number of the set operations are distinctly
>> non-trivial.  (OTOH, those are not long for this world, and should be
>> creation X86_EMU_* constants instead).
>>
> I actually wanted to ask about that. In
> https://xenbits.xen.org/gitweb/?p=xen.git;a=blob;f=tools/libxc/xc_sr_save_x86_hvm.c;h=97a8c49807f192c47209525f51e4d79a50c66cec;hb=HEAD#l61
> the toolstack only selects certain HVM params to be saved (and
> restored later). I originally followed the same logic in the fork
> code-path but after a lot of experiments it looks like it's actually
> OK to grab all params but only call set_param on the ones that have a
> non-zero value. So setting some params with a zero value has certainly
> lead to crashes, but otherwise it seems to "just work" to copy all the
> rest.

I think you're trying to ascribe any form of design/plan to a system
which had none. :)

The code you quote was like that because that is how legacy migration
worked.  That said, eliding empty records was an effort-saving exercise
(avoid redundant hypercalls on destination side), not because there was
any suggestion that attempting to explicitly set 0 would crash.

Do you have any idea which param was causing problems?

~Andrew
Tamas K Lengyel Dec. 20, 2019, 5:50 p.m. UTC | #12
On Fri, Dec 20, 2019 at 10:47 AM Andrew Cooper
<andrew.cooper3@citrix.com> wrote:
>
> On 20/12/2019 17:36, Tamas K Lengyel wrote:
> > On Fri, Dec 20, 2019 at 10:32 AM Andrew Cooper
> > <andrew.cooper3@citrix.com> wrote:
> >> On 20/12/2019 17:27, Tamas K Lengyel wrote:
> >>> On Fri, Dec 20, 2019 at 9:47 AM Jan Beulich <jbeulich@suse.com> wrote:
> >>>> On 18.12.2019 20:40, Tamas K Lengyel wrote:
> >>>>> Currently the hvm parameters are only accessible via the HVMOP hypercalls. By
> >>>>> exposing hvm_{get/set}_param it will be possible for VM forking to copy the
> >>>>> parameters directly into the clone domain.
> >>>> Having peeked ahead at patch 17, where this gets used, I wonder why
> >>>> you want a pair of one-by-one functions, rather than a copy-all one.
> >>>> This then wouldn't require exposure of the functions you touch here.
> >>> Well, provided there is no such function in existence today it was
> >>> just easier to use what's already available. I still wouldn't want to
> >>> implement a one-shot function like that because this same code-path is
> >>> shared by the save-restore operations on the toolstack side, so at
> >>> least I have a reasonable assumption that it won't break on me in the
> >>> future.
> >> In particular, a number of the set operations are distinctly
> >> non-trivial.  (OTOH, those are not long for this world, and should be
> >> creation X86_EMU_* constants instead).
> >>
> > I actually wanted to ask about that. In
> > https://xenbits.xen.org/gitweb/?p=xen.git;a=blob;f=tools/libxc/xc_sr_save_x86_hvm.c;h=97a8c49807f192c47209525f51e4d79a50c66cec;hb=HEAD#l61
> > the toolstack only selects certain HVM params to be saved (and
> > restored later). I originally followed the same logic in the fork
> > code-path but after a lot of experiments it looks like it's actually
> > OK to grab all params but only call set_param on the ones that have a
> > non-zero value. So setting some params with a zero value has certainly
> > lead to crashes, but otherwise it seems to "just work" to copy all the
> > rest.
>
> I think you're trying to ascribe any form of design/plan to a system
> which had none. :)
>
> The code you quote was like that because that is how legacy migration
> worked.  That said, eliding empty records was an effort-saving exercise
> (avoid redundant hypercalls on destination side), not because there was
> any suggestion that attempting to explicitly set 0 would crash.
>
> Do you have any idea which param was causing problems?

Yes, HVM_PARAM_IDENT_PT was one sure. There may have been others (I
don't recall now) but simply checking for non-zero value before
calling set_param resolved everything.

Tamas
Andrew Cooper Dec. 20, 2019, 6 p.m. UTC | #13
On 20/12/2019 17:50, Tamas K Lengyel wrote:
> On Fri, Dec 20, 2019 at 10:47 AM Andrew Cooper
> <andrew.cooper3@citrix.com> wrote:
>> On 20/12/2019 17:36, Tamas K Lengyel wrote:
>>> On Fri, Dec 20, 2019 at 10:32 AM Andrew Cooper
>>> <andrew.cooper3@citrix.com> wrote:
>>>> On 20/12/2019 17:27, Tamas K Lengyel wrote:
>>>>> On Fri, Dec 20, 2019 at 9:47 AM Jan Beulich <jbeulich@suse.com> wrote:
>>>>>> On 18.12.2019 20:40, Tamas K Lengyel wrote:
>>>>>>> Currently the hvm parameters are only accessible via the HVMOP hypercalls. By
>>>>>>> exposing hvm_{get/set}_param it will be possible for VM forking to copy the
>>>>>>> parameters directly into the clone domain.
>>>>>> Having peeked ahead at patch 17, where this gets used, I wonder why
>>>>>> you want a pair of one-by-one functions, rather than a copy-all one.
>>>>>> This then wouldn't require exposure of the functions you touch here.
>>>>> Well, provided there is no such function in existence today it was
>>>>> just easier to use what's already available. I still wouldn't want to
>>>>> implement a one-shot function like that because this same code-path is
>>>>> shared by the save-restore operations on the toolstack side, so at
>>>>> least I have a reasonable assumption that it won't break on me in the
>>>>> future.
>>>> In particular, a number of the set operations are distinctly
>>>> non-trivial.  (OTOH, those are not long for this world, and should be
>>>> creation X86_EMU_* constants instead).
>>>>
>>> I actually wanted to ask about that. In
>>> https://xenbits.xen.org/gitweb/?p=xen.git;a=blob;f=tools/libxc/xc_sr_save_x86_hvm.c;h=97a8c49807f192c47209525f51e4d79a50c66cec;hb=HEAD#l61
>>> the toolstack only selects certain HVM params to be saved (and
>>> restored later). I originally followed the same logic in the fork
>>> code-path but after a lot of experiments it looks like it's actually
>>> OK to grab all params but only call set_param on the ones that have a
>>> non-zero value. So setting some params with a zero value has certainly
>>> lead to crashes, but otherwise it seems to "just work" to copy all the
>>> rest.
>> I think you're trying to ascribe any form of design/plan to a system
>> which had none. :)
>>
>> The code you quote was like that because that is how legacy migration
>> worked.  That said, eliding empty records was an effort-saving exercise
>> (avoid redundant hypercalls on destination side), not because there was
>> any suggestion that attempting to explicitly set 0 would crash.
>>
>> Do you have any idea which param was causing problems?
> Yes, HVM_PARAM_IDENT_PT was one sure. There may have been others (I
> don't recall now) but simply checking for non-zero value before
> calling set_param resolved everything.

IDENT_PT is an Westmere(?) wrinkle.

There was one processor back in those days which supported EPT, but
didn't support VT-x running in unpaged mode.  Therefore, we had to fake
up unpaged mode by pointing vCR3 at an identity pagetable inside the
guests physical address space.

The crash won't be from the IDENT_PT itself, but the paging_update_cr3()
side effect.  Was it a host crash, or guest crash?

~Andrew
Tamas K Lengyel Dec. 20, 2019, 6:05 p.m. UTC | #14
On Fri, Dec 20, 2019 at 11:00 AM Andrew Cooper
<andrew.cooper3@citrix.com> wrote:
>
> On 20/12/2019 17:50, Tamas K Lengyel wrote:
> > On Fri, Dec 20, 2019 at 10:47 AM Andrew Cooper
> > <andrew.cooper3@citrix.com> wrote:
> >> On 20/12/2019 17:36, Tamas K Lengyel wrote:
> >>> On Fri, Dec 20, 2019 at 10:32 AM Andrew Cooper
> >>> <andrew.cooper3@citrix.com> wrote:
> >>>> On 20/12/2019 17:27, Tamas K Lengyel wrote:
> >>>>> On Fri, Dec 20, 2019 at 9:47 AM Jan Beulich <jbeulich@suse.com> wrote:
> >>>>>> On 18.12.2019 20:40, Tamas K Lengyel wrote:
> >>>>>>> Currently the hvm parameters are only accessible via the HVMOP hypercalls. By
> >>>>>>> exposing hvm_{get/set}_param it will be possible for VM forking to copy the
> >>>>>>> parameters directly into the clone domain.
> >>>>>> Having peeked ahead at patch 17, where this gets used, I wonder why
> >>>>>> you want a pair of one-by-one functions, rather than a copy-all one.
> >>>>>> This then wouldn't require exposure of the functions you touch here.
> >>>>> Well, provided there is no such function in existence today it was
> >>>>> just easier to use what's already available. I still wouldn't want to
> >>>>> implement a one-shot function like that because this same code-path is
> >>>>> shared by the save-restore operations on the toolstack side, so at
> >>>>> least I have a reasonable assumption that it won't break on me in the
> >>>>> future.
> >>>> In particular, a number of the set operations are distinctly
> >>>> non-trivial.  (OTOH, those are not long for this world, and should be
> >>>> creation X86_EMU_* constants instead).
> >>>>
> >>> I actually wanted to ask about that. In
> >>> https://xenbits.xen.org/gitweb/?p=xen.git;a=blob;f=tools/libxc/xc_sr_save_x86_hvm.c;h=97a8c49807f192c47209525f51e4d79a50c66cec;hb=HEAD#l61
> >>> the toolstack only selects certain HVM params to be saved (and
> >>> restored later). I originally followed the same logic in the fork
> >>> code-path but after a lot of experiments it looks like it's actually
> >>> OK to grab all params but only call set_param on the ones that have a
> >>> non-zero value. So setting some params with a zero value has certainly
> >>> lead to crashes, but otherwise it seems to "just work" to copy all the
> >>> rest.
> >> I think you're trying to ascribe any form of design/plan to a system
> >> which had none. :)
> >>
> >> The code you quote was like that because that is how legacy migration
> >> worked.  That said, eliding empty records was an effort-saving exercise
> >> (avoid redundant hypercalls on destination side), not because there was
> >> any suggestion that attempting to explicitly set 0 would crash.
> >>
> >> Do you have any idea which param was causing problems?
> > Yes, HVM_PARAM_IDENT_PT was one sure. There may have been others (I
> > don't recall now) but simply checking for non-zero value before
> > calling set_param resolved everything.
>
> IDENT_PT is an Westmere(?) wrinkle.
>
> There was one processor back in those days which supported EPT, but
> didn't support VT-x running in unpaged mode.  Therefore, we had to fake
> up unpaged mode by pointing vCR3 at an identity pagetable inside the
> guests physical address space.

Eh, yikes.

>
> The crash won't be from the IDENT_PT itself, but the paging_update_cr3()
> side effect.  Was it a host crash, or guest crash?
>

Yes, that's what I recall after I looked into it. It was a guest a
crash as I remember.

Tamas
Jan Beulich Dec. 23, 2019, 9:37 a.m. UTC | #15
On 20.12.2019 18:32, Andrew Cooper wrote:
> On 20/12/2019 17:27, Tamas K Lengyel wrote:
>> On Fri, Dec 20, 2019 at 9:47 AM Jan Beulich <jbeulich@suse.com> wrote:
>>> On 18.12.2019 20:40, Tamas K Lengyel wrote:
>>>> Currently the hvm parameters are only accessible via the HVMOP hypercalls. By
>>>> exposing hvm_{get/set}_param it will be possible for VM forking to copy the
>>>> parameters directly into the clone domain.
>>> Having peeked ahead at patch 17, where this gets used, I wonder why
>>> you want a pair of one-by-one functions, rather than a copy-all one.
>>> This then wouldn't require exposure of the functions you touch here.
>> Well, provided there is no such function in existence today it was
>> just easier to use what's already available. I still wouldn't want to
>> implement a one-shot function like that because this same code-path is
>> shared by the save-restore operations on the toolstack side, so at
>> least I have a reasonable assumption that it won't break on me in the
>> future.
> 
> In particular, a number of the set operations are distinctly
> non-trivial.

How is trivial or not related to there being one function doing
the looping wanted here vs the looping being done by the caller
around the two per-entity calls?

Jan
Tamas K Lengyel Dec. 23, 2019, 2:55 p.m. UTC | #16
On Mon, Dec 23, 2019 at 2:37 AM Jan Beulich <jbeulich@suse.com> wrote:
>
> On 20.12.2019 18:32, Andrew Cooper wrote:
> > On 20/12/2019 17:27, Tamas K Lengyel wrote:
> >> On Fri, Dec 20, 2019 at 9:47 AM Jan Beulich <jbeulich@suse.com> wrote:
> >>> On 18.12.2019 20:40, Tamas K Lengyel wrote:
> >>>> Currently the hvm parameters are only accessible via the HVMOP hypercalls. By
> >>>> exposing hvm_{get/set}_param it will be possible for VM forking to copy the
> >>>> parameters directly into the clone domain.
> >>> Having peeked ahead at patch 17, where this gets used, I wonder why
> >>> you want a pair of one-by-one functions, rather than a copy-all one.
> >>> This then wouldn't require exposure of the functions you touch here.
> >> Well, provided there is no such function in existence today it was
> >> just easier to use what's already available. I still wouldn't want to
> >> implement a one-shot function like that because this same code-path is
> >> shared by the save-restore operations on the toolstack side, so at
> >> least I have a reasonable assumption that it won't break on me in the
> >> future.
> >
> > In particular, a number of the set operations are distinctly
> > non-trivial.
>
> How is trivial or not related to there being one function doing
> the looping wanted here vs the looping being done by the caller
> around the two per-entity calls?

I don't really get why would it matter where the looping is being
done? Even if I were to add a single function to do this, it would do
the same looping and just call the now internally kept get/set params
functions.

Tamas
Jan Beulich Dec. 27, 2019, 8:02 a.m. UTC | #17
(re-sending, as I still don't see the mail having appeared on the list)

On 23.12.2019 15:55, Tamas K Lengyel wrote:
> On Mon, Dec 23, 2019 at 2:37 AM Jan Beulich <jbeulich@suse.com> wrote:
>>
>> On 20.12.2019 18:32, Andrew Cooper wrote:
>>> On 20/12/2019 17:27, Tamas K Lengyel wrote:
>>>> On Fri, Dec 20, 2019 at 9:47 AM Jan Beulich <jbeulich@suse.com> wrote:
>>>>> On 18.12.2019 20:40, Tamas K Lengyel wrote:
>>>>>> Currently the hvm parameters are only accessible via the HVMOP hypercalls. By
>>>>>> exposing hvm_{get/set}_param it will be possible for VM forking to copy the
>>>>>> parameters directly into the clone domain.
>>>>> Having peeked ahead at patch 17, where this gets used, I wonder why
>>>>> you want a pair of one-by-one functions, rather than a copy-all one.
>>>>> This then wouldn't require exposure of the functions you touch here.
>>>> Well, provided there is no such function in existence today it was
>>>> just easier to use what's already available. I still wouldn't want to
>>>> implement a one-shot function like that because this same code-path is
>>>> shared by the save-restore operations on the toolstack side, so at
>>>> least I have a reasonable assumption that it won't break on me in the
>>>> future.
>>>
>>> In particular, a number of the set operations are distinctly
>>> non-trivial.
>>
>> How is trivial or not related to there being one function doing
>> the looping wanted here vs the looping being done by the caller
>> around the two per-entity calls?
> 
> I don't really get why would it matter where the looping is being
> done? Even if I were to add a single function to do this, it would do
> the same looping and just call the now internally kept get/set params
> functions.

The difference (to me) is what level of control gets exposed outside
of the file. For example I also dislike external code doing this
somewhat odd (but necessary as per your communication with Andrew)
checking against zero values. Such are implementation details which
would better not be scatter around.

Jan
Tamas K Lengyel Dec. 27, 2019, 1:10 p.m. UTC | #18
On Fri, Dec 27, 2019 at 1:04 AM Jan Beulich <JBeulich@suse.com> wrote:
>
> (re-sending, as I still don't see the mail having appeared on the list)
>
> On 23.12.2019 15:55, Tamas K Lengyel wrote:
> > On Mon, Dec 23, 2019 at 2:37 AM Jan Beulich <jbeulich@suse.com> wrote:
> >>
> >> On 20.12.2019 18:32, Andrew Cooper wrote:
> >>> On 20/12/2019 17:27, Tamas K Lengyel wrote:
> >>>> On Fri, Dec 20, 2019 at 9:47 AM Jan Beulich <jbeulich@suse.com> wrote:
> >>>>> On 18.12.2019 20:40, Tamas K Lengyel wrote:
> >>>>>> Currently the hvm parameters are only accessible via the HVMOP hypercalls. By
> >>>>>> exposing hvm_{get/set}_param it will be possible for VM forking to copy the
> >>>>>> parameters directly into the clone domain.
> >>>>> Having peeked ahead at patch 17, where this gets used, I wonder why
> >>>>> you want a pair of one-by-one functions, rather than a copy-all one.
> >>>>> This then wouldn't require exposure of the functions you touch here.
> >>>> Well, provided there is no such function in existence today it was
> >>>> just easier to use what's already available. I still wouldn't want to
> >>>> implement a one-shot function like that because this same code-path is
> >>>> shared by the save-restore operations on the toolstack side, so at
> >>>> least I have a reasonable assumption that it won't break on me in the
> >>>> future.
> >>>
> >>> In particular, a number of the set operations are distinctly
> >>> non-trivial.
> >>
> >> How is trivial or not related to there being one function doing
> >> the looping wanted here vs the looping being done by the caller
> >> around the two per-entity calls?
> >
> > I don't really get why would it matter where the looping is being
> > done? Even if I were to add a single function to do this, it would do
> > the same looping and just call the now internally kept get/set params
> > functions.
>
> The difference (to me) is what level of control gets exposed outside
> of the file. For example I also dislike external code doing this
> somewhat odd (but necessary as per your communication with Andrew)
> checking against zero values. Such are implementation details which
> would better not be scatter around.

But you do realize that both of these functions are already exposed
via hypercalls? So it's OK to call them from the toolstack but not
from other parts of Xen itself? I don't see much reason there. But to
me it makes 0 difference where the loop that copies the params is
done, as long as it's within Xen. So if you really want it to be in
hvm.c I can do that, I just find it silly.

Tamas
Jan Beulich Dec. 27, 2019, 1:44 p.m. UTC | #19
On 27.12.2019 14:10, Tamas K Lengyel wrote:
> On Fri, Dec 27, 2019 at 1:04 AM Jan Beulich <JBeulich@suse.com> wrote:
>>
>> (re-sending, as I still don't see the mail having appeared on the list)
>>
>> On 23.12.2019 15:55, Tamas K Lengyel wrote:
>>> On Mon, Dec 23, 2019 at 2:37 AM Jan Beulich <jbeulich@suse.com> wrote:
>>>>
>>>> On 20.12.2019 18:32, Andrew Cooper wrote:
>>>>> On 20/12/2019 17:27, Tamas K Lengyel wrote:
>>>>>> On Fri, Dec 20, 2019 at 9:47 AM Jan Beulich <jbeulich@suse.com> wrote:
>>>>>>> On 18.12.2019 20:40, Tamas K Lengyel wrote:
>>>>>>>> Currently the hvm parameters are only accessible via the HVMOP hypercalls. By
>>>>>>>> exposing hvm_{get/set}_param it will be possible for VM forking to copy the
>>>>>>>> parameters directly into the clone domain.
>>>>>>> Having peeked ahead at patch 17, where this gets used, I wonder why
>>>>>>> you want a pair of one-by-one functions, rather than a copy-all one.
>>>>>>> This then wouldn't require exposure of the functions you touch here.
>>>>>> Well, provided there is no such function in existence today it was
>>>>>> just easier to use what's already available. I still wouldn't want to
>>>>>> implement a one-shot function like that because this same code-path is
>>>>>> shared by the save-restore operations on the toolstack side, so at
>>>>>> least I have a reasonable assumption that it won't break on me in the
>>>>>> future.
>>>>>
>>>>> In particular, a number of the set operations are distinctly
>>>>> non-trivial.
>>>>
>>>> How is trivial or not related to there being one function doing
>>>> the looping wanted here vs the looping being done by the caller
>>>> around the two per-entity calls?
>>>
>>> I don't really get why would it matter where the looping is being
>>> done? Even if I were to add a single function to do this, it would do
>>> the same looping and just call the now internally kept get/set params
>>> functions.
>>
>> The difference (to me) is what level of control gets exposed outside
>> of the file. For example I also dislike external code doing this
>> somewhat odd (but necessary as per your communication with Andrew)
>> checking against zero values. Such are implementation details which
>> would better not be scatter around.
> 
> But you do realize that both of these functions are already exposed
> via hypercalls? So it's OK to call them from the toolstack but not
> from other parts of Xen itself? I don't see much reason there.

Right now we have exactly one path each allowing this get/set. Adding
a 2nd (from outside of hvm.c) opens the door for possible races
between the various (for now just two) possible call sites. Noticing
a possible problem when adding new code is imo quite a bit more
likely if everything lives centralized in one place. IOW "exposure"
here isn't meant so much in the sense of what entity in the system
gets to drive the data, but which entities within Xen may play with
it.

Jan
Tamas K Lengyel Dec. 27, 2019, 2:06 p.m. UTC | #20
On Fri, Dec 27, 2019 at 6:44 AM Jan Beulich <jbeulich@suse.com> wrote:
>
> On 27.12.2019 14:10, Tamas K Lengyel wrote:
> > On Fri, Dec 27, 2019 at 1:04 AM Jan Beulich <JBeulich@suse.com> wrote:
> >>
> >> (re-sending, as I still don't see the mail having appeared on the list)
> >>
> >> On 23.12.2019 15:55, Tamas K Lengyel wrote:
> >>> On Mon, Dec 23, 2019 at 2:37 AM Jan Beulich <jbeulich@suse.com> wrote:
> >>>>
> >>>> On 20.12.2019 18:32, Andrew Cooper wrote:
> >>>>> On 20/12/2019 17:27, Tamas K Lengyel wrote:
> >>>>>> On Fri, Dec 20, 2019 at 9:47 AM Jan Beulich <jbeulich@suse.com> wrote:
> >>>>>>> On 18.12.2019 20:40, Tamas K Lengyel wrote:
> >>>>>>>> Currently the hvm parameters are only accessible via the HVMOP hypercalls. By
> >>>>>>>> exposing hvm_{get/set}_param it will be possible for VM forking to copy the
> >>>>>>>> parameters directly into the clone domain.
> >>>>>>> Having peeked ahead at patch 17, where this gets used, I wonder why
> >>>>>>> you want a pair of one-by-one functions, rather than a copy-all one.
> >>>>>>> This then wouldn't require exposure of the functions you touch here.
> >>>>>> Well, provided there is no such function in existence today it was
> >>>>>> just easier to use what's already available. I still wouldn't want to
> >>>>>> implement a one-shot function like that because this same code-path is
> >>>>>> shared by the save-restore operations on the toolstack side, so at
> >>>>>> least I have a reasonable assumption that it won't break on me in the
> >>>>>> future.
> >>>>>
> >>>>> In particular, a number of the set operations are distinctly
> >>>>> non-trivial.
> >>>>
> >>>> How is trivial or not related to there being one function doing
> >>>> the looping wanted here vs the looping being done by the caller
> >>>> around the two per-entity calls?
> >>>
> >>> I don't really get why would it matter where the looping is being
> >>> done? Even if I were to add a single function to do this, it would do
> >>> the same looping and just call the now internally kept get/set params
> >>> functions.
> >>
> >> The difference (to me) is what level of control gets exposed outside
> >> of the file. For example I also dislike external code doing this
> >> somewhat odd (but necessary as per your communication with Andrew)
> >> checking against zero values. Such are implementation details which
> >> would better not be scatter around.
> >
> > But you do realize that both of these functions are already exposed
> > via hypercalls? So it's OK to call them from the toolstack but not
> > from other parts of Xen itself? I don't see much reason there.
>
> Right now we have exactly one path each allowing this get/set. Adding
> a 2nd (from outside of hvm.c) opens the door for possible races
> between the various (for now just two) possible call sites. Noticing
> a possible problem when adding new code is imo quite a bit more
> likely if everything lives centralized in one place. IOW "exposure"
> here isn't meant so much in the sense of what entity in the system
> gets to drive the data, but which entities within Xen may play with
> it.

Sure, I'll move the loop to hvm.c then.

Thanks,
Tamas
diff mbox series

Patch

diff --git a/xen/arch/x86/hvm/hvm.c b/xen/arch/x86/hvm/hvm.c
index 614ed60fe4..5a3a962fbb 100644
--- a/xen/arch/x86/hvm/hvm.c
+++ b/xen/arch/x86/hvm/hvm.c
@@ -4072,16 +4072,17 @@  static int hvmop_set_evtchn_upcall_vector(
 }
 
 static int hvm_allow_set_param(struct domain *d,
-                               const struct xen_hvm_param *a)
+                               uint32_t index,
+                               uint64_t new_value)
 {
-    uint64_t value = d->arch.hvm.params[a->index];
+    uint64_t value = d->arch.hvm.params[index];
     int rc;
 
     rc = xsm_hvm_param(XSM_TARGET, d, HVMOP_set_param);
     if ( rc )
         return rc;
 
-    switch ( a->index )
+    switch ( index )
     {
     /* The following parameters can be set by the guest. */
     case HVM_PARAM_CALLBACK_IRQ:
@@ -4114,7 +4115,7 @@  static int hvm_allow_set_param(struct domain *d,
     if ( rc )
         return rc;
 
-    switch ( a->index )
+    switch ( index )
     {
     /* The following parameters should only be changed once. */
     case HVM_PARAM_VIRIDIAN:
@@ -4124,7 +4125,7 @@  static int hvm_allow_set_param(struct domain *d,
     case HVM_PARAM_NR_IOREQ_SERVER_PAGES:
     case HVM_PARAM_ALTP2M:
     case HVM_PARAM_MCA_CAP:
-        if ( value != 0 && a->value != value )
+        if ( value != 0 && new_value != value )
             rc = -EEXIST;
         break;
     default:
@@ -4134,13 +4135,11 @@  static int hvm_allow_set_param(struct domain *d,
     return rc;
 }
 
-static int hvmop_set_param(
+int hvmop_set_param(
     XEN_GUEST_HANDLE_PARAM(xen_hvm_param_t) arg)
 {
-    struct domain *curr_d = current->domain;
     struct xen_hvm_param a;
     struct domain *d;
-    struct vcpu *v;
     int rc;
 
     if ( copy_from_guest(&a, arg, 1) )
@@ -4160,23 +4159,42 @@  static int hvmop_set_param(
     if ( !is_hvm_domain(d) )
         goto out;
 
-    rc = hvm_allow_set_param(d, &a);
+    rc = hvm_set_param(d, a.index, a.value);
+
+ out:
+    rcu_unlock_domain(d);
+    return rc;
+}
+
+int hvm_set_param(
+    struct domain *d,
+    uint32_t index,
+    uint64_t value)
+{
+    struct domain *curr_d = current->domain;
+    int rc;
+    struct vcpu *v;
+
+    if ( index >= HVM_NR_PARAMS )
+        return -EINVAL;
+
+    rc = hvm_allow_set_param(d, index, value);
     if ( rc )
         goto out;
 
-    switch ( a.index )
+    switch ( index )
     {
     case HVM_PARAM_CALLBACK_IRQ:
-        hvm_set_callback_via(d, a.value);
+        hvm_set_callback_via(d, value);
         hvm_latch_shinfo_size(d);
         break;
     case HVM_PARAM_TIMER_MODE:
-        if ( a.value > HVMPTM_one_missed_tick_pending )
+        if ( value > HVMPTM_one_missed_tick_pending )
             rc = -EINVAL;
         break;
     case HVM_PARAM_VIRIDIAN:
-        if ( (a.value & ~HVMPV_feature_mask) ||
-             !(a.value & HVMPV_base_freq) )
+        if ( (value & ~HVMPV_feature_mask) ||
+             !(value & HVMPV_base_freq) )
             rc = -EINVAL;
         break;
     case HVM_PARAM_IDENT_PT:
@@ -4186,7 +4204,7 @@  static int hvmop_set_param(
          */
         if ( !paging_mode_hap(d) || !cpu_has_vmx )
         {
-            d->arch.hvm.params[a.index] = a.value;
+            d->arch.hvm.params[index] = value;
             break;
         }
 
@@ -4201,7 +4219,7 @@  static int hvmop_set_param(
 
         rc = 0;
         domain_pause(d);
-        d->arch.hvm.params[a.index] = a.value;
+        d->arch.hvm.params[index] = value;
         for_each_vcpu ( d, v )
             paging_update_cr3(v, false);
         domain_unpause(d);
@@ -4210,23 +4228,23 @@  static int hvmop_set_param(
         break;
     case HVM_PARAM_DM_DOMAIN:
         /* The only value this should ever be set to is DOMID_SELF */
-        if ( a.value != DOMID_SELF )
+        if ( value != DOMID_SELF )
             rc = -EINVAL;
 
-        a.value = curr_d->domain_id;
+        value = curr_d->domain_id;
         break;
     case HVM_PARAM_ACPI_S_STATE:
         rc = 0;
-        if ( a.value == 3 )
+        if ( value == 3 )
             hvm_s3_suspend(d);
-        else if ( a.value == 0 )
+        else if ( value == 0 )
             hvm_s3_resume(d);
         else
             rc = -EINVAL;
 
         break;
     case HVM_PARAM_ACPI_IOPORTS_LOCATION:
-        rc = pmtimer_change_ioport(d, a.value);
+        rc = pmtimer_change_ioport(d, value);
         break;
     case HVM_PARAM_MEMORY_EVENT_CR0:
     case HVM_PARAM_MEMORY_EVENT_CR3:
@@ -4241,24 +4259,24 @@  static int hvmop_set_param(
         rc = xsm_hvm_param_nested(XSM_PRIV, d);
         if ( rc )
             break;
-        if ( a.value > 1 )
+        if ( value > 1 )
             rc = -EINVAL;
         /*
          * Remove the check below once we have
          * shadow-on-shadow.
          */
-        if ( !paging_mode_hap(d) && a.value )
+        if ( !paging_mode_hap(d) && value )
             rc = -EINVAL;
-        if ( a.value &&
+        if ( value &&
              d->arch.hvm.params[HVM_PARAM_ALTP2M] )
             rc = -EINVAL;
         /* Set up NHVM state for any vcpus that are already up. */
-        if ( a.value &&
+        if ( value &&
              !d->arch.hvm.params[HVM_PARAM_NESTEDHVM] )
             for_each_vcpu(d, v)
                 if ( rc == 0 )
                     rc = nestedhvm_vcpu_initialise(v);
-        if ( !a.value || rc )
+        if ( !value || rc )
             for_each_vcpu(d, v)
                 nestedhvm_vcpu_destroy(v);
         break;
@@ -4266,30 +4284,30 @@  static int hvmop_set_param(
         rc = xsm_hvm_param_altp2mhvm(XSM_PRIV, d);
         if ( rc )
             break;
-        if ( a.value > XEN_ALTP2M_limited )
+        if ( value > XEN_ALTP2M_limited )
             rc = -EINVAL;
-        if ( a.value &&
+        if ( value &&
              d->arch.hvm.params[HVM_PARAM_NESTEDHVM] )
             rc = -EINVAL;
         break;
     case HVM_PARAM_TRIPLE_FAULT_REASON:
-        if ( a.value > SHUTDOWN_MAX )
+        if ( value > SHUTDOWN_MAX )
             rc = -EINVAL;
         break;
     case HVM_PARAM_IOREQ_SERVER_PFN:
-        d->arch.hvm.ioreq_gfn.base = a.value;
+        d->arch.hvm.ioreq_gfn.base = value;
         break;
     case HVM_PARAM_NR_IOREQ_SERVER_PAGES:
     {
         unsigned int i;
 
-        if ( a.value == 0 ||
-             a.value > sizeof(d->arch.hvm.ioreq_gfn.mask) * 8 )
+        if ( value == 0 ||
+             value > sizeof(d->arch.hvm.ioreq_gfn.mask) * 8 )
         {
             rc = -EINVAL;
             break;
         }
-        for ( i = 0; i < a.value; i++ )
+        for ( i = 0; i < value; i++ )
             set_bit(i, &d->arch.hvm.ioreq_gfn.mask);
 
         break;
@@ -4301,35 +4319,35 @@  static int hvmop_set_param(
                      sizeof(d->arch.hvm.ioreq_gfn.legacy_mask) * 8);
         BUILD_BUG_ON(HVM_PARAM_BUFIOREQ_PFN >
                      sizeof(d->arch.hvm.ioreq_gfn.legacy_mask) * 8);
-        if ( a.value )
-            set_bit(a.index, &d->arch.hvm.ioreq_gfn.legacy_mask);
+        if ( value )
+            set_bit(index, &d->arch.hvm.ioreq_gfn.legacy_mask);
         break;
 
     case HVM_PARAM_X87_FIP_WIDTH:
-        if ( a.value != 0 && a.value != 4 && a.value != 8 )
+        if ( value != 0 && value != 4 && value != 8 )
         {
             rc = -EINVAL;
             break;
         }
-        d->arch.x87_fip_width = a.value;
+        d->arch.x87_fip_width = value;
         break;
 
     case HVM_PARAM_VM86_TSS:
         /* Hardware would silently truncate high bits. */
-        if ( a.value != (uint32_t)a.value )
+        if ( value != (uint32_t)value )
         {
             if ( d == curr_d )
                 domain_crash(d);
             rc = -EINVAL;
         }
         /* Old hvmloader binaries hardcode the size to 128 bytes. */
-        if ( a.value )
-            a.value |= (128ULL << 32) | VM86_TSS_UPDATED;
-        a.index = HVM_PARAM_VM86_TSS_SIZED;
+        if ( value )
+            value |= (128ULL << 32) | VM86_TSS_UPDATED;
+        index = HVM_PARAM_VM86_TSS_SIZED;
         break;
 
     case HVM_PARAM_VM86_TSS_SIZED:
-        if ( (a.value >> 32) < sizeof(struct tss32) )
+        if ( (value >> 32) < sizeof(struct tss32) )
         {
             if ( d == curr_d )
                 domain_crash(d);
@@ -4340,34 +4358,33 @@  static int hvmop_set_param(
          * 256 bits interrupt redirection bitmap + 64k bits I/O bitmap
          * plus one padding byte).
          */
-        if ( (a.value >> 32) > sizeof(struct tss32) +
+        if ( (value >> 32) > sizeof(struct tss32) +
                                (0x100 / 8) + (0x10000 / 8) + 1 )
-            a.value = (uint32_t)a.value |
+            value = (uint32_t)value |
                       ((sizeof(struct tss32) + (0x100 / 8) +
                                                (0x10000 / 8) + 1) << 32);
-        a.value |= VM86_TSS_UPDATED;
+        value |= VM86_TSS_UPDATED;
         break;
 
     case HVM_PARAM_MCA_CAP:
-        rc = vmce_enable_mca_cap(d, a.value);
+        rc = vmce_enable_mca_cap(d, value);
         break;
     }
 
     if ( rc != 0 )
         goto out;
 
-    d->arch.hvm.params[a.index] = a.value;
+    d->arch.hvm.params[index] = value;
 
     HVM_DBG_LOG(DBG_LEVEL_HCALL, "set param %u = %"PRIx64,
-                a.index, a.value);
+                index, value);
 
  out:
-    rcu_unlock_domain(d);
     return rc;
 }
 
 static int hvm_allow_get_param(struct domain *d,
-                               const struct xen_hvm_param *a)
+                               uint32_t index)
 {
     int rc;
 
@@ -4375,7 +4392,7 @@  static int hvm_allow_get_param(struct domain *d,
     if ( rc )
         return rc;
 
-    switch ( a->index )
+    switch ( index )
     {
     /* The following parameters can be read by the guest. */
     case HVM_PARAM_CALLBACK_IRQ:
@@ -4429,42 +4446,60 @@  static int hvmop_get_param(
     if ( !is_hvm_domain(d) )
         goto out;
 
-    rc = hvm_allow_get_param(d, &a);
+    rc = hvm_get_param(d, a.index, &a.value);
     if ( rc )
         goto out;
 
-    switch ( a.index )
+    rc = __copy_to_guest(arg, &a, 1) ? -EFAULT : 0;
+
+    HVM_DBG_LOG(DBG_LEVEL_HCALL, "get param %u = %"PRIx64,
+                a.index, a.value);
+
+ out:
+    rcu_unlock_domain(d);
+    return rc;
+}
+
+int hvm_get_param(
+    struct domain *d,
+    uint32_t index,
+    uint64_t *value)
+{
+    int rc;
+
+    if ( index >= HVM_NR_PARAMS || !value )
+        return -EINVAL;
+
+    rc = hvm_allow_get_param(d, index);
+    if ( rc )
+        return rc;
+
+    switch ( index )
     {
     case HVM_PARAM_ACPI_S_STATE:
-        a.value = d->arch.hvm.is_s3_suspended ? 3 : 0;
+        *value = d->arch.hvm.is_s3_suspended ? 3 : 0;
         break;
 
     case HVM_PARAM_VM86_TSS:
-        a.value = (uint32_t)d->arch.hvm.params[HVM_PARAM_VM86_TSS_SIZED];
+        *value = (uint32_t)d->arch.hvm.params[HVM_PARAM_VM86_TSS_SIZED];
         break;
 
     case HVM_PARAM_VM86_TSS_SIZED:
-        a.value = d->arch.hvm.params[HVM_PARAM_VM86_TSS_SIZED] &
-                  ~VM86_TSS_UPDATED;
+        *value = d->arch.hvm.params[HVM_PARAM_VM86_TSS_SIZED] &
+                   ~VM86_TSS_UPDATED;
         break;
 
     case HVM_PARAM_X87_FIP_WIDTH:
-        a.value = d->arch.x87_fip_width;
+        *value = d->arch.x87_fip_width;
         break;
     default:
-        a.value = d->arch.hvm.params[a.index];
+        *value = d->arch.hvm.params[index];
         break;
     }
 
-    rc = __copy_to_guest(arg, &a, 1) ? -EFAULT : 0;
-
-    HVM_DBG_LOG(DBG_LEVEL_HCALL, "get param %u = %"PRIx64,
-                a.index, a.value);
+    return 0;
+};
 
- out:
-    rcu_unlock_domain(d);
-    return rc;
-}
 
 /*
  * altp2m operations are envisioned as being used in several different
diff --git a/xen/include/asm-x86/hvm/hvm.h b/xen/include/asm-x86/hvm/hvm.h
index 1d7b66f927..a6f4ae76a1 100644
--- a/xen/include/asm-x86/hvm/hvm.h
+++ b/xen/include/asm-x86/hvm/hvm.h
@@ -335,6 +335,10 @@  unsigned long hvm_cr4_guest_valid_bits(const struct domain *d, bool restore);
 bool hvm_flush_vcpu_tlb(bool (*flush_vcpu)(void *ctxt, struct vcpu *v),
                         void *ctxt);
 
+/* Caller must hold domain locks */
+int hvm_get_param(struct domain *d, uint32_t index, uint64_t *value);
+int hvm_set_param(struct domain *d, uint32_t index, uint64_t value);
+
 #ifdef CONFIG_HVM
 
 #define hvm_get_guest_tsc(v) hvm_get_guest_tsc_fixed(v, 0)