/* SPDX-License-Identifier: GPL-2.0-only */
/*
* relocate_kernel . S - put the kernel image in place to boot
* Copyright ( C ) 2002 - 2005 Eric Biederman < ebiederm @ xmission . com >
*/
#include <linux/linkage.h>
#include <linux/stringify.h>
#include <asm/alternative.h>
#include <asm/page_types.h>
#include <asm/kexec.h>
#include <asm/processor-flags.h>
#include <asm/pgtable_types.h>
#include <asm/nospec-branch.h>
#include <asm/unwind_hints.h>
#include <asm/asm-offsets.h>
/*
* Must be relocatable PIC code callable as a C function , in particular
* there must be a plain RET and not jump to return thunk .
*/
#define PTR(x) (x << 3 )
#define PAGE_ATTR (_PAGE_PRESENT | _PAGE_RW | _PAGE_ACCESSED | _PAGE_DIRTY)
/*
* The . text . . relocate_kernel and . data . . relocate_kernel sections are copied
* into the control page , and the remainder of the page is used as the stack .
*/
.section .data ..relocate_kernel,"a" ;
/* Minimal CPU state */
SYM_DATA_LOCAL(saved_rsp, .quad 0 )
SYM_DATA_LOCAL(saved_cr0, .quad 0 )
SYM_DATA_LOCAL(saved_cr3, .quad 0 )
SYM_DATA_LOCAL(saved_cr4, .quad 0 )
/* other data */
SYM_DATA(kexec_va_control_page, .quad 0 )
SYM_DATA(kexec_pa_table_page, .quad 0 )
SYM_DATA(kexec_pa_swap_page, .quad 0 )
SYM_DATA_LOCAL(pa_backup_pages_map, .quad 0 )
SYM_DATA(kexec_debug_8250_mmio32, .quad 0 )
SYM_DATA(kexec_debug_8250_port, .word 0 )
.balign 16
SYM_DATA_START_LOCAL(kexec_debug_gdt)
.word kexec_debug_gdt_end - kexec_debug_gdt - 1
.long 0
.word 0
.quad 0 x00cf9a000000ffff /* __KERNEL32_CS */
.quad 0 x00af9a000000ffff /* __KERNEL_CS */
.quad 0 x00cf92000000ffff /* __KERNEL_DS */
SYM_DATA_END_LABEL(kexec_debug_gdt, SYM_L_LOCAL, kexec_debug_gdt_end)
.balign 8
SYM_DATA_START(kexec_debug_idt)
.skip 0 x100, 0 x00
SYM_DATA_END(kexec_debug_idt)
.section .text..relocate_kernel,"ax" ;
.code64
SYM_CODE_START_NOALIGN(relocate_kernel)
UNWIND_HINT_END_OF_STACK
ANNOTATE_NOENDBR
/*
* % rdi indirection_page
* % rsi pa_control_page
* % rdx start address
* % rcx preserve_context
* % r8 host_mem_enc_active
*/
/* Save the CPU context, used for jumping back */
pushq %rbx
pushq %rbp
pushq %r12
pushq %r13
pushq %r14
pushq %r15
pushf
/* Invalidate GDT/IDT, zero out flags */
pushq $0
pushq $0
lidt (%rsp)
lgdt (%rsp)
addq $8 , %rsp
popfq
/* Switch to the identity mapped page tables */
movq %cr3, %rax
movq kexec_pa_table_page(%rip), %r9
movq %r9, %cr3
/* Leave CR4 in %r13 to enable the right paging mode later. */
movq %cr4, %r13
/* Disable global pages immediately to ensure this mapping is RWX */
movq %r13, %r12
andq $~(X86_CR4_PGE), %r12
movq %r12, %cr4
/* Save %rsp and CRs. */
movq %r13, saved_cr4(%rip)
movq %rsp, saved_rsp(%rip)
movq %rax, saved_cr3(%rip)
movq %cr0, %rax
movq %rax, saved_cr0(%rip)
/* save indirection list for jumping back */
movq %rdi, pa_backup_pages_map(%rip)
/* Save the preserve_context to %r11 as swap_pages clobbers %rcx. */
movq %rcx, %r11
/* setup a new stack at the end of the physical control page */
lea PAGE_SIZE(%rsi), %rsp
/* jump to identity mapped page */
0 : addq $identity_mapped - 0 b, %rsi
subq $__relocate_kernel_start - 0 b, %rsi
ANNOTATE_RETPOLINE_SAFE
jmp *%rsi
SYM_CODE_END(relocate_kernel)
SYM_CODE_START_LOCAL_NOALIGN(identity_mapped)
UNWIND_HINT_END_OF_STACK
/*
* % rdi indirection page
* % rdx start address
* % r8 host_mem_enc_active
* % r9 page table page
* % r11 preserve_context
* % r13 original CR4 when relocate_kernel ( ) was invoked
*/
/* store the start address on the stack */
pushq %rdx
/* Create a GDTR (16 bits limit, 64 bits addr) on stack */
leaq kexec_debug_gdt(%rip), %rax
pushq %rax
pushw (%rax)
/* Load the GDT, put the stack back */
lgdt (%rsp)
addq $10 , %rsp
/* Test that we can load segments */
movq %ds, %rax
movq %rax, %ds
/* Now an IDTR on the stack to load the IDT the kernel created */
leaq kexec_debug_idt(%rip), %rsi
pushq %rsi
pushw $0 xff
lidt (%rsp)
addq $10 , %rsp
//int3
/*
* Clear X86_CR4_CET ( if it was set ) such that we can clear CR0_WP
* below .
*/
movq %cr4, %rax
andq $~(X86_CR4_CET), %rax
movq %rax, %cr4
/*
* Set cr0 to a known state :
* - Paging enabled
* - Alignment check disabled
* - Write protect disabled
* - No task switch
* - Don ' t do FP software emulation .
* - Protected mode enabled
*/
movq %cr0, %rax
andq $~(X86_CR0_AM | X86_CR0_WP | X86_CR0_TS | X86_CR0_EM), %rax
orl $(X86_CR0_PG | X86_CR0_PE), %eax
movq %rax, %cr0
/*
* Set cr4 to a known state :
* - physical address extension enabled
* - 5 - level paging , if it was enabled before
* - Machine check exception on TDX guest , if it was enabled before .
* Clearing MCE might not be allowed in TDX guests , depending on setup .
*
* Use R13 that contains the original CR4 value , read in relocate_kernel ( ) .
* PAE is always set in the original CR4 .
*/
andl $(X86_CR4_PAE | X86_CR4_LA57), %r13d
ALTERNATIVE "" , __stringify(orl $X86_CR4_MCE, %r13d), X86_FEATURE_TDX_GUEST
movq %r13, %cr4
/* Flush the TLB (needed?) */
movq %r9, %cr3
/*
* If SME is active , there could be old encrypted cache line
* entries that will conflict with the now unencrypted memory
* used by kexec . Flush the caches before copying the kernel .
*/
testq %r8, %r8
jz .Lsme_off
wbinvd
.Lsme_off:
call swap_pages
/*
* To be certain of avoiding problems with self - modifying code
* I need to execute a serializing instruction here .
* So I flush the TLB by reloading % cr3 here , it ' s handy ,
* and not processor dependent .
*/
movq %cr3, %rax
movq %rax, %cr3
testq %r11, %r11 /* preserve_context */
jnz .Lrelocate
/*
* set all of the registers to known values
* leave % rsp alone
*/
xorl %eax, %eax
xorl %ebx, %ebx
xorl %ecx, %ecx
xorl %edx, %edx
xorl %esi, %esi
xorl %edi, %edi
xorl %ebp, %ebp
xorl %r8d, %r8d
xorl %r9d, %r9d
xorl %r10d, %r10d
xorl %r11d, %r11d
xorl %r12d, %r12d
xorl %r13d, %r13d
xorl %r14d, %r14d
xorl %r15d, %r15d
ANNOTATE_UNRET_SAFE
ret
int3
.Lrelocate:
popq %rdx
/* Use the swap page for the callee's stack */
movq kexec_pa_swap_page(%rip), %r10
leaq PAGE_SIZE(%r10), %rsp
/* push the existing entry point onto the callee's stack */
pushq %rdx
ANNOTATE_RETPOLINE_SAFE
call *%rdx
/* get the re-entry point of the peer system */
popq %rbp
movq kexec_pa_swap_page(%rip), %r10
movq pa_backup_pages_map(%rip), %rdi
movq kexec_pa_table_page(%rip), %rax
movq %rax, %cr3
/* Find start (and end) of this physical mapping of control page */
leaq (%rip), %r8
ANNOTATE_NOENDBR
andq $PAGE_MASK, %r8
lea PAGE_SIZE(%r8), %rsp
movl $1 , %r11d /* Ensure preserve_context flag is set */
call swap_pages
movq kexec_va_control_page(%rip), %rax
0 : addq $virtual_mapped - 0 b, %rax
subq $__relocate_kernel_start - 0 b, %rax
pushq %rax
ANNOTATE_UNRET_SAFE
ret
int3
SYM_CODE_END(identity_mapped)
SYM_CODE_START_LOCAL_NOALIGN(virtual_mapped)
UNWIND_HINT_END_OF_STACK
ANNOTATE_NOENDBR // RET target, above
movq saved_rsp(%rip), %rsp
movq saved_cr4(%rip), %rax
movq %rax, %cr4
movq saved_cr3(%rip), %rax
movq saved_cr0(%rip), %r8
movq %rax, %cr3
movq %r8, %cr0
#ifdef CONFIG_KEXEC_JUMP
/* Saved in save_processor_state. */
movq $saved_context, %rax
lgdt saved_context_gdt_desc(%rax)
#endif
/* relocate_kernel() returns the re-entry point for next time */
movq %rbp, %rax
popf
popq %r15
popq %r14
popq %r13
popq %r12
popq %rbp
popq %rbx
ANNOTATE_UNRET_SAFE
ret
int3
SYM_CODE_END(virtual_mapped)
/* Do the copies */
SYM_CODE_START_LOCAL_NOALIGN(swap_pages)
UNWIND_HINT_END_OF_STACK
/*
* % rdi indirection page
* % r11 preserve_context
*/
movq %rdi, %rcx /* Put the indirection_page in %rcx */
xorl %edi, %edi
xorl %esi, %esi
jmp .Lstart /* Should start with an indirection record */
.Lloop: /* top, read another word for the indirection page */
movq (%rbx), %rcx
addq $8 , %rbx
.Lstart:
testb $0 x1, %cl /* is it a destination page? */
jz .Lnotdest
movq %rcx, %rdi
andq $0 xfffffffffffff000, %rdi
jmp .Lloop
.Lnotdest:
testb $0 x2, %cl /* is it an indirection page? */
jz .Lnotind
movq %rcx, %rbx
andq $0 xfffffffffffff000, %rbx
jmp .Lloop
.Lnotind:
testb $0 x4, %cl /* is it the done indicator? */
jz .Lnotdone
jmp .Ldone
.Lnotdone:
testb $0 x8, %cl /* is it the source indicator? */
jz .Lloop /* Ignore it otherwise */
movq %rcx, %rsi /* For ever source page do a copy */
andq $0 xfffffffffffff000, %rsi
movq %rdi, %rdx /* Save destination page to %rdx */
movq %rsi, %rax /* Save source page to %rax */
testq %r11, %r11 /* Only actually swap for ::preserve_context */
jz .Lnoswap
/* copy source page to swap page */
movq kexec_pa_swap_page(%rip), %rdi
movl $512 , %ecx
rep movsq
/* copy destination page to source page */
movq %rax, %rdi
movq %rdx, %rsi
movl $512 , %ecx
rep movsq
/* copy swap page to destination page */
movq %rdx, %rdi
movq kexec_pa_swap_page(%rip), %rsi
.Lnoswap:
movl $512 , %ecx
rep movsq
lea PAGE_SIZE(%rax), %rsi
jmp .Lloop
.Ldone:
ANNOTATE_UNRET_SAFE
ret
int3
SYM_CODE_END(swap_pages)
/*
* Generic ' print character ' routine
* - % al : Character to be printed ( may clobber % rax )
* - % rdx : MMIO address or port .
*/
#define XMTRDY 0 x20
#define TXR 0 /* Transmit register (WRITE) */
#define LSR 5 /* Line Status */
SYM_CODE_START_LOCAL_NOALIGN(pr_char_8250)
UNWIND_HINT_FUNC
ANNOTATE_NOENDBR
addw $LSR, %dx
xchg %al, %ah
.Lxmtrdy_loop:
inb %dx, %al
testb $XMTRDY, %al
jnz .Lready
pause
jmp .Lxmtrdy_loop
.Lready:
subw $LSR, %dx
xchg %al, %ah
outb %al, %dx
pr_char_null:
ANNOTATE_NOENDBR
ANNOTATE_UNRET_SAFE
ret
SYM_CODE_END(pr_char_8250)
SYM_CODE_START_LOCAL_NOALIGN(pr_char_8250_mmio32)
UNWIND_HINT_FUNC
ANNOTATE_NOENDBR
.Lxmtrdy_loop_mmio:
movb (LSR*4 )(%rdx), %ah
testb $XMTRDY, %ah
jnz .Lready_mmio
pause
jmp .Lxmtrdy_loop_mmio
.Lready_mmio:
movb %al, (%rdx)
ANNOTATE_UNRET_SAFE
ret
SYM_CODE_END(pr_char_8250_mmio32)
/*
* Load pr_char function pointer into % rsi and load % rdx with whatever
* that function wants to see there ( typically port / MMIO address ) .
*/
.macro pr_setup
leaq pr_char_8250(%rip), %rsi
movw kexec_debug_8250_port(%rip), %dx
testw %dx, %dx
jnz 1 f
leaq pr_char_8250_mmio32(%rip), %rsi
movq kexec_debug_8250_mmio32(%rip), %rdx
testq %rdx, %rdx
jnz 1 f
leaq pr_char_null(%rip), %rsi
1 :
.endm
/* Print the nybble in %bl, clobber %rax */
SYM_CODE_START_LOCAL_NOALIGN(pr_nybble)
UNWIND_HINT_FUNC
movb %bl, %al
nop
andb $0 x0f, %al
addb $0 x30, %al
cmpb $0 x3a, %al
jb 1 f
addb $('a' - '0' - 10 ), %al
ANNOTATE_RETPOLINE_SAFE
1 : jmp *%rsi
SYM_CODE_END(pr_nybble)
SYM_CODE_START_LOCAL_NOALIGN(pr_qword)
UNWIND_HINT_FUNC
movq $16 , %rcx
1 : rolq $4 , %rbx
call pr_nybble
loop 1 b
movb $'\n' , %al
ANNOTATE_RETPOLINE_SAFE
jmp *%rsi
SYM_CODE_END(pr_qword)
.macro print_reg a, b, c, d, r
movb $\a, %al
ANNOTATE_RETPOLINE_SAFE
call *%rsi
movb $\b, %al
ANNOTATE_RETPOLINE_SAFE
call *%rsi
movb $\c, %al
ANNOTATE_RETPOLINE_SAFE
call *%rsi
movb $\d, %al
ANNOTATE_RETPOLINE_SAFE
call *%rsi
movq \r, %rbx
call pr_qword
.endm
SYM_CODE_START_NOALIGN(kexec_debug_exc_vectors)
/* Each of these is 6 bytes. */
.macro vec_err exc
UNWIND_HINT_ENTRY
. = kexec_debug_exc_vectors + (\exc * KEXEC_DEBUG_EXC_HANDLER_SIZE)
nop
nop
pushq $\exc
jmp exc_handler
.endm
.macro vec_noerr exc
UNWIND_HINT_ENTRY
. = kexec_debug_exc_vectors + (\exc * KEXEC_DEBUG_EXC_HANDLER_SIZE)
pushq $0
pushq $\exc
jmp exc_handler
.endm
ANNOTATE_NOENDBR
vec_noerr 0 // #DE
vec_noerr 1 // #DB
vec_noerr 2 // #NMI
vec_noerr 3 // #BP
vec_noerr 4 // #OF
vec_noerr 5 // #BR
vec_noerr 6 // #UD
vec_noerr 7 // #NM
vec_err 8 // #DF
vec_noerr 9
vec_err 10 // #TS
vec_err 11 // #NP
vec_err 12 // #SS
vec_err 13 // #GP
vec_err 14 // #PF
vec_noerr 15
SYM_CODE_END(kexec_debug_exc_vectors)
SYM_CODE_START_LOCAL_NOALIGN(exc_handler)
/* No need for RET mitigations during kexec */
VALIDATE_UNRET_END
pushq %rax
pushq %rbx
pushq %rcx
pushq %rdx
pushq %rsi
/* Stack frame */
#define EXC_SS 0 x58 /* Architectural... */
#define EXC_RSP 0 x50
#define EXC_EFLAGS 0 x48
#define EXC_CS 0 x40
#define EXC_RIP 0 x38
#define EXC_ERRORCODE 0 x30 /* Either architectural or zero pushed by handler */
#define EXC_EXCEPTION 0 x28 /* Pushed by handler entry point */
#define EXC_RAX 0 x20 /* Pushed just above in exc_handler */
#define EXC_RBX 0 x18
#define EXC_RCX 0 x10
#define EXC_RDX 0 x08
#define EXC_RSI 0 x00
/* Set up %rdx/%rsi for debug output */
pr_setup
/* rip and exception info */
print_reg 'E' , 'x' , 'c' , ':' , EXC_EXCEPTION(%rsp)
print_reg 'E' , 'r' , 'r' , ':' , EXC_ERRORCODE(%rsp)
print_reg 'r' , 'i' , 'p' , ':' , EXC_RIP(%rsp)
print_reg 'r' , 's' , 'p' , ':' , EXC_RSP(%rsp)
/* We spilled these to the stack */
print_reg 'r' , 'a' , 'x' , ':' , EXC_RAX(%rsp)
print_reg 'r' , 'b' , 'x' , ':' , EXC_RBX(%rsp)
print_reg 'r' , 'c' , 'x' , ':' , EXC_RCX(%rsp)
print_reg 'r' , 'd' , 'x' , ':' , EXC_RDX(%rsp)
print_reg 'r' , 's' , 'i' , ':' , EXC_RSI(%rsp)
/* Other registers untouched */
print_reg 'r' , 'd' , 'i' , ':' , %rdi
print_reg 'r' , '8' , ' ' , ':' , %r8
print_reg 'r' , '9' , ' ' , ':' , %r9
print_reg 'r' , '1' , '0' , ':' , %r10
print_reg 'r' , '1' , '1' , ':' , %r11
print_reg 'r' , '1' , '2' , ':' , %r12
print_reg 'r' , '1' , '3' , ':' , %r13
print_reg 'r' , '1' , '4' , ':' , %r14
print_reg 'r' , '1' , '5' , ':' , %r15
print_reg 'c' , 'r' , '2' , ':' , %cr2
/* Only return from INT3 */
cmpq $3 , EXC_EXCEPTION(%rsp)
jne .Ldie
popq %rsi
popq %rdx
popq %rcx
popq %rbx
popq %rax
addq $16 , %rsp
iretq
.Ldie:
hlt
jmp .Ldie
SYM_CODE_END(exc_handler)
Messung V0.5 in Prozent C=88 H=100 G=94
¤ Dauer der Verarbeitung: 0.15 Sekunden
(vorverarbeitet am 2026-09-28)
¤
*© Formatika GbR, Deutschland