arch/x86/hyperv/hv_crash.c | 80 ++++++++++---------- 1 file changed, 42 insertions(+), 38 deletions(-)
From: Ard Biesheuvel <ardb@kernel.org>
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@kernel.org>
---
Build tested only.
Cc: Mukesh Rathor <mrathor@linux.microsoft.com>
Cc: "K. Y. Srinivasan" <kys@microsoft.com>
Cc: Haiyang Zhang <haiyangz@microsoft.com>
Cc: Wei Liu <wei.liu@kernel.org>
Cc: Dexuan Cui <decui@microsoft.com>
Cc: Long Li <longli@microsoft.com>
Cc: Thomas Gleixner <tglx@kernel.org>
Cc: Ingo Molnar <mingo@redhat.com>
Cc: Borislav Petkov <bp@alien8.de>
Cc: Dave Hansen <dave.hansen@linux.intel.com>
Cc: "H. Peter Anvin" <hpa@zytor.com>
Cc: Uros Bizjak <ubizjak@gmail.com>
Cc: linux-hyperv@vger.kernel.org
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"
+ "pushq %q0 \n\t"
+ "lretq \n\t"
+ :: "a"(hv_crash_ctxt.cs), "i"(hv_crash_handle));
}
/* Tell gcc we are using lretq long jump in the above function intentionally */
STACK_FRAME_NON_STANDARD(hv_crash_c_entry);
--
2.53.0.414.gf7e9f6c205-goog
On Thu, Feb 26, 2026 at 10:50:57AM +0100, Ard Biesheuvel wrote:
> From: Ard Biesheuvel <ardb@kernel.org>
>
> 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@kernel.org>
Ard, thank you for the patch.
For avoidance of doubt, I expect another version to be sent. We will
review and test the new version on our side.
Wei
> @@ -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));
I know this is pre-existing, but the asm here is poor.
All segment registers loads can have a memory operand, rather than
forcing through %eax, which in turn reduces the setup logic the compiler
needs to emit.
Something like this:
"movl %0, %%ss" : : "m"(hv_crash_ctxt.ss)
ought to do.
>
> - 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" + "pushq %q0 \n\t" + "lretq \n\t" + ::
> "a"(hv_crash_ctxt.cs), "i"(hv_crash_handle));
As Uros notes, "a" is clobbered here but the compiler is not informed.
But, it's not necessary.
As a naked function you could even use 3x asm() statements, but you can
get the compiler to sort out the function reference automatically with:
asm volatile ("push %q0\n\t"
"push %q1\n\t"
"lretq"
:: "r"(hv_crash_ctxt.cs), "r"(hv_crash_handle));
(Only tested in godbolt)
~Andrew
On Thu, 26 Feb 2026, at 13:01, Andrew Cooper wrote:
>> @@ -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));
>
> I know this is pre-existing, but the asm here is poor.
>
> All segment registers loads can have a memory operand, rather than
> forcing through %eax, which in turn reduces the setup logic the compiler
> needs to emit.
>
> Something like this:
>
> "movl %0, %%ss" : : "m"(hv_crash_ctxt.ss)
>
> ought to do.
>
'movw' seems to work, yes.
...
>
> As Uros notes, "a" is clobbered here but the compiler is not informed.
> But, it's not necessary.
>
> As a naked function you could even use 3x asm() statements, but you can
> get the compiler to sort out the function reference automatically with:
>
> asm volatile ("push %q0\n\t"
> "push %q1\n\t"
> "lretq"
> :: "r"(hv_crash_ctxt.cs), "r"(hv_crash_handle));
>
>
Yeah much better - thanks.
On 26/02/2026 1:07 pm, Ard Biesheuvel wrote:
>
> On Thu, 26 Feb 2026, at 13:01, Andrew Cooper wrote:
>>> @@ -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));
>> I know this is pre-existing, but the asm here is poor.
>>
>> All segment registers loads can have a memory operand, rather than
>> forcing through %eax, which in turn reduces the setup logic the compiler
>> needs to emit.
>>
>> Something like this:
>>
>> "movl %0, %%ss" : : "m"(hv_crash_ctxt.ss)
>>
>> ought to do.
>>
> 'movw' seems to work, yes.
movw works, but is sub-optimal.
The segment register instructions are somewhat weird even by x86 standards.
They should always be written as 32-bit operations (movl, and %eax),
removing the operand size prefix which is not necessary for these
instructions to function correctly.
It's absolutely marginal, but it does always pain me to read asm like
this and see the myth of how to access segment selectors being repeated
time and time again.
~Andrew
On Thu, 26 Feb 2026, at 14:24, Andrew Cooper wrote:
> On 26/02/2026 1:07 pm, Ard Biesheuvel wrote:
>>
>> On Thu, 26 Feb 2026, at 13:01, Andrew Cooper wrote:
>>>> @@ -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));
>>> I know this is pre-existing, but the asm here is poor.
>>>
>>> All segment registers loads can have a memory operand, rather than
>>> forcing through %eax, which in turn reduces the setup logic the compiler
>>> needs to emit.
>>>
>>> Something like this:
>>>
>>> "movl %0, %%ss" : : "m"(hv_crash_ctxt.ss)
>>>
>>> ought to do.
>>>
>> 'movw' seems to work, yes.
>
> movw works, but is sub-optimal.
>
Can you give an asm example where movl with a segment register is accepted by the assembler? I only managed that with movw, hence my comment.
On 26/02/2026 1:29 pm, Ard Biesheuvel wrote:
>
> On Thu, 26 Feb 2026, at 14:24, Andrew Cooper wrote:
>> On 26/02/2026 1:07 pm, Ard Biesheuvel wrote:
>>> On Thu, 26 Feb 2026, at 13:01, Andrew Cooper wrote:
>>>>> @@ -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));
>>>> I know this is pre-existing, but the asm here is poor.
>>>>
>>>> All segment registers loads can have a memory operand, rather than
>>>> forcing through %eax, which in turn reduces the setup logic the compiler
>>>> needs to emit.
>>>>
>>>> Something like this:
>>>>
>>>> "movl %0, %%ss" : : "m"(hv_crash_ctxt.ss)
>>>>
>>>> ought to do.
>>>>
>>> 'movw' seems to work, yes.
>> movw works, but is sub-optimal.
>>
> Can you give an asm example where movl with a segment register is accepted by the assembler? I only managed that with movw, hence my comment.
Oh lovely, that looks like a binutils bug, but I bet it comes from not
realising that `mov sreg` is different to the more general mov forms.
Using no suffix will emit the optimal instruction without a warning.
https://godbolt.org/z/GYKs31Gqn
~Andrew
On Thu, Feb 26, 2026 at 10:51 AM Ard Biesheuvel <ardb+git@google.com> wrote:
>
> From: Ard Biesheuvel <ardb@kernel.org>
>
> 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@kernel.org>
> ---
> Build tested only.
>
> Cc: Mukesh Rathor <mrathor@linux.microsoft.com>
> Cc: "K. Y. Srinivasan" <kys@microsoft.com>
> Cc: Haiyang Zhang <haiyangz@microsoft.com>
> Cc: Wei Liu <wei.liu@kernel.org>
> Cc: Dexuan Cui <decui@microsoft.com>
> Cc: Long Li <longli@microsoft.com>
> Cc: Thomas Gleixner <tglx@kernel.org>
> Cc: Ingo Molnar <mingo@redhat.com>
> Cc: Borislav Petkov <bp@alien8.de>
> Cc: Dave Hansen <dave.hansen@linux.intel.com>
> Cc: "H. Peter Anvin" <hpa@zytor.com>
> Cc: Uros Bizjak <ubizjak@gmail.com>
> Cc: linux-hyperv@vger.kernel.org
>
> 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.
© 2016 - 2026 Red Hat, Inc.