162306a36Sopenharmony_ci/* SPDX-License-Identifier: GPL-2.0 */
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci *
462306a36Sopenharmony_ci *	Trampoline.S	Derived from Setup.S by Linus Torvalds
562306a36Sopenharmony_ci *
662306a36Sopenharmony_ci *	4 Jan 1997 Michael Chastain: changed to gnu as.
762306a36Sopenharmony_ci *	15 Sept 2005 Eric Biederman: 64bit PIC support
862306a36Sopenharmony_ci *
962306a36Sopenharmony_ci *	Entry: CS:IP point to the start of our code, we are
1062306a36Sopenharmony_ci *	in real mode with no stack, but the rest of the
1162306a36Sopenharmony_ci *	trampoline page to make our stack and everything else
1262306a36Sopenharmony_ci *	is a mystery.
1362306a36Sopenharmony_ci *
1462306a36Sopenharmony_ci *	On entry to trampoline_start, the processor is in real mode
1562306a36Sopenharmony_ci *	with 16-bit addressing and 16-bit data.  CS has some value
1662306a36Sopenharmony_ci *	and IP is zero.  Thus, data addresses need to be absolute
1762306a36Sopenharmony_ci *	(no relocation) and are taken with regard to r_base.
1862306a36Sopenharmony_ci *
1962306a36Sopenharmony_ci *	With the addition of trampoline_level4_pgt this code can
2062306a36Sopenharmony_ci *	now enter a 64bit kernel that lives at arbitrary 64bit
2162306a36Sopenharmony_ci *	physical addresses.
2262306a36Sopenharmony_ci *
2362306a36Sopenharmony_ci *	If you work on this file, check the object module with objdump
2462306a36Sopenharmony_ci *	--full-contents --reloc to make sure there are no relocation
2562306a36Sopenharmony_ci *	entries.
2662306a36Sopenharmony_ci */
2762306a36Sopenharmony_ci
2862306a36Sopenharmony_ci#include <linux/linkage.h>
2962306a36Sopenharmony_ci#include <asm/pgtable_types.h>
3062306a36Sopenharmony_ci#include <asm/page_types.h>
3162306a36Sopenharmony_ci#include <asm/msr.h>
3262306a36Sopenharmony_ci#include <asm/segment.h>
3362306a36Sopenharmony_ci#include <asm/processor-flags.h>
3462306a36Sopenharmony_ci#include <asm/realmode.h>
3562306a36Sopenharmony_ci#include "realmode.h"
3662306a36Sopenharmony_ci
3762306a36Sopenharmony_ci	.text
3862306a36Sopenharmony_ci	.code16
3962306a36Sopenharmony_ci
4062306a36Sopenharmony_ci.macro LOCK_AND_LOAD_REALMODE_ESP lock_pa=0
4162306a36Sopenharmony_ci	/*
4262306a36Sopenharmony_ci	 * Make sure only one CPU fiddles with the realmode stack
4362306a36Sopenharmony_ci	 */
4462306a36Sopenharmony_ci.Llock_rm\@:
4562306a36Sopenharmony_ci	.if \lock_pa
4662306a36Sopenharmony_ci        lock btsl       $0, pa_tr_lock
4762306a36Sopenharmony_ci	.else
4862306a36Sopenharmony_ci        lock btsl       $0, tr_lock
4962306a36Sopenharmony_ci	.endif
5062306a36Sopenharmony_ci        jnc             2f
5162306a36Sopenharmony_ci        pause
5262306a36Sopenharmony_ci        jmp             .Llock_rm\@
5362306a36Sopenharmony_ci2:
5462306a36Sopenharmony_ci	# Setup stack
5562306a36Sopenharmony_ci	movl	$rm_stack_end, %esp
5662306a36Sopenharmony_ci.endm
5762306a36Sopenharmony_ci
5862306a36Sopenharmony_ci	.balign	PAGE_SIZE
5962306a36Sopenharmony_ciSYM_CODE_START(trampoline_start)
6062306a36Sopenharmony_ci	cli			# We should be safe anyway
6162306a36Sopenharmony_ci	wbinvd
6262306a36Sopenharmony_ci
6362306a36Sopenharmony_ci	LJMPW_RM(1f)
6462306a36Sopenharmony_ci1:
6562306a36Sopenharmony_ci	mov	%cs, %ax	# Code and data in the same place
6662306a36Sopenharmony_ci	mov	%ax, %ds
6762306a36Sopenharmony_ci	mov	%ax, %es
6862306a36Sopenharmony_ci	mov	%ax, %ss
6962306a36Sopenharmony_ci
7062306a36Sopenharmony_ci	LOCK_AND_LOAD_REALMODE_ESP
7162306a36Sopenharmony_ci
7262306a36Sopenharmony_ci	call	verify_cpu		# Verify the cpu supports long mode
7362306a36Sopenharmony_ci	testl   %eax, %eax		# Check for return code
7462306a36Sopenharmony_ci	jnz	no_longmode
7562306a36Sopenharmony_ci
7662306a36Sopenharmony_ci.Lswitch_to_protected:
7762306a36Sopenharmony_ci	/*
7862306a36Sopenharmony_ci	 * GDT tables in non default location kernel can be beyond 16MB and
7962306a36Sopenharmony_ci	 * lgdt will not be able to load the address as in real mode default
8062306a36Sopenharmony_ci	 * operand size is 16bit. Use lgdtl instead to force operand size
8162306a36Sopenharmony_ci	 * to 32 bit.
8262306a36Sopenharmony_ci	 */
8362306a36Sopenharmony_ci
8462306a36Sopenharmony_ci	lidtl	tr_idt	# load idt with 0, 0
8562306a36Sopenharmony_ci	lgdtl	tr_gdt	# load gdt with whatever is appropriate
8662306a36Sopenharmony_ci
8762306a36Sopenharmony_ci	movw	$__KERNEL_DS, %dx	# Data segment descriptor
8862306a36Sopenharmony_ci
8962306a36Sopenharmony_ci	# Enable protected mode
9062306a36Sopenharmony_ci	movl	$(CR0_STATE & ~X86_CR0_PG), %eax
9162306a36Sopenharmony_ci	movl	%eax, %cr0		# into protected mode
9262306a36Sopenharmony_ci
9362306a36Sopenharmony_ci	# flush prefetch and jump to startup_32
9462306a36Sopenharmony_ci	ljmpl	$__KERNEL32_CS, $pa_startup_32
9562306a36Sopenharmony_ci
9662306a36Sopenharmony_cino_longmode:
9762306a36Sopenharmony_ci	hlt
9862306a36Sopenharmony_ci	jmp no_longmode
9962306a36Sopenharmony_ciSYM_CODE_END(trampoline_start)
10062306a36Sopenharmony_ci
10162306a36Sopenharmony_ci#ifdef CONFIG_AMD_MEM_ENCRYPT
10262306a36Sopenharmony_ci/* SEV-ES supports non-zero IP for entry points - no alignment needed */
10362306a36Sopenharmony_ciSYM_CODE_START(sev_es_trampoline_start)
10462306a36Sopenharmony_ci	cli			# We should be safe anyway
10562306a36Sopenharmony_ci
10662306a36Sopenharmony_ci	LJMPW_RM(1f)
10762306a36Sopenharmony_ci1:
10862306a36Sopenharmony_ci	mov	%cs, %ax	# Code and data in the same place
10962306a36Sopenharmony_ci	mov	%ax, %ds
11062306a36Sopenharmony_ci	mov	%ax, %es
11162306a36Sopenharmony_ci	mov	%ax, %ss
11262306a36Sopenharmony_ci
11362306a36Sopenharmony_ci	LOCK_AND_LOAD_REALMODE_ESP
11462306a36Sopenharmony_ci
11562306a36Sopenharmony_ci	jmp	.Lswitch_to_protected
11662306a36Sopenharmony_ciSYM_CODE_END(sev_es_trampoline_start)
11762306a36Sopenharmony_ci#endif	/* CONFIG_AMD_MEM_ENCRYPT */
11862306a36Sopenharmony_ci
11962306a36Sopenharmony_ci#include "../kernel/verify_cpu.S"
12062306a36Sopenharmony_ci
12162306a36Sopenharmony_ci	.section ".text32","ax"
12262306a36Sopenharmony_ci	.code32
12362306a36Sopenharmony_ci	.balign 4
12462306a36Sopenharmony_ciSYM_CODE_START(startup_32)
12562306a36Sopenharmony_ci	movl	%edx, %ss
12662306a36Sopenharmony_ci	addl	$pa_real_mode_base, %esp
12762306a36Sopenharmony_ci	movl	%edx, %ds
12862306a36Sopenharmony_ci	movl	%edx, %es
12962306a36Sopenharmony_ci	movl	%edx, %fs
13062306a36Sopenharmony_ci	movl	%edx, %gs
13162306a36Sopenharmony_ci
13262306a36Sopenharmony_ci	/*
13362306a36Sopenharmony_ci	 * Check for memory encryption support. This is a safety net in
13462306a36Sopenharmony_ci	 * case BIOS hasn't done the necessary step of setting the bit in
13562306a36Sopenharmony_ci	 * the MSR for this AP. If SME is active and we've gotten this far
13662306a36Sopenharmony_ci	 * then it is safe for us to set the MSR bit and continue. If we
13762306a36Sopenharmony_ci	 * don't we'll eventually crash trying to execute encrypted
13862306a36Sopenharmony_ci	 * instructions.
13962306a36Sopenharmony_ci	 */
14062306a36Sopenharmony_ci	btl	$TH_FLAGS_SME_ACTIVE_BIT, pa_tr_flags
14162306a36Sopenharmony_ci	jnc	.Ldone
14262306a36Sopenharmony_ci	movl	$MSR_AMD64_SYSCFG, %ecx
14362306a36Sopenharmony_ci	rdmsr
14462306a36Sopenharmony_ci	bts	$MSR_AMD64_SYSCFG_MEM_ENCRYPT_BIT, %eax
14562306a36Sopenharmony_ci	jc	.Ldone
14662306a36Sopenharmony_ci
14762306a36Sopenharmony_ci	/*
14862306a36Sopenharmony_ci	 * Memory encryption is enabled but the SME enable bit for this
14962306a36Sopenharmony_ci	 * CPU has has not been set.  It is safe to set it, so do so.
15062306a36Sopenharmony_ci	 */
15162306a36Sopenharmony_ci	wrmsr
15262306a36Sopenharmony_ci.Ldone:
15362306a36Sopenharmony_ci
15462306a36Sopenharmony_ci	movl	pa_tr_cr4, %eax
15562306a36Sopenharmony_ci	movl	%eax, %cr4		# Enable PAE mode
15662306a36Sopenharmony_ci
15762306a36Sopenharmony_ci	# Setup trampoline 4 level pagetables
15862306a36Sopenharmony_ci	movl	$pa_trampoline_pgd, %eax
15962306a36Sopenharmony_ci	movl	%eax, %cr3
16062306a36Sopenharmony_ci
16162306a36Sopenharmony_ci	# Set up EFER
16262306a36Sopenharmony_ci	movl	$MSR_EFER, %ecx
16362306a36Sopenharmony_ci	rdmsr
16462306a36Sopenharmony_ci	/*
16562306a36Sopenharmony_ci	 * Skip writing to EFER if the register already has desired
16662306a36Sopenharmony_ci	 * value (to avoid #VE for the TDX guest).
16762306a36Sopenharmony_ci	 */
16862306a36Sopenharmony_ci	cmp	pa_tr_efer, %eax
16962306a36Sopenharmony_ci	jne	.Lwrite_efer
17062306a36Sopenharmony_ci	cmp	pa_tr_efer + 4, %edx
17162306a36Sopenharmony_ci	je	.Ldone_efer
17262306a36Sopenharmony_ci.Lwrite_efer:
17362306a36Sopenharmony_ci	movl	pa_tr_efer, %eax
17462306a36Sopenharmony_ci	movl	pa_tr_efer + 4, %edx
17562306a36Sopenharmony_ci	wrmsr
17662306a36Sopenharmony_ci
17762306a36Sopenharmony_ci.Ldone_efer:
17862306a36Sopenharmony_ci	# Enable paging and in turn activate Long Mode.
17962306a36Sopenharmony_ci	movl	$CR0_STATE, %eax
18062306a36Sopenharmony_ci	movl	%eax, %cr0
18162306a36Sopenharmony_ci
18262306a36Sopenharmony_ci	/*
18362306a36Sopenharmony_ci	 * At this point we're in long mode but in 32bit compatibility mode
18462306a36Sopenharmony_ci	 * with EFER.LME = 1, CS.L = 0, CS.D = 1 (and in turn
18562306a36Sopenharmony_ci	 * EFER.LMA = 1). Now we want to jump in 64bit mode, to do that we use
18662306a36Sopenharmony_ci	 * the new gdt/idt that has __KERNEL_CS with CS.L = 1.
18762306a36Sopenharmony_ci	 */
18862306a36Sopenharmony_ci	ljmpl	$__KERNEL_CS, $pa_startup_64
18962306a36Sopenharmony_ciSYM_CODE_END(startup_32)
19062306a36Sopenharmony_ci
19162306a36Sopenharmony_ciSYM_CODE_START(pa_trampoline_compat)
19262306a36Sopenharmony_ci	/*
19362306a36Sopenharmony_ci	 * In compatibility mode.  Prep ESP and DX for startup_32, then disable
19462306a36Sopenharmony_ci	 * paging and complete the switch to legacy 32-bit mode.
19562306a36Sopenharmony_ci	 */
19662306a36Sopenharmony_ci	LOCK_AND_LOAD_REALMODE_ESP lock_pa=1
19762306a36Sopenharmony_ci	movw	$__KERNEL_DS, %dx
19862306a36Sopenharmony_ci
19962306a36Sopenharmony_ci	movl	$(CR0_STATE & ~X86_CR0_PG), %eax
20062306a36Sopenharmony_ci	movl	%eax, %cr0
20162306a36Sopenharmony_ci	ljmpl   $__KERNEL32_CS, $pa_startup_32
20262306a36Sopenharmony_ciSYM_CODE_END(pa_trampoline_compat)
20362306a36Sopenharmony_ci
20462306a36Sopenharmony_ci	.section ".text64","ax"
20562306a36Sopenharmony_ci	.code64
20662306a36Sopenharmony_ci	.balign 4
20762306a36Sopenharmony_ciSYM_CODE_START(startup_64)
20862306a36Sopenharmony_ci	# Now jump into the kernel using virtual addresses
20962306a36Sopenharmony_ci	jmpq	*tr_start(%rip)
21062306a36Sopenharmony_ciSYM_CODE_END(startup_64)
21162306a36Sopenharmony_ci
21262306a36Sopenharmony_ciSYM_CODE_START(trampoline_start64)
21362306a36Sopenharmony_ci	/*
21462306a36Sopenharmony_ci	 * APs start here on a direct transfer from 64-bit BIOS with identity
21562306a36Sopenharmony_ci	 * mapped page tables.  Load the kernel's GDT in order to gear down to
21662306a36Sopenharmony_ci	 * 32-bit mode (to handle 4-level vs. 5-level paging), and to (re)load
21762306a36Sopenharmony_ci	 * segment registers.  Load the zero IDT so any fault triggers a
21862306a36Sopenharmony_ci	 * shutdown instead of jumping back into BIOS.
21962306a36Sopenharmony_ci	 */
22062306a36Sopenharmony_ci	lidt	tr_idt(%rip)
22162306a36Sopenharmony_ci	lgdt	tr_gdt64(%rip)
22262306a36Sopenharmony_ci
22362306a36Sopenharmony_ci	ljmpl	*tr_compat(%rip)
22462306a36Sopenharmony_ciSYM_CODE_END(trampoline_start64)
22562306a36Sopenharmony_ci
22662306a36Sopenharmony_ci	.section ".rodata","a"
22762306a36Sopenharmony_ci	# Duplicate the global descriptor table
22862306a36Sopenharmony_ci	# so the kernel can live anywhere
22962306a36Sopenharmony_ci	.balign	16
23062306a36Sopenharmony_ciSYM_DATA_START(tr_gdt)
23162306a36Sopenharmony_ci	.short	tr_gdt_end - tr_gdt - 1	# gdt limit
23262306a36Sopenharmony_ci	.long	pa_tr_gdt
23362306a36Sopenharmony_ci	.short	0
23462306a36Sopenharmony_ci	.quad	0x00cf9b000000ffff	# __KERNEL32_CS
23562306a36Sopenharmony_ci	.quad	0x00af9b000000ffff	# __KERNEL_CS
23662306a36Sopenharmony_ci	.quad	0x00cf93000000ffff	# __KERNEL_DS
23762306a36Sopenharmony_ciSYM_DATA_END_LABEL(tr_gdt, SYM_L_LOCAL, tr_gdt_end)
23862306a36Sopenharmony_ci
23962306a36Sopenharmony_ciSYM_DATA_START(tr_gdt64)
24062306a36Sopenharmony_ci	.short	tr_gdt_end - tr_gdt - 1	# gdt limit
24162306a36Sopenharmony_ci	.long	pa_tr_gdt
24262306a36Sopenharmony_ci	.long	0
24362306a36Sopenharmony_ciSYM_DATA_END(tr_gdt64)
24462306a36Sopenharmony_ci
24562306a36Sopenharmony_ciSYM_DATA_START(tr_compat)
24662306a36Sopenharmony_ci	.long	pa_trampoline_compat
24762306a36Sopenharmony_ci	.short	__KERNEL32_CS
24862306a36Sopenharmony_ciSYM_DATA_END(tr_compat)
24962306a36Sopenharmony_ci
25062306a36Sopenharmony_ci	.bss
25162306a36Sopenharmony_ci	.balign	PAGE_SIZE
25262306a36Sopenharmony_ciSYM_DATA(trampoline_pgd, .space PAGE_SIZE)
25362306a36Sopenharmony_ci
25462306a36Sopenharmony_ci	.balign	8
25562306a36Sopenharmony_ciSYM_DATA_START(trampoline_header)
25662306a36Sopenharmony_ci	SYM_DATA_LOCAL(tr_start,	.space 8)
25762306a36Sopenharmony_ci	SYM_DATA(tr_efer,		.space 8)
25862306a36Sopenharmony_ci	SYM_DATA(tr_cr4,		.space 4)
25962306a36Sopenharmony_ci	SYM_DATA(tr_flags,		.space 4)
26062306a36Sopenharmony_ci	SYM_DATA(tr_lock,		.space 4)
26162306a36Sopenharmony_ciSYM_DATA_END(trampoline_header)
26262306a36Sopenharmony_ci
26362306a36Sopenharmony_ci#include "trampoline_common.S"
264