diff mbox series

[v2,2/4] x86/vmware: Add a header file for hypercall definitions

Message ID 20190823081316.28478-3-thomas_os@shipmail.org (mailing list archive)
State New, archived
Headers show
Series None | expand

Commit Message

Thomas Hellström (Intel) Aug. 23, 2019, 8:13 a.m. UTC
From: Thomas Hellstrom <thellstrom@vmware.com>

The new header is intended to be used by drivers using the backdoor.
Follow the kvm example using alternatives self-patching to
choose between vmcall, vmmcall and io instructions.

Also define two new CPU feature flags to indicate hypervisor support
for vmcall- and vmmcall instructions.

Cc: Thomas Gleixner <tglx@linutronix.de>
Cc: Ingo Molnar <mingo@redhat.com>
Cc: Borislav Petkov <bp@alien8.de>
Cc: "H. Peter Anvin" <hpa@zytor.com>
Cc: <x86@kernel.org>
Cc: <dri-devel@lists.freedesktop.org>
Signed-off-by: Thomas Hellstrom <thellstrom@vmware.com>
Reviewed-by: Doug Covelli <dcovelli@vmware.com>
---
 MAINTAINERS                        |  1 +
 arch/x86/include/asm/cpufeatures.h |  2 ++
 arch/x86/include/asm/vmware.h      | 48 ++++++++++++++++++++++++++++++
 arch/x86/kernel/cpu/vmware.c       |  6 +++-
 4 files changed, 56 insertions(+), 1 deletion(-)
 create mode 100644 arch/x86/include/asm/vmware.h

Comments

Borislav Petkov Aug. 27, 2019, 1:10 p.m. UTC | #1
On Fri, Aug 23, 2019 at 10:13:14AM +0200, Thomas Hellström (VMware) wrote:
> From: Thomas Hellstrom <thellstrom@vmware.com>
> 
> The new header is intended to be used by drivers using the backdoor.
> Follow the kvm example using alternatives self-patching to
> choose between vmcall, vmmcall and io instructions.
> 
> Also define two new CPU feature flags to indicate hypervisor support
> for vmcall- and vmmcall instructions.

I could use some of the explanation why we need two feature flags added
here from:

https://lkml.kernel.org/r/970d2bb6-ab29-315f-f5d8-5d11095859af@shipmail.org

Thx.
Borislav Petkov Aug. 27, 2019, 3:44 p.m. UTC | #2
On Fri, Aug 23, 2019 at 10:13:14AM +0200, Thomas Hellström (VMware) wrote:
> +/*
> + * The high bandwidth out call. The low word of edx is presumed to have the
> + * HB and OUT bits set.
> + */
> +#define VMWARE_HYPERCALL_HB_OUT						\
> +	ALTERNATIVE_2("movw $" VMWARE_HYPERVISOR_PORT_HB ", %%dx; rep outsb", \

Hmm, that looks fishy:

This call in vmw_port_hb_out(), for example, gets converted to the asm
below (I've left in the asm touching only rDX).

# drivers/gpu/drm/vmwgfx/vmwgfx_msg.c:160:              VMW_PORT_HB_OUT(
#NO_APP
        movzwl  0(%rbp), %edx   # channel_20(D)->channel_id, channel_20(D)->channel_id

	...

        sall    $16, %edx       #, tmp172
        orl     $3, %edx        #, tmp173

this is adding channel_id and flags:

                        VMWARE_HYPERVISOR_HB | (channel->channel_id << 16) |
                        VMWARE_HYPERVISOR_OUT,

the $3 being (VMWARE_HYPERVISOR_HB | VMWARE_HYPERVISOR_OUT).

        movslq  %edx, %rdx      # tmp173, tmp174

Here it is sign-extending it.

#APP
# 160 "drivers/gpu/drm/vmwgfx/vmwgfx_msg.c" 1
        push %rbp;mov %r8, %rbp;# ALT: oldinstr2        # bp
661:
        movw $0x5659, %dx; rep outsb

And now here you're overwriting the low word of %edx. And now it
contains:

0x[channel_id]5659

and the low word doesn't contain the 3, i.e., (VMWARE_HYPERVISOR_HB |
VMWARE_HYPERVISOR_OUT) anymore. And that's before you do the hypercall
so I'm guessing that cannot be right.

Or?
Thomas Hellström (Intel) Aug. 27, 2019, 7:19 p.m. UTC | #3
On 8/27/19 5:44 PM, Borislav Petkov wrote:
> On Fri, Aug 23, 2019 at 10:13:14AM +0200, Thomas Hellström (VMware) wrote:
>> +/*
>> + * The high bandwidth out call. The low word of edx is presumed to have the
>> + * HB and OUT bits set.
>> + */
>> +#define VMWARE_HYPERCALL_HB_OUT						\
>> +	ALTERNATIVE_2("movw $" VMWARE_HYPERVISOR_PORT_HB ", %%dx; rep outsb", \
> Hmm, that looks fishy:
>
> This call in vmw_port_hb_out(), for example, gets converted to the asm
> below (I've left in the asm touching only rDX).
>
> # drivers/gpu/drm/vmwgfx/vmwgfx_msg.c:160:              VMW_PORT_HB_OUT(
> #NO_APP
>          movzwl  0(%rbp), %edx   # channel_20(D)->channel_id, channel_20(D)->channel_id
>
> 	...
>
>          sall    $16, %edx       #, tmp172
>          orl     $3, %edx        #, tmp173
>
> this is adding channel_id and flags:
>
>                          VMWARE_HYPERVISOR_HB | (channel->channel_id << 16) |
>                          VMWARE_HYPERVISOR_OUT,
>
> the $3 being (VMWARE_HYPERVISOR_HB | VMWARE_HYPERVISOR_OUT).
>
>          movslq  %edx, %rdx      # tmp173, tmp174
>
> Here it is sign-extending it.
>
> #APP
> # 160 "drivers/gpu/drm/vmwgfx/vmwgfx_msg.c" 1
>          push %rbp;mov %r8, %rbp;# ALT: oldinstr2        # bp
> 661:
>          movw $0x5659, %dx; rep outsb
>
> And now here you're overwriting the low word of %edx. And now it
> contains:
>
> 0x[channel_id]5659
>
> and the low word doesn't contain the 3, i.e., (VMWARE_HYPERVISOR_HB |
> VMWARE_HYPERVISOR_OUT) anymore. And that's before you do the hypercall
> so I'm guessing that cannot be right.
>
> Or?
>
It should be correct. The flags VMWARE_HYPERVISOR_HB and 
VMWARE_HYPERVISOR_OUT are only valid for the vmcall / vmmcall versions.

For the legacy version, the direction is toggled by the instruction (in 
vs out) and LB vs HB is toggled by the port number (0x5658 vs 0x5659)

So in essence the low word definition of %edx is different in the two 
versions. I've chosen to use the new vmcall/vmmcall definition in the 
driver code.

/Thomas
Borislav Petkov Aug. 27, 2019, 7:54 p.m. UTC | #4
On Tue, Aug 27, 2019 at 09:19:03PM +0200, Thomas Hellström (VMware) wrote:
> It should be correct. The flags VMWARE_HYPERVISOR_HB and
> VMWARE_HYPERVISOR_OUT are only valid for the vmcall / vmmcall versions.
> 
> For the legacy version, the direction is toggled by the instruction (in vs
> out) and LB vs HB is toggled by the port number (0x5658 vs 0x5659)
> 
> So in essence the low word definition of %edx is different in the two
> versions. I've chosen to use the new vmcall/vmmcall definition in the driver
> code.

Ah, ok, I see what you mean. The old method would overwrite the low word
of %edx but the new one would have the flags already prepared and *not*
overwrite them so all good.

Can you please document that more explicitly in the comment in
arch/x86/include/asm/vmware.h? Something like:

"... The new vmcall interface instead uses a set of flags to select
bandwidth mode and transfer direction. The set of flags is already
loaded into %edx by the macros which use VMWARE_HYPERCALL* and only when
the guest must use the old VMWARE_HYPERVISOR_PORT* method, the low word
is overwritten by the respective port number."

Anyway, something along those lines. We want to have the alternatives
code as clear and as transparent as possible because, well, of obvious
reasons. :-)

Thx.
diff mbox series

Patch

diff --git a/MAINTAINERS b/MAINTAINERS
index c2d975da561f..5bf65a49fa19 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -17203,6 +17203,7 @@  M:	"VMware, Inc." <pv-drivers@vmware.com>
 L:	virtualization@lists.linux-foundation.org
 S:	Supported
 F:	arch/x86/kernel/cpu/vmware.c
+F:	arch/x86/include/asm/vmware.h
 
 VMWARE PVRDMA DRIVER
 M:	Adit Ranadive <aditr@vmware.com>
diff --git a/arch/x86/include/asm/cpufeatures.h b/arch/x86/include/asm/cpufeatures.h
index 998c2cc08363..55fa3b3f0bac 100644
--- a/arch/x86/include/asm/cpufeatures.h
+++ b/arch/x86/include/asm/cpufeatures.h
@@ -232,6 +232,8 @@ 
 #define X86_FEATURE_VMMCALL		( 8*32+15) /* Prefer VMMCALL to VMCALL */
 #define X86_FEATURE_XENPV		( 8*32+16) /* "" Xen paravirtual guest */
 #define X86_FEATURE_EPT_AD		( 8*32+17) /* Intel Extended Page Table access-dirty bit */
+#define X86_FEATURE_VMCALL		( 8*32+18) /* "" Hypervisor supports the VMCALL instruction */
+#define X86_FEATURE_VMW_VMMCALL		( 8*32+19) /* "" VMware prefers VMMCALL hypercall instruction */
 
 /* Intel-defined CPU features, CPUID level 0x00000007:0 (EBX), word 9 */
 #define X86_FEATURE_FSGSBASE		( 9*32+ 0) /* RDFSBASE, WRFSBASE, RDGSBASE, WRGSBASE instructions*/
diff --git a/arch/x86/include/asm/vmware.h b/arch/x86/include/asm/vmware.h
new file mode 100644
index 000000000000..4b220e2bb3e8
--- /dev/null
+++ b/arch/x86/include/asm/vmware.h
@@ -0,0 +1,48 @@ 
+/* SPDX-License-Identifier: GPL-2.0 or MIT */
+#ifndef _ASM_X86_VMWARE_H
+#define _ASM_X86_VMWARE_H
+
+#include <asm/cpufeatures.h>
+#include <asm/alternative.h>
+
+/*
+ * The hypercall definitions differ in the low word of the edx argument in
+ * the following way: The old port base interface uses the port number to
+ * distinguish between high- and low bandwidth versions. The new vmcall
+ * interface instead uses a set of flags to select bandwidth mode and
+ * transfer direction. New driver code should strictly use the new
+ * definition of edx content.
+ */
+
+/* Old port-based version */
+#define VMWARE_HYPERVISOR_PORT    "0x5658"
+#define VMWARE_HYPERVISOR_PORT_HB "0x5659"
+
+/* Current vmcall / vmmcall version */
+#define VMWARE_HYPERVISOR_HB   BIT(0)
+#define VMWARE_HYPERVISOR_OUT  BIT(1)
+
+/* The low bandwidth call. The low word of edx is presumed clear. */
+#define VMWARE_HYPERCALL						\
+	ALTERNATIVE_2("movw $" VMWARE_HYPERVISOR_PORT ", %%dx; inl (%%dx)", \
+		      "vmcall", X86_FEATURE_VMCALL,			\
+		      "vmmcall", X86_FEATURE_VMW_VMMCALL)
+
+/*
+ * The high bandwidth out call. The low word of edx is presumed to have the
+ * HB and OUT bits set.
+ */
+#define VMWARE_HYPERCALL_HB_OUT						\
+	ALTERNATIVE_2("movw $" VMWARE_HYPERVISOR_PORT_HB ", %%dx; rep outsb", \
+		      "vmcall", X86_FEATURE_VMCALL,			\
+		      "vmmcall", X86_FEATURE_VMW_VMMCALL)
+
+/*
+ * The high bandwidth in call. The low word of edx is presumed to have the
+ * HB bit set.
+ */
+#define VMWARE_HYPERCALL_HB_IN						\
+	ALTERNATIVE_2("movw $" VMWARE_HYPERVISOR_PORT_HB ", %%dx; rep insb", \
+		      "vmcall", X86_FEATURE_VMCALL,			\
+		      "vmmcall", X86_FEATURE_VMW_VMMCALL)
+#endif
diff --git a/arch/x86/kernel/cpu/vmware.c b/arch/x86/kernel/cpu/vmware.c
index fcb84b1e099e..abaa1b27353c 100644
--- a/arch/x86/kernel/cpu/vmware.c
+++ b/arch/x86/kernel/cpu/vmware.c
@@ -31,6 +31,7 @@ 
 #include <asm/timer.h>
 #include <asm/apic.h>
 #include <asm/svm.h>
+#include <asm/vmware.h>
 
 #undef pr_fmt
 #define pr_fmt(fmt)	"vmware: " fmt
@@ -41,7 +42,6 @@ 
 #define CPUID_VMWARE_FEATURES_ECX_VMCALL     BIT(1)
 
 #define VMWARE_HYPERVISOR_MAGIC	0x564D5868
-#define VMWARE_HYPERVISOR_PORT	0x5658
 
 #define VMWARE_CMD_GETVERSION    10
 #define VMWARE_CMD_GETHZ         45
@@ -165,6 +165,10 @@  static void __init vmware_set_capabilities(void)
 {
 	setup_force_cpu_cap(X86_FEATURE_CONSTANT_TSC);
 	setup_force_cpu_cap(X86_FEATURE_TSC_RELIABLE);
+	if (vmware_hypercall_mode == CPUID_VMWARE_FEATURES_ECX_VMCALL)
+		setup_force_cpu_cap(X86_FEATURE_VMCALL);
+	else if (vmware_hypercall_mode == CPUID_VMWARE_FEATURES_ECX_VMMCALL)
+		setup_force_cpu_cap(X86_FEATURE_VMW_VMMCALL);
 }
 
 static void __init vmware_platform_setup(void)