From: Kameron Carr <[email protected]> Sent: Tuesday, June 9,
2026 11:10 AM
>
> 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;
This code sequence for handling the realm case is almost exactly
duplicated for the three hypercall variants. The only difference is
how gprs[2] and gprs[3] are populated. So I think the code
sequence could go into a helper routine with the appropriate
values for gprs[2] and gprs[3] passed in.
> + }
> +
> + 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
>