x86/retpoline/hyperv: Convert assembler indirect jumps
[pandora-kernel.git] / drivers / hv / hv.c
index 0fb100e..c31014b 100644 (file)
@@ -27,6 +27,7 @@
 #include <linux/vmalloc.h>
 #include <linux/hyperv.h>
 #include <asm/hyperv.h>
+#include <asm/nospec-branch.h>
 #include "hyperv_vmbus.h"
 
 /* The one and only */
@@ -113,10 +114,13 @@ static u64 do_hypercall(u64 control, void *input, void *output)
        u64 output_address = (output) ? virt_to_phys(output) : 0;
        void *hypercall_page = hv_context.hypercall_page;
 
-       __asm__ __volatile__("mov %0, %%r8" : : "r" (output_address) : "r8");
-       __asm__ __volatile__("call *%3" : "=a" (hv_status) :
-                            "c" (control), "d" (input_address),
-                            "m" (hypercall_page));
+       __asm__ __volatile__("mov %4, %%r8\n"
+                            CALL_NOSPEC
+                            : "=a" (hv_status), ASM_CALL_CONSTRAINT,
+                              "+c" (control), "+d" (input_address)
+                            :  "r" (output_address),
+                               THUNK_TARGET(hypercall_page)
+                            : "cc", "memory", "r8", "r9", "r10", "r11");
 
        return hv_status;
 
@@ -134,11 +138,14 @@ static u64 do_hypercall(u64 control, void *input, void *output)
        u32 output_address_lo = output_address & 0xFFFFFFFF;
        void *hypercall_page = hv_context.hypercall_page;
 
-       __asm__ __volatile__ ("call *%8" : "=d"(hv_status_hi),
-                             "=a"(hv_status_lo) : "d" (control_hi),
-                             "a" (control_lo), "b" (input_address_hi),
-                             "c" (input_address_lo), "D"(output_address_hi),
-                             "S"(output_address_lo), "m" (hypercall_page));
+       __asm__ __volatile__(CALL_NOSPEC
+                            : "=d" (hv_status_hi), "=a" (hv_status_lo),
+                              "+c" (input_address_lo), ASM_CALL_CONSTRAINT
+                            : "d" (control_hi), "a" (control_lo),
+                              "b" (input_address_hi),
+                              "D"(output_address_hi), "S"(output_address_lo),
+                              THUNK_TARGET(hypercall_page)
+                            : "cc", "memory");
 
        return hv_status_lo | ((u64)hv_status_hi << 32);
 #endif /* !x86_64 */
@@ -158,6 +165,8 @@ int hv_init(void)
        memset(hv_context.synic_event_page, 0, sizeof(void *) * MAX_NUM_CPUS);
        memset(hv_context.synic_message_page, 0,
               sizeof(void *) * MAX_NUM_CPUS);
+       memset(hv_context.post_msg_page, 0,
+              sizeof(void *) * MAX_NUM_CPUS);
 
        if (!query_hypervisor_presence())
                goto cleanup;
@@ -176,7 +185,7 @@ int hv_init(void)
        /* See if the hypercall page is already set */
        rdmsrl(HV_X64_MSR_HYPERCALL, hypercall_msr.as_uint64);
 
-       virtaddr = __vmalloc(PAGE_SIZE, GFP_KERNEL, PAGE_KERNEL_EXEC);
+       virtaddr = __vmalloc(PAGE_SIZE, GFP_KERNEL, PAGE_KERNEL_RX);
 
        if (!virtaddr)
                goto cleanup;
@@ -258,26 +267,18 @@ u16 hv_post_message(union hv_connection_id connection_id,
                  enum hv_message_type message_type,
                  void *payload, size_t payload_size)
 {
-       struct aligned_input {
-               u64 alignment8;
-               struct hv_input_post_message msg;
-       };
 
        struct hv_input_post_message *aligned_msg;
        u16 status;
-       unsigned long addr;
 
        if (payload_size > HV_MESSAGE_PAYLOAD_BYTE_COUNT)
                return -EMSGSIZE;
 
-       addr = (unsigned long)kmalloc(sizeof(struct aligned_input), GFP_ATOMIC);
-       if (!addr)
-               return -ENOMEM;
-
        aligned_msg = (struct hv_input_post_message *)
-                       (ALIGN(addr, HV_HYPERCALL_PARAM_ALIGN));
+                       hv_context.post_msg_page[get_cpu()];
 
        aligned_msg->connectionid = connection_id;
+       aligned_msg->reserved = 0;
        aligned_msg->message_type = message_type;
        aligned_msg->payload_size = payload_size;
        memcpy((void *)aligned_msg->payload, payload, payload_size);
@@ -285,8 +286,7 @@ u16 hv_post_message(union hv_connection_id connection_id,
        status = do_hypercall(HVCALL_POST_MESSAGE, aligned_msg, NULL)
                & 0xFFFF;
 
-       kfree((void *)addr);
-
+       put_cpu();
        return status;
 }
 
@@ -347,6 +347,14 @@ void hv_synic_init(void *irqarg)
                goto cleanup;
        }
 
+       hv_context.post_msg_page[cpu] =
+               (void *)get_zeroed_page(GFP_ATOMIC);
+
+       if (hv_context.post_msg_page[cpu] == NULL) {
+               pr_err("Unable to allocate post msg page\n");
+               goto cleanup;
+       }
+
        /* Setup the Synic's message page */
        rdmsrl(HV_X64_MSR_SIMP, simp.as_uint64);
        simp.simp_enabled = 1;
@@ -388,6 +396,8 @@ cleanup:
 
        if (hv_context.synic_message_page[cpu])
                free_page((unsigned long)hv_context.synic_message_page[cpu]);
+       if (hv_context.post_msg_page[cpu])
+               free_page((unsigned long)hv_context.post_msg_page[cpu]);
        return;
 }
 
@@ -426,4 +436,5 @@ void hv_synic_cleanup(void *arg)
 
        free_page((unsigned long)hv_context.synic_message_page[cpu]);
        free_page((unsigned long)hv_context.synic_event_page[cpu]);
+       free_page((unsigned long)hv_context.post_msg_page[cpu]);
 }