Re: [RFT PATCH] x86/hyperv: Use __naked attribute to fix stackless C function
From: Uros Bizjak
Date: Thu Feb 26 2026 - 05:37:18 EST
On Thu, Feb 26, 2026 at 10:51 AM Ard Biesheuvel <ardb+git@xxxxxxxxxx> wrote:
>
> From: Ard Biesheuvel <ardb@xxxxxxxxxx>
>
> hv_crash_c_entry() is a C function that is entered without a stack,
> and this is only allowed for functions that have the __naked attribute,
> which informs the compiler that it must not emit the usual prologue and
> epilogue or emit any other kind of instrumentation that relies on a
> stack frame.
>
> So split up the function, and set the __naked attribute on the initial
> part that sets up the stack, GDT, IDT and other pieces that are needed
> for ordinary C execution. Given that function calls are not permitted
> either, use the existing long return coded in an asm() block to call the
> second part of the function, which is an ordinary function that is
> permitted to call other functions as usual.
>
> Fixes: 94212d34618c ("x86/hyperv: Implement hypervisor RAM collection into vmcore")
> Signed-off-by: Ard Biesheuvel <ardb@xxxxxxxxxx>
> ---
> Build tested only.
>
> Cc: Mukesh Rathor <mrathor@xxxxxxxxxxxxxxxxxxx>
> Cc: "K. Y. Srinivasan" <kys@xxxxxxxxxxxxx>
> Cc: Haiyang Zhang <haiyangz@xxxxxxxxxxxxx>
> Cc: Wei Liu <wei.liu@xxxxxxxxxx>
> Cc: Dexuan Cui <decui@xxxxxxxxxxxxx>
> Cc: Long Li <longli@xxxxxxxxxxxxx>
> Cc: Thomas Gleixner <tglx@xxxxxxxxxx>
> Cc: Ingo Molnar <mingo@xxxxxxxxxx>
> Cc: Borislav Petkov <bp@xxxxxxxxx>
> Cc: Dave Hansen <dave.hansen@xxxxxxxxxxxxxxx>
> Cc: "H. Peter Anvin" <hpa@xxxxxxxxx>
> Cc: Uros Bizjak <ubizjak@xxxxxxxxx>
> Cc: linux-hyperv@xxxxxxxxxxxxxxx
>
> arch/x86/hyperv/hv_crash.c | 80 ++++++++++----------
> 1 file changed, 42 insertions(+), 38 deletions(-)
>
> diff --git a/arch/x86/hyperv/hv_crash.c b/arch/x86/hyperv/hv_crash.c
> index a78e4fed5720..d77766e8d37e 100644
> --- a/arch/x86/hyperv/hv_crash.c
> +++ b/arch/x86/hyperv/hv_crash.c
> @@ -107,14 +107,12 @@ static void __noreturn hv_panic_timeout_reboot(void)
> cpu_relax();
> }
>
> -/* This cannot be inlined as it needs stack */
> -static noinline __noclone void hv_crash_restore_tss(void)
> +static void hv_crash_restore_tss(void)
> {
> load_TR_desc();
> }
>
> -/* This cannot be inlined as it needs stack */
> -static noinline void hv_crash_clear_kernpt(void)
> +static void hv_crash_clear_kernpt(void)
> {
> pgd_t *pgd;
> p4d_t *p4d;
> @@ -125,6 +123,25 @@ static noinline void hv_crash_clear_kernpt(void)
> native_p4d_clear(p4d);
> }
>
> +
> +static void __noreturn hv_crash_handle(void)
> +{
> + hv_crash_restore_tss();
> + hv_crash_clear_kernpt();
> +
> + /* we are now fully in devirtualized normal kernel mode */
> + __crash_kexec(NULL);
> +
> + hv_panic_timeout_reboot();
> +}
> +
> +/*
> + * __naked functions do not permit function calls, not even to __always_inline
> + * functions that only contain asm() blocks themselves. So use a macro instead.
> + */
> +#define hv_wrmsr(msr, val) \
> + asm("wrmsr" :: "c"(msr), "a"((u32)val), "d"((u32)(val >> 32)) : "memory")
> +
> /*
> * This is the C entry point from the asm glue code after the disable hypercall.
> * We enter here in IA32-e long mode, ie, full 64bit mode running on kernel
> @@ -133,49 +150,36 @@ static noinline void hv_crash_clear_kernpt(void)
> * available. We restore kernel GDT, and rest of the context, and continue
> * to kexec.
> */
> -static asmlinkage void __noreturn hv_crash_c_entry(void)
> +static void __naked hv_crash_c_entry(void)
> {
> - struct hv_crash_ctxt *ctxt = &hv_crash_ctxt;
> -
> /* first thing, restore kernel gdt */
> - native_load_gdt(&ctxt->gdtr);
> + asm volatile("lgdt %0" : : "m" (hv_crash_ctxt.gdtr));
>
> - asm volatile("movw %%ax, %%ss" : : "a"(ctxt->ss));
> - asm volatile("movq %0, %%rsp" : : "m"(ctxt->rsp));
> + asm volatile("movw %%ax, %%ss" : : "a"(hv_crash_ctxt.ss));
> + asm volatile("movq %0, %%rsp" : : "m"(hv_crash_ctxt.rsp));
>
> - asm volatile("movw %%ax, %%ds" : : "a"(ctxt->ds));
> - asm volatile("movw %%ax, %%es" : : "a"(ctxt->es));
> - asm volatile("movw %%ax, %%fs" : : "a"(ctxt->fs));
> - asm volatile("movw %%ax, %%gs" : : "a"(ctxt->gs));
> + asm volatile("movw %%ax, %%ds" : : "a"(hv_crash_ctxt.ds));
> + asm volatile("movw %%ax, %%es" : : "a"(hv_crash_ctxt.es));
> + asm volatile("movw %%ax, %%fs" : : "a"(hv_crash_ctxt.fs));
> + asm volatile("movw %%ax, %%gs" : : "a"(hv_crash_ctxt.gs));
>
> - native_wrmsrq(MSR_IA32_CR_PAT, ctxt->pat);
> - asm volatile("movq %0, %%cr0" : : "r"(ctxt->cr0));
> + hv_wrmsr(MSR_IA32_CR_PAT, hv_crash_ctxt.pat);
> + asm volatile("movq %0, %%cr0" : : "r"(hv_crash_ctxt.cr0));
>
> - asm volatile("movq %0, %%cr8" : : "r"(ctxt->cr8));
> - asm volatile("movq %0, %%cr4" : : "r"(ctxt->cr4));
> - asm volatile("movq %0, %%cr2" : : "r"(ctxt->cr4));
> + asm volatile("movq %0, %%cr8" : : "r"(hv_crash_ctxt.cr8));
> + asm volatile("movq %0, %%cr4" : : "r"(hv_crash_ctxt.cr4));
> + asm volatile("movq %0, %%cr2" : : "r"(hv_crash_ctxt.cr4));
>
> - native_load_idt(&ctxt->idtr);
> - native_wrmsrq(MSR_GS_BASE, ctxt->gsbase);
> - native_wrmsrq(MSR_EFER, ctxt->efer);
> + asm volatile("lidt %0" : : "m" (hv_crash_ctxt.idtr));
> + hv_wrmsr(MSR_GS_BASE, hv_crash_ctxt.gsbase);
> + hv_wrmsr(MSR_EFER, hv_crash_ctxt.efer);
>
> /* restore the original kernel CS now via far return */
> - asm volatile("movzwq %0, %%rax\n\t"
> - "pushq %%rax\n\t"
> - "pushq $1f\n\t"
> - "lretq\n\t"
> - "1:nop\n\t" : : "m"(ctxt->cs) : "rax");
> -
> - /* We are in asmlinkage without stack frame, hence make C function
> - * calls which will buy stack frames.
> - */
> - hv_crash_restore_tss();
> - hv_crash_clear_kernpt();
> -
> - /* we are now fully in devirtualized normal kernel mode */
> - __crash_kexec(NULL);
> -
> - hv_panic_timeout_reboot();
> + asm volatile("pushq %q0 \n\t"
> + "leaq %c1(%%rip), %q0 \n\t"
You can use %a1 instead of %c1(%%rip).
> + "pushq %q0 \n\t"
> + "lretq \n\t"
No need for terminating \n\t after the last insn in the asm template.
> + :: "a"(hv_crash_ctxt.cs), "i"(hv_crash_handle));
Pedantically, you need ': "+a"(...) : "i"(...)' here.
Uros.