diff mbox series

[v3,29/33] KVM: PPC: Book3S HV: Handle differing endianness for H_ENTER_NESTED

Message ID 1538479892-14835-30-git-send-email-paulus@ozlabs.org (mailing list archive)
State New, archived
Headers show
Series KVM: PPC: Book3S HV: Nested HV virtualization | expand

Commit Message

Paul Mackerras Oct. 2, 2018, 11:31 a.m. UTC
From: Suraj Jitindar Singh <sjitindarsingh@gmail.com>

The hcall H_ENTER_NESTED takes as the two parameters the address in
L1 guest memory of a hv_regs struct and a pt_regs struct which the
L1 guest would like to use to run a L2 guest and in which are returned
the exit state of the L2 guest.  For efficiency, these are in the
endianness of the L1 guest, rather than being always big-endian as is
usually the case for PAPR hypercalls.

When reading/writing these structures, this patch handles the case
where the endianness of the L1 guest differs from that of the L0
hypervisor, by byteswapping the structures after reading and before
writing them back.

Since all the fields of the pt_regs are of the same type, i.e.,
unsigned long, we treat it as an array of unsigned longs.  The fields
of struct hv_guest_state are not all the same, so its fields are
byteswapped individually.

Signed-off-by: Suraj Jitindar Singh <sjitindarsingh@gmail.com>
Signed-off-by: Paul Mackerras <paulus@ozlabs.org>
---
 arch/powerpc/kvm/book3s_hv_nested.c | 51 ++++++++++++++++++++++++++++++++++++-
 1 file changed, 50 insertions(+), 1 deletion(-)

Comments

David Gibson Oct. 3, 2018, 6:13 a.m. UTC | #1
On Tue, Oct 02, 2018 at 09:31:28PM +1000, Paul Mackerras wrote:
> From: Suraj Jitindar Singh <sjitindarsingh@gmail.com>
> 
> The hcall H_ENTER_NESTED takes as the two parameters the address in
> L1 guest memory of a hv_regs struct and a pt_regs struct which the
> L1 guest would like to use to run a L2 guest and in which are returned
> the exit state of the L2 guest.  For efficiency, these are in the
> endianness of the L1 guest, rather than being always big-endian as is
> usually the case for PAPR hypercalls.

Does that actually make a difference for efficiency?  I thought the
presence of the byte-reversing loads and stores meant byteswapping was
basically zero-cost on POWER.

> When reading/writing these structures, this patch handles the case
> where the endianness of the L1 guest differs from that of the L0
> hypervisor, by byteswapping the structures after reading and before
> writing them back.
> 
> Since all the fields of the pt_regs are of the same type, i.e.,
> unsigned long, we treat it as an array of unsigned longs.  The fields
> of struct hv_guest_state are not all the same, so its fields are
> byteswapped individually.
> 
> Signed-off-by: Suraj Jitindar Singh <sjitindarsingh@gmail.com>
> Signed-off-by: Paul Mackerras <paulus@ozlabs.org>

Looks correct, though, so

Reviewed-by: David Gibson <david@gibson.dropbear.id.au>

> ---
>  arch/powerpc/kvm/book3s_hv_nested.c | 51 ++++++++++++++++++++++++++++++++++++-
>  1 file changed, 50 insertions(+), 1 deletion(-)
> 
> diff --git a/arch/powerpc/kvm/book3s_hv_nested.c b/arch/powerpc/kvm/book3s_hv_nested.c
> index 7b1088a..228dc11 100644
> --- a/arch/powerpc/kvm/book3s_hv_nested.c
> +++ b/arch/powerpc/kvm/book3s_hv_nested.c
> @@ -51,6 +51,48 @@ void kvmhv_save_hv_regs(struct kvm_vcpu *vcpu, struct hv_guest_state *hr)
>  	hr->ppr = vcpu->arch.ppr;
>  }
>  
> +static void byteswap_pt_regs(struct pt_regs *regs)
> +{
> +	unsigned long *addr = (unsigned long *) regs;
> +
> +	for (; addr < ((unsigned long *) (regs + 1)); addr++)
> +		*addr = swab64(*addr);
> +}
> +
> +static void byteswap_hv_regs(struct hv_guest_state *hr)
> +{
> +	hr->version = swab64(hr->version);
> +	hr->lpid = swab32(hr->lpid);
> +	hr->vcpu_token = swab32(hr->vcpu_token);
> +	hr->lpcr = swab64(hr->lpcr);
> +	hr->pcr = swab64(hr->pcr);
> +	hr->amor = swab64(hr->amor);
> +	hr->dpdes = swab64(hr->dpdes);
> +	hr->hfscr = swab64(hr->hfscr);
> +	hr->tb_offset = swab64(hr->tb_offset);
> +	hr->dawr0 = swab64(hr->dawr0);
> +	hr->dawrx0 = swab64(hr->dawrx0);
> +	hr->ciabr = swab64(hr->ciabr);
> +	hr->hdec_expiry = swab64(hr->hdec_expiry);
> +	hr->purr = swab64(hr->purr);
> +	hr->spurr = swab64(hr->spurr);
> +	hr->ic = swab64(hr->ic);
> +	hr->vtb = swab64(hr->vtb);
> +	hr->hdar = swab64(hr->hdar);
> +	hr->hdsisr = swab64(hr->hdsisr);
> +	hr->heir = swab64(hr->heir);
> +	hr->asdr = swab64(hr->asdr);
> +	hr->srr0 = swab64(hr->srr0);
> +	hr->srr1 = swab64(hr->srr1);
> +	hr->sprg[0] = swab64(hr->sprg[0]);
> +	hr->sprg[1] = swab64(hr->sprg[1]);
> +	hr->sprg[2] = swab64(hr->sprg[2]);
> +	hr->sprg[3] = swab64(hr->sprg[3]);
> +	hr->pidr = swab64(hr->pidr);
> +	hr->cfar = swab64(hr->cfar);
> +	hr->ppr = swab64(hr->ppr);
> +}
> +
>  static void save_hv_return_state(struct kvm_vcpu *vcpu, int trap,
>  				 struct hv_guest_state *hr)
>  {
> @@ -175,6 +217,8 @@ long kvmhv_enter_nested_guest(struct kvm_vcpu *vcpu)
>  				  sizeof(struct hv_guest_state));
>  	if (err)
>  		return H_PARAMETER;
> +	if (kvmppc_need_byteswap(vcpu))
> +		byteswap_hv_regs(&l2_hv);
>  	if (l2_hv.version != HV_GUEST_STATE_VERSION)
>  		return H_P2;
>  
> @@ -183,7 +227,8 @@ long kvmhv_enter_nested_guest(struct kvm_vcpu *vcpu)
>  				  sizeof(struct pt_regs));
>  	if (err)
>  		return H_PARAMETER;
> -
> +	if (kvmppc_need_byteswap(vcpu))
> +		byteswap_pt_regs(&l2_regs);
>  	if (l2_hv.vcpu_token >= NR_CPUS)
>  		return H_PARAMETER;
>  
> @@ -255,6 +300,10 @@ long kvmhv_enter_nested_guest(struct kvm_vcpu *vcpu)
>  	kvmhv_put_nested(l2);
>  
>  	/* copy l2_hv_state and regs back to guest */
> +	if (kvmppc_need_byteswap(vcpu)) {
> +		byteswap_hv_regs(&l2_hv);
> +		byteswap_pt_regs(&l2_regs);
> +	}
>  	err = kvm_vcpu_write_guest(vcpu, hv_ptr, &l2_hv,
>  				   sizeof(struct hv_guest_state));
>  	if (err)
Paul Mackerras Oct. 4, 2018, 9:29 a.m. UTC | #2
On Wed, Oct 03, 2018 at 04:13:36PM +1000, David Gibson wrote:
> On Tue, Oct 02, 2018 at 09:31:28PM +1000, Paul Mackerras wrote:
> > From: Suraj Jitindar Singh <sjitindarsingh@gmail.com>
> > 
> > The hcall H_ENTER_NESTED takes as the two parameters the address in
> > L1 guest memory of a hv_regs struct and a pt_regs struct which the
> > L1 guest would like to use to run a L2 guest and in which are returned
> > the exit state of the L2 guest.  For efficiency, these are in the
> > endianness of the L1 guest, rather than being always big-endian as is
> > usually the case for PAPR hypercalls.
> 
> Does that actually make a difference for efficiency?  I thought the
> presence of the byte-reversing loads and stores meant byteswapping was
> basically zero-cost on POWER.

It means that the L1 hypervisor can pass a pointer to the regs struct
in the kvm_vcpu struct rather than having to make a copy, and copy the
values back after the H_ENTER_NESTED.  I'll reword the commit message
to say it's mostly about convenience and to a lesser extent about
performance.

Paul.
diff mbox series

Patch

diff --git a/arch/powerpc/kvm/book3s_hv_nested.c b/arch/powerpc/kvm/book3s_hv_nested.c
index 7b1088a..228dc11 100644
--- a/arch/powerpc/kvm/book3s_hv_nested.c
+++ b/arch/powerpc/kvm/book3s_hv_nested.c
@@ -51,6 +51,48 @@  void kvmhv_save_hv_regs(struct kvm_vcpu *vcpu, struct hv_guest_state *hr)
 	hr->ppr = vcpu->arch.ppr;
 }
 
+static void byteswap_pt_regs(struct pt_regs *regs)
+{
+	unsigned long *addr = (unsigned long *) regs;
+
+	for (; addr < ((unsigned long *) (regs + 1)); addr++)
+		*addr = swab64(*addr);
+}
+
+static void byteswap_hv_regs(struct hv_guest_state *hr)
+{
+	hr->version = swab64(hr->version);
+	hr->lpid = swab32(hr->lpid);
+	hr->vcpu_token = swab32(hr->vcpu_token);
+	hr->lpcr = swab64(hr->lpcr);
+	hr->pcr = swab64(hr->pcr);
+	hr->amor = swab64(hr->amor);
+	hr->dpdes = swab64(hr->dpdes);
+	hr->hfscr = swab64(hr->hfscr);
+	hr->tb_offset = swab64(hr->tb_offset);
+	hr->dawr0 = swab64(hr->dawr0);
+	hr->dawrx0 = swab64(hr->dawrx0);
+	hr->ciabr = swab64(hr->ciabr);
+	hr->hdec_expiry = swab64(hr->hdec_expiry);
+	hr->purr = swab64(hr->purr);
+	hr->spurr = swab64(hr->spurr);
+	hr->ic = swab64(hr->ic);
+	hr->vtb = swab64(hr->vtb);
+	hr->hdar = swab64(hr->hdar);
+	hr->hdsisr = swab64(hr->hdsisr);
+	hr->heir = swab64(hr->heir);
+	hr->asdr = swab64(hr->asdr);
+	hr->srr0 = swab64(hr->srr0);
+	hr->srr1 = swab64(hr->srr1);
+	hr->sprg[0] = swab64(hr->sprg[0]);
+	hr->sprg[1] = swab64(hr->sprg[1]);
+	hr->sprg[2] = swab64(hr->sprg[2]);
+	hr->sprg[3] = swab64(hr->sprg[3]);
+	hr->pidr = swab64(hr->pidr);
+	hr->cfar = swab64(hr->cfar);
+	hr->ppr = swab64(hr->ppr);
+}
+
 static void save_hv_return_state(struct kvm_vcpu *vcpu, int trap,
 				 struct hv_guest_state *hr)
 {
@@ -175,6 +217,8 @@  long kvmhv_enter_nested_guest(struct kvm_vcpu *vcpu)
 				  sizeof(struct hv_guest_state));
 	if (err)
 		return H_PARAMETER;
+	if (kvmppc_need_byteswap(vcpu))
+		byteswap_hv_regs(&l2_hv);
 	if (l2_hv.version != HV_GUEST_STATE_VERSION)
 		return H_P2;
 
@@ -183,7 +227,8 @@  long kvmhv_enter_nested_guest(struct kvm_vcpu *vcpu)
 				  sizeof(struct pt_regs));
 	if (err)
 		return H_PARAMETER;
-
+	if (kvmppc_need_byteswap(vcpu))
+		byteswap_pt_regs(&l2_regs);
 	if (l2_hv.vcpu_token >= NR_CPUS)
 		return H_PARAMETER;
 
@@ -255,6 +300,10 @@  long kvmhv_enter_nested_guest(struct kvm_vcpu *vcpu)
 	kvmhv_put_nested(l2);
 
 	/* copy l2_hv_state and regs back to guest */
+	if (kvmppc_need_byteswap(vcpu)) {
+		byteswap_hv_regs(&l2_hv);
+		byteswap_pt_regs(&l2_regs);
+	}
 	err = kvm_vcpu_write_guest(vcpu, hv_ptr, &l2_hv,
 				   sizeof(struct hv_guest_state));
 	if (err)