Modify the five hypercall wrapper functions to check is_realm_world()
and use the per-CPU rsi_host_call structure when inside a Realm.

Signed-off-by: Kameron Carr <[email protected]>
---
 arch/arm64/hyperv/hv_core.c | 175 +++++++++++++++++++++++++++++-------
 1 file changed, 141 insertions(+), 34 deletions(-)

diff --git a/arch/arm64/hyperv/hv_core.c b/arch/arm64/hyperv/hv_core.c
index e33a9e3c366a1..1759998ef2667 100644
--- a/arch/arm64/hyperv/hv_core.c
+++ b/arch/arm64/hyperv/hv_core.c
@@ -16,6 +16,7 @@
 #include <asm-generic/bug.h>
 #include <hyperv/hvhdk.h>
 #include <asm/mshyperv.h>
+#include <asm/rsi.h>
 
 /*
  * hv_do_hypercall- Invoke the specified hypercall
@@ -25,12 +26,32 @@ u64 hv_do_hypercall(u64 control, void *input, void *output)
        struct arm_smccc_res    res;
        u64                     input_address;
        u64                     output_address;
+       struct rsi_host_call *hostcall;
+       unsigned long flags;
+       u64 ret;
 
        input_address = input ? virt_to_phys(input) : 0;
        output_address = output ? virt_to_phys(output) : 0;
 
-       arm_smccc_1_1_hvc(HV_FUNC_ID, control,
-                         input_address, output_address, &res);
+       if (is_realm_world()) {
+               local_irq_save(flags);
+               hostcall = *this_cpu_ptr(hyperv_pcpu_hostcall_struct);
+               memset(hostcall, 0, sizeof(*hostcall));
+               hostcall->gprs[0] = HV_FUNC_ID;
+               hostcall->gprs[1] = control;
+               hostcall->gprs[2] = input_address;
+               hostcall->gprs[3] = output_address;
+
+               if (rsi_host_call(virt_to_phys(hostcall)) == RSI_SUCCESS)
+                       ret = hostcall->gprs[0];
+               else
+                       ret = HV_STATUS_INVALID_HYPERCALL_INPUT;
+               local_irq_restore(flags);
+               return ret;
+       }
+
+       arm_smccc_1_1_hvc(HV_FUNC_ID, control, input_address,
+                         output_address, &res);
        return res.a0;
 }
 EXPORT_SYMBOL_GPL(hv_do_hypercall);
@@ -45,9 +66,28 @@ u64 hv_do_fast_hypercall8(u16 code, u64 input)
 {
        struct arm_smccc_res    res;
        u64                     control;
+       struct rsi_host_call *hostcall;
+       unsigned long flags;
+       u64 ret;
 
        control = (u64)code | HV_HYPERCALL_FAST_BIT;
 
+       if (is_realm_world()) {
+               local_irq_save(flags);
+               hostcall = *this_cpu_ptr(hyperv_pcpu_hostcall_struct);
+               memset(hostcall, 0, sizeof(*hostcall));
+               hostcall->gprs[0] = HV_FUNC_ID;
+               hostcall->gprs[1] = control;
+               hostcall->gprs[2] = input;
+
+               if (rsi_host_call(virt_to_phys(hostcall)) == RSI_SUCCESS)
+                       ret = hostcall->gprs[0];
+               else
+                       ret = HV_STATUS_INVALID_HYPERCALL_INPUT;
+               local_irq_restore(flags);
+               return ret;
+       }
+
        arm_smccc_1_1_hvc(HV_FUNC_ID, control, input, &res);
        return res.a0;
 }
@@ -62,9 +102,29 @@ u64 hv_do_fast_hypercall16(u16 code, u64 input1, u64 input2)
 {
        struct arm_smccc_res    res;
        u64                     control;
+       struct rsi_host_call *hostcall;
+       unsigned long flags;
+       u64 ret;
 
        control = (u64)code | HV_HYPERCALL_FAST_BIT;
 
+       if (is_realm_world()) {
+               local_irq_save(flags);
+               hostcall = *this_cpu_ptr(hyperv_pcpu_hostcall_struct);
+               memset(hostcall, 0, sizeof(*hostcall));
+               hostcall->gprs[0] = HV_FUNC_ID;
+               hostcall->gprs[1] = control;
+               hostcall->gprs[2] = input1;
+               hostcall->gprs[3] = input2;
+
+               if (rsi_host_call(virt_to_phys(hostcall)) == RSI_SUCCESS)
+                       ret = hostcall->gprs[0];
+               else
+                       ret = HV_STATUS_INVALID_HYPERCALL_INPUT;
+               local_irq_restore(flags);
+               return ret;
+       }
+
        arm_smccc_1_1_hvc(HV_FUNC_ID, control, input1, input2, &res);
        return res.a0;
 }
@@ -76,24 +136,44 @@ EXPORT_SYMBOL_GPL(hv_do_fast_hypercall16);
 void hv_set_vpreg(u32 msr, u64 value)
 {
        struct arm_smccc_res res;
+       struct rsi_host_call *hostcall;
+       unsigned long flags;
+       u64 status;
+
+       if (is_realm_world()) {
+               local_irq_save(flags);
+               hostcall = *this_cpu_ptr(hyperv_pcpu_hostcall_struct);
+               memset(hostcall, 0, sizeof(*hostcall));
+               hostcall->gprs[0] = HV_FUNC_ID;
+               hostcall->gprs[1] = HVCALL_SET_VP_REGISTERS |
+                                   HV_HYPERCALL_FAST_BIT |
+                                   HV_HYPERCALL_REP_COMP_1;
+               hostcall->gprs[2] = HV_PARTITION_ID_SELF;
+               hostcall->gprs[3] = HV_VP_INDEX_SELF;
+               hostcall->gprs[4] = msr;
+               hostcall->gprs[6] = value;
 
-       arm_smccc_1_1_hvc(HV_FUNC_ID,
-               HVCALL_SET_VP_REGISTERS | HV_HYPERCALL_FAST_BIT |
-                       HV_HYPERCALL_REP_COMP_1,
-               HV_PARTITION_ID_SELF,
-               HV_VP_INDEX_SELF,
-               msr,
-               0,
-               value,
-               0,
-               &res);
+               if (rsi_host_call(virt_to_phys(hostcall)) == RSI_SUCCESS)
+                       status = hostcall->gprs[0];
+               else
+                       status = HV_STATUS_INVALID_HYPERCALL_INPUT;
+               local_irq_restore(flags);
+       } else {
+               arm_smccc_1_1_hvc(HV_FUNC_ID,
+                                 HVCALL_SET_VP_REGISTERS |
+                                         HV_HYPERCALL_FAST_BIT |
+                                         HV_HYPERCALL_REP_COMP_1,
+                                 HV_PARTITION_ID_SELF, HV_VP_INDEX_SELF, msr,
+                                 0, value, 0, &res);
+               status = res.a0;
+       }
 
        /*
-        * Something is fundamentally broken in the hypervisor if
-        * setting a VP register fails. There's really no way to
-        * continue as a guest VM, so panic.
+        * Something is fundamentally broken in the hypervisor (or, in a
+        * Realm, the RMM denied the host call) if setting a VP register
+        * fails. There's really no way to continue as a guest VM, so panic.
         */
-       BUG_ON(!hv_result_success(res.a0));
+       BUG_ON(!hv_result_success(status));
 }
 EXPORT_SYMBOL_GPL(hv_set_vpreg);
 
@@ -108,29 +188,56 @@ void hv_get_vpreg_128(u32 msr, struct 
hv_get_vp_registers_output *result)
 {
        struct arm_smccc_1_2_regs args;
        struct arm_smccc_1_2_regs res;
+       struct rsi_host_call *hostcall;
+       u64 status;
 
-       args.a0 = HV_FUNC_ID;
-       args.a1 = HVCALL_GET_VP_REGISTERS | HV_HYPERCALL_FAST_BIT |
-                       HV_HYPERCALL_REP_COMP_1;
-       args.a2 = HV_PARTITION_ID_SELF;
-       args.a3 = HV_VP_INDEX_SELF;
-       args.a4 = msr;
+       if (is_realm_world()) {
+               unsigned long flags;
 
-       /*
-        * Use the SMCCC 1.2 interface because the results are in registers
-        * beyond X0-X3.
-        */
-       arm_smccc_1_2_hvc(&args, &res);
+               local_irq_save(flags);
+               hostcall = *this_cpu_ptr(hyperv_pcpu_hostcall_struct);
+               memset(hostcall, 0, sizeof(*hostcall));
+
+               hostcall->gprs[0] = HV_FUNC_ID;
+               hostcall->gprs[1] = HVCALL_GET_VP_REGISTERS |
+                                   HV_HYPERCALL_FAST_BIT |
+                                   HV_HYPERCALL_REP_COMP_1;
+               hostcall->gprs[2] = HV_PARTITION_ID_SELF;
+               hostcall->gprs[3] = HV_VP_INDEX_SELF;
+               hostcall->gprs[4] = msr;
+
+               if (rsi_host_call(virt_to_phys(hostcall)) == RSI_SUCCESS) {
+                       status = hostcall->gprs[0];
+                       result->as64.low = hostcall->gprs[6];
+                       result->as64.high = hostcall->gprs[7];
+               } else {
+                       status = HV_STATUS_INVALID_HYPERCALL_INPUT;
+               }
+               local_irq_restore(flags);
+       } else {
+               args.a0 = HV_FUNC_ID;
+               args.a1 = HVCALL_GET_VP_REGISTERS | HV_HYPERCALL_FAST_BIT |
+                         HV_HYPERCALL_REP_COMP_1;
+               args.a2 = HV_PARTITION_ID_SELF;
+               args.a3 = HV_VP_INDEX_SELF;
+               args.a4 = msr;
+
+               /*
+                * Use the SMCCC 1.2 interface because the results are in
+                * registers beyond X0-X3.
+                */
+               arm_smccc_1_2_hvc(&args, &res);
+               status = res.a0;
+               result->as64.low = res.a6;
+               result->as64.high = res.a7;
+       }
 
        /*
-        * Something is fundamentally broken in the hypervisor if
-        * getting a VP register fails. There's really no way to
-        * continue as a guest VM, so panic.
+        * Something is fundamentally broken in the hypervisor (or, in a
+        * Realm, the RMM denied the host call) if getting a VP register
+        * fails. There's really no way to continue as a guest VM, so panic.
         */
-       BUG_ON(!hv_result_success(res.a0));
-
-       result->as64.low = res.a6;
-       result->as64.high = res.a7;
+       BUG_ON(!hv_result_success(status));
 }
 EXPORT_SYMBOL_GPL(hv_get_vpreg_128);
 
-- 
2.45.4


Reply via email to