/* SPDX-License-Identifier: GPL-2.0 */
/*
* linux / boot / head . S
*
* Copyright ( C ) 1991 , 1992 , 1993 Linus Torvalds
*/
/*
* head . S contains the 32 - bit startup code .
*
* NOTE !!! Startup happens at absolute address 0x00001000, which is also where
* the page directory will exist . The startup code will be overwritten by
* the page directory . [ According to comments etc elsewhere on a compressed
* kernel it will end up at 0 x1000 + 1 Mb I hope so as I assume this . - AC ]
*
* Page 0 is deliberately kept safe , since System Management Mode code in
* laptops may need to access the BIOS data stored there . This is also
* useful for future device drivers that either access the BIOS via VM86
* mode .
*/
/*
* High loaded stuff by Hans Lermen & Werner Almesberger , Feb . 1996
*/
.code32
.text
#include <linux/init.h>
#include <linux/linkage.h>
#include <asm/segment.h>
#include <asm/boot.h>
#include <asm/msr.h>
#include <asm/processor-flags.h>
#include <asm/asm-offsets.h>
#include <asm/bootparam.h>
#include <asm/desc_defs.h>
#include <asm/trapnr.h>
/*
* Fix alignment at 16 bytes . Following CONFIG_FUNCTION_ALIGNMENT will result
* in assembly errors due to trying to move . org backward due to the excessive
* alignment .
*/
#undef __ALIGN
#define __ALIGN .balign 16 , 0 x90
/*
* Locally defined symbols should be marked hidden :
*/
.hidden _bss
.hidden _ebss
.hidden _end
__HEAD
/*
* This macro gives the relative virtual address of X , i . e . the offset of X
* from startup_32 . This is the same as the link - time virtual address of X ,
* since startup_32 is at 0 , but defining it this way tells the
* assembler / linker that we do not want the actual run - time address of X . This
* prevents the linker from trying to create unwanted run - time relocation
* entries for the reference when the compressed kernel is linked as PIE .
*
* A reference X ( % reg ) will result in the link - time VA of X being stored with
* the instruction , and a run - time R_X86_64_RELATIVE relocation entry that
* adds the 64 - bit base address where the kernel is loaded .
*
* Replacing it with ( X - startup_32 ) ( % reg ) results in the offset being stored ,
* and no run - time relocation .
*
* The macro should be used as a displacement with a base register containing
* the run - time address of startup_32 [ i . e . rva ( X ) ( % reg ) ] , or as an immediate
* [ $ rva ( X ) ] .
*
* This macro can only be used from within the . head . text section , since the
* expression requires startup_32 to be in the same section as the code being
* assembled .
*/
#define rva(X) ((X) - startup_32)
.code32
SYM_FUNC_START(startup_32)
/*
* 32 bit entry is 0 and it is ABI so immutable !
* If we come here directly from a bootloader ,
* kernel ( text + data + bss + brk ) ramdisk , zero_page , command line
* all need to be under the 4 G limit .
*/
cld
cli
/*
* Calculate the delta between where we were compiled to run
* at and where we were actually loaded at . This can only be done
* with a short local call on x86 . Nothing else will tell us what
* address we are running at . The reserved chunk of the real - mode
* data at 0 x1e4 ( defined as a scratch field ) are used as the stack
* for this calculation . Only 4 bytes are needed .
*/
leal (BP_scratch+4 )(%esi), %esp
call 1 f
1 : popl %ebp
subl $ rva(1 b), %ebp
/* Load new GDT with the 64bit segments using 32bit descriptor */
leal rva(gdt)(%ebp), %eax
movl %eax, 2 (%eax)
lgdt (%eax)
/* Load segment registers with our descriptors */
movl $__BOOT_DS, %eax
movl %eax, %ds
movl %eax, %es
movl %eax, %fs
movl %eax, %gs
movl %eax, %ss
/* Setup a stack and load CS from current GDT */
leal rva(boot_stack_end)(%ebp), %esp
pushl $__KERNEL32_CS
leal rva(1 f)(%ebp), %eax
pushl %eax
lretl
1 :
/* Setup Exception handling for SEV-ES */
#ifdef CONFIG_AMD_MEM_ENCRYPT
call startup32_load_idt
#endif
/* Make sure cpu supports long mode. */
call verify_cpu
testl %eax, %eax
jnz .Lno_longmode
/*
* Compute the delta between where we were compiled to run at
* and where the code will actually run at .
*
* % ebp contains the address we are loaded at by the boot loader and % ebx
* contains the address where we should move the kernel image temporarily
* for safe in - place decompression .
*/
#ifdef CONFIG_RELOCATABLE
movl %ebp, %ebx
movl BP_kernel_alignment(%esi), %eax
decl %eax
addl %eax, %ebx
notl %eax
andl %eax, %ebx
cmpl $LOAD_PHYSICAL_ADDR, %ebx
jae 1 f
#endif
movl $LOAD_PHYSICAL_ADDR, %ebx
1 :
/* Target address to relocate to for decompression */
addl BP_init_size(%esi), %ebx
subl $ rva(_end), %ebx
/*
* Prepare for entering 64 bit mode
*/
/* Enable PAE mode */
movl %cr4, %eax
orl $X86_CR4_PAE, %eax
movl %eax, %cr4
/*
* Build early 4 G boot pagetable
*/
/*
* If SEV is active then set the encryption mask in the page tables .
* This will ensure that when the kernel is copied and decompressed
* it will be done so encrypted .
*/
xorl %edx, %edx
#ifdef CONFIG_AMD_MEM_ENCRYPT
call get_sev_encryption_bit
xorl %edx, %edx
testl %eax, %eax
jz 1 f
subl $32 , %eax /* Encryption bit is always above bit 31 */
bts %eax, %edx /* Set encryption mask for page tables */
/*
* Set MSR_AMD64_SEV_ENABLED_BIT in sev_status so that
* startup32_check_sev_cbit ( ) will do a check . sev_enable ( ) will
* initialize sev_status with all the bits reported by
* MSR_AMD_SEV_STATUS later , but only MSR_AMD64_SEV_ENABLED_BIT
* needs to be set for now .
*/
movl $1 , rva(sev_status)(%ebp)
1 :
#endif
/* Initialize Page tables to 0 */
leal rva(pgtable)(%ebx), %edi
xorl %eax, %eax
movl $(BOOT_INIT_PGT_SIZE/4 ), %ecx
rep stosl
/* Build Level 4 */
leal rva(pgtable + 0 )(%ebx), %edi
leal 0 x1007 (%edi), %eax
movl %eax, 0 (%edi)
addl %edx, 4 (%edi)
/* Build Level 3 */
leal rva(pgtable + 0 x1000)(%ebx), %edi
leal 0 x1007(%edi), %eax
movl $4 , %ecx
1 : movl %eax, 0 x00(%edi)
addl %edx, 0 x04(%edi)
addl $0 x00001000, %eax
addl $8 , %edi
decl %ecx
jnz 1 b
/* Build Level 2 */
leal rva(pgtable + 0 x2000)(%ebx), %edi
movl $0 x00000183, %eax
movl $2048 , %ecx
1 : movl %eax, 0 (%edi)
addl %edx, 4 (%edi)
addl $0 x00200000, %eax
addl $8 , %edi
decl %ecx
jnz 1 b
/* Enable the boot page tables */
leal rva(pgtable)(%ebx), %eax
movl %eax, %cr3
/* Enable Long mode in EFER (Extended Feature Enable Register) */
movl $MSR_EFER, %ecx
rdmsr
btsl $_EFER_LME, %eax
wrmsr
/* After gdt is loaded */
xorl %eax, %eax
lldt %ax
movl $__BOOT_TSS, %eax
ltr %ax
#ifdef CONFIG_AMD_MEM_ENCRYPT
/* Check if the C-bit position is correct when SEV is active */
call startup32_check_sev_cbit
#endif
/*
* Setup for the jump to 64 bit mode
*
* When the jump is performed we will be in long mode but
* in 32 bit compatibility mode with EFER . LME = 1 , CS . L = 0 , CS . D = 1
* ( and in turn EFER . LMA = 1 ) . To jump into 64 bit mode we use
* the new gdt / idt that has _ _ KERNEL_CS with CS . L = 1 .
* We place all of the values on our mini stack so lret can
* used to perform that far jump .
*/
leal rva(startup_64)(%ebp), %eax
pushl $__KERNEL_CS
pushl %eax
/* Enter paged protected Mode, activating Long Mode */
movl $CR0_STATE, %eax
movl %eax, %cr0
/* Jump from 32bit compatibility mode into 64bit mode. */
lret
SYM_FUNC_END(startup_32)
.code64
.org 0 x200
SYM_CODE_START(startup_64)
/*
* 64 bit entry is 0 x200 and it is ABI so immutable !
* We come here either from startup_32 or directly from a
* 64 bit bootloader .
* If we come here from a bootloader , kernel ( text + data + bss + brk ) ,
* ramdisk , zero_page , command line could be above 4 G .
* We depend on an identity mapped page table being provided
* that maps our entire kernel ( text + data + bss + brk ) , zero page
* and command line .
*/
cld
cli
/* Setup data segments. */
xorl %eax, %eax
movl %eax, %ds
movl %eax, %es
movl %eax, %ss
movl %eax, %fs
movl %eax, %gs
/*
* Compute the decompressed kernel start address . It is where
* we were loaded at aligned to a 2 M boundary . % rbp contains the
* decompressed kernel start address .
*
* If it is a relocatable kernel then decompress and run the kernel
* from load address aligned to 2 MB addr , otherwise decompress and
* run the kernel from LOAD_PHYSICAL_ADDR
*
* We cannot rely on the calculation done in 32 - bit mode , since we
* may have been invoked via the 64 - bit entry point .
*/
/* Start with the delta to where the kernel will run at. */
#ifdef CONFIG_RELOCATABLE
leaq startup_32(%rip) /* - $startup_32 */, %rbp
movl BP_kernel_alignment(%rsi), %eax
decl %eax
addq %rax, %rbp
notq %rax
andq %rax, %rbp
cmpq $LOAD_PHYSICAL_ADDR, %rbp
jae 1 f
#endif
movq $LOAD_PHYSICAL_ADDR, %rbp
1 :
/* Target address to relocate to for decompression */
movl BP_init_size(%rsi), %ebx
subl $ rva(_end), %ebx
addq %rbp, %rbx
/* Set up the stack */
leaq rva(boot_stack_end)(%rbx), %rsp
/*
* At this point we are in long mode with 4 - level paging enabled ,
* but we might want to enable 5 - level paging or vice versa .
*
* The problem is that we cannot do it directly . Setting or clearing
* CR4 . LA57 in long mode would trigger # GP . So we need to switch off
* long mode and paging first .
*
* We also need a trampoline in lower memory to switch over from
* 4 - to 5 - level paging for cases when the bootloader puts the kernel
* above 4 G , but didn ' t enable 5 - level paging for us .
*
* The same trampoline can be used to switch from 5 - to 4 - level paging
* mode , like when starting 4 - level paging kernel via kexec ( ) when
* original kernel worked in 5 - level paging mode .
*
* For the trampoline , we need the top page table to reside in lower
* memory as we don ' t have a way to load 64 - bit values into CR3 in
* 32 - bit mode .
*/
/* Make sure we have GDT with 32-bit code segment */
leaq gdt64(%rip), %rax
addq %rax, 2 (%rax)
lgdt (%rax)
/* Reload CS so IRET returns to a CS actually in the GDT */
pushq $__KERNEL_CS
leaq .Lon_kernel_cs(%rip), %rax
pushq %rax
lretq
.Lon_kernel_cs:
/*
* RSI holds a pointer to a boot_params structure provided by the
* loader , and this needs to be preserved across C function calls . So
* move it into a callee saved register .
*/
movq %rsi, %r15
call load_stage1_idt
#ifdef CONFIG_AMD_MEM_ENCRYPT
/*
* Now that the stage1 interrupt handlers are set up , # VC exceptions from
* CPUID instructions can be properly handled for SEV - ES guests .
*
* For SEV - SNP , the CPUID table also needs to be set up in advance of any
* CPUID instructions being issued , so go ahead and do that now via
* sev_enable ( ) , which will also handle the rest of the SEV - related
* detection / setup to ensure that has been done in advance of any dependent
* code . Pass the boot_params pointer as the first argument .
*/
movq %r15, %rdi
call sev_enable
#endif
/* Preserve only the CR4 bits that must be preserved, and clear the rest */
movq %cr4, %rax
andl $(X86_CR4_PAE | X86_CR4_MCE | X86_CR4_LA57), %eax
movq %rax, %cr4
/*
* configure_5level_paging ( ) updates the number of paging levels using
* a trampoline in 32 - bit addressable memory if the current number does
* not match the desired number .
*
* Pass the boot_params pointer as the first argument . The second
* argument is the relocated address of the page table to use instead
* of the page table in trampoline memory ( if required ) .
*/
movq %r15, %rdi
leaq rva(top_pgtable)(%rbx), %rsi
call configure_5level_paging
/* Zero EFLAGS */
pushq $0
popfq
/*
* Copy the compressed kernel to the end of our buffer
* where decompression in place becomes safe .
*/
leaq (_bss-8 )(%rip), %rsi
leaq rva(_bss-8 )(%rbx), %rdi
movl $(_bss - startup_32), %ecx
shrl $3 , %ecx
std
rep movsq
cld
/*
* The GDT may get overwritten either during the copy we just did or
* during extract_kernel below . To avoid any issues , repoint the GDTR
* to the new copy of the GDT .
*/
leaq rva(gdt64)(%rbx), %rax
leaq rva(gdt)(%rbx), %rdx
movq %rdx, 2 (%rax)
lgdt (%rax)
/*
* Jump to the relocated address .
*/
leaq rva(.Lrelocated)(%rbx), %rax
jmp *%rax
SYM_CODE_END(startup_64)
.text
SYM_FUNC_START_LOCAL_NOALIGN(.Lrelocated)
/*
* Clear BSS ( stack is currently empty )
*/
xorl %eax, %eax
leaq _bss(%rip), %rdi
leaq _ebss(%rip), %rcx
subq %rdi, %rcx
shrq $3 , %rcx
rep stosq
call load_stage2_idt
/* Pass boot_params to initialize_identity_maps() */
movq %r15, %rdi
call initialize_identity_maps
/*
* Do the extraction , and jump to the new kernel . .
*/
/* pass struct boot_params pointer and output target address */
movq %r15, %rdi
movq %rbp, %rsi
call extract_kernel /* returns kernel entry point in %rax */
/*
* Jump to the decompressed kernel .
*/
movq %r15, %rsi
jmp *%rax
SYM_FUNC_END(.Lrelocated)
.code32
SYM_FUNC_START_LOCAL_NOALIGN(.Lno_longmode)
/* This isn't an x86-64 CPU, so hang intentionally, we cannot continue */
1 :
hlt
jmp 1 b
SYM_FUNC_END(.Lno_longmode)
.globl verify_cpu
#include "../../kernel/verify_cpu.S"
.data
SYM_DATA_START_LOCAL(gdt64)
.word gdt_end - gdt - 1
.quad gdt - gdt64
SYM_DATA_END(gdt64)
.balign 8
SYM_DATA_START_LOCAL(gdt)
.word gdt_end - gdt - 1
.long 0
.word 0
.quad 0 x00cf9a000000ffff /* __KERNEL32_CS */
.quad 0 x00af9a000000ffff /* __KERNEL_CS */
.quad 0 x00cf92000000ffff /* __KERNEL_DS */
.quad 0 x0080890000000000 /* TS descriptor */
.quad 0 x0000000000000000 /* TS continued */
SYM_DATA_END_LABEL(gdt, SYM_L_LOCAL, gdt_end)
SYM_DATA_START(boot_idt_desc)
.word boot_idt_end - boot_idt - 1
.quad 0
SYM_DATA_END(boot_idt_desc)
.balign 8
SYM_DATA_START(boot_idt)
.rept BOOT_IDT_ENTRIES
.quad 0
.quad 0
.endr
SYM_DATA_END_LABEL(boot_idt, SYM_L_GLOBAL, boot_idt_end)
/*
* Stack and heap for uncompression
*/
.bss
.balign 4
SYM_DATA_START_LOCAL(boot_stack)
.fill BOOT_STACK_SIZE, 1 , 0
.balign 16
SYM_DATA_END_LABEL(boot_stack, SYM_L_LOCAL, boot_stack_end)
/*
* Space for page tables ( not in . bss so not zeroed )
*/
.section ".pgtable" ,"aw" ,@nobits
.balign 4096
SYM_DATA_LOCAL(pgtable, .fill BOOT_PGT_SIZE, 1 , 0 )
/*
* The page table is going to be used instead of page table in the trampoline
* memory .
*/
SYM_DATA_LOCAL(top_pgtable, .fill PAGE_SIZE, 1 , 0 )
Messung V0.5 in Prozent C=89 H=75 G=82
¤ Dauer der Verarbeitung: 0.13 Sekunden
(vorverarbeitet am 2026-09-28)
¤
*© Formatika GbR, Deutschland