/*
 * Common bootstrapping code to transition from 16-bit to 32-bit code, and to
 * transition from 32-bit to 64-bit code (x86-64 only)
 */
#include "apic-defs.h"

per_cpu_size = PER_CPU_SIZE

 /* EFI provides it's own SIPI sequence to handle relocation. */
#ifndef CONFIG_EFI
.code16
.globl rm_trampoline
rm_trampoline:

/* Store SIPI vector code at the beginning of trampoline. */
sipi_entry:
	mov %cr0, %eax
	or $1, %eax
	mov %eax, %cr0
	lgdtl ap_rm_gdt_descr - sipi_entry
	ljmpl $8, $ap_start32
sipi_end:

.globl ap_rm_gdt_descr
ap_rm_gdt_descr:
#ifdef __i386__
	.word 0
	.long 0
#else
	.word gdt32_end - gdt32 - 1
	.long gdt32
#endif

.globl rm_trampoline_end
rm_trampoline_end:
#endif

/* The 32-bit => 64-bit trampoline is x86-64 only. */
#ifdef __x86_64__
.code32

/*
 * EFI builds with "-shared -fPIC" and so cannot directly reference any absolute
 * address.  In 64-bit mode, RIP-relative addressing neatly solves the problem,
 * but 32-bit code doesn't have that luxury.  Make a dummy CALL to get RIP into
 * a GPR in order to emulate RIP-relative for 32-bit transition code.
 */
.macro load_absolute_addr, addr, reg
#ifdef CONFIG_EFI
	call 1f
1:
	pop \reg
	add \addr - 1b, \reg
#else
	mov \addr, \reg
#endif
.endm

MSR_GS_BASE = 0xc0000101

.macro setup_percpu_area
	lea -per_cpu_size(%esp), %eax
	mov $0, %edx
	mov $MSR_GS_BASE, %ecx
	wrmsr
.endm

.macro setup_segments
	mov $MSR_GS_BASE, %ecx
	rdmsr

	mov $0x10, %bx
	mov %bx, %ds
	mov %bx, %es
	mov %bx, %fs
	mov %bx, %gs
	mov %bx, %ss

	/* restore MSR_GS_BASE */
	wrmsr
.endm

prepare_64:
	load_absolute_addr $gdt_descr, %edx
	lgdtl (%edx)

	setup_segments

	xor %eax, %eax
	mov %eax, %cr4

enter_long_mode:
	mov %cr4, %eax
	bts $5, %eax  // pae
	mov %eax, %cr4

	/* Note, EFI doesn't yet support 5-level paging. */
#ifdef CONFIG_EFI
	load_absolute_addr $ptl4, %eax
#else
	mov pt_root, %eax
#endif
	mov %eax, %cr3

efer = 0xc0000080
	mov $efer, %ecx
	rdmsr
	bts $8, %eax
	wrmsr

	mov %cr0, %eax
	bts $0, %eax
	bts $31, %eax
	mov %eax, %cr0
	ret

.globl ap_start32
ap_start32:
	setup_segments

	load_absolute_addr $smp_stacktop, %edx
	mov $-per_cpu_size, %esp
	lock xaddl %esp, (%edx)

	setup_percpu_area
	call prepare_64

	load_absolute_addr $ap_start64, %edx
	pushl $0x08
	pushl %edx
	lretl
#endif
