xref: /kvm-unit-tests/x86/trampolines.S (revision dca3f4c041143c8e8dc70c6890a19a5730310230)
1/*
2 * Common bootstrapping code to transition from 16-bit to 32-bit code, and to
3 * transition from 32-bit to 64-bit code (x86-64 only)
4 */
5#include "apic-defs.h"
6#include "smp.h"
7
8per_cpu_size = PER_CPU_SIZE
9
10#include "desc.h"
11
12 /* EFI provides it's own SIPI sequence to handle relocation. */
13#ifndef CONFIG_EFI
14.code16
15.globl rm_trampoline
16rm_trampoline:
17
18/* Store SIPI vector code at the beginning of trampoline. */
19sipi_entry:
20	mov %cr0, %eax
21	or $1, %eax
22	mov %eax, %cr0
23	lgdtl ap_rm_gdt_descr - sipi_entry
24	ljmpl $KERNEL_CS, $ap_start32
25sipi_end:
26
27.globl ap_rm_gdt_descr
28ap_rm_gdt_descr:
29#ifdef __i386__
30	.word 0
31	.long 0
32#else
33	.word gdt32_end - gdt32 - 1
34	.long gdt32
35#endif
36
37.globl rm_trampoline_end
38rm_trampoline_end:
39#endif
40
41/* The 32-bit => 64-bit trampoline is x86-64 only. */
42#ifdef __x86_64__
43.code32
44
45/*
46 * EFI builds with "-shared -fPIC" and so cannot directly reference any absolute
47 * address.  In 64-bit mode, RIP-relative addressing neatly solves the problem,
48 * but 32-bit code doesn't have that luxury.  Make a dummy CALL to get RIP into
49 * a GPR in order to emulate RIP-relative for 32-bit transition code.
50 */
51.macro load_absolute_addr, addr, reg
52#ifdef CONFIG_EFI
53	call 1f
541:
55	pop \reg
56	add \addr - 1b, \reg
57#else
58	mov \addr, \reg
59#endif
60.endm
61
62MSR_GS_BASE = 0xc0000101
63
64.macro setup_percpu_area
65	lea -per_cpu_size(%esp), %eax
66	mov $0, %edx
67	mov $MSR_GS_BASE, %ecx
68	wrmsr
69.endm
70
71.macro setup_segments
72	mov $MSR_GS_BASE, %ecx
73	rdmsr
74
75	mov $KERNEL_DS, %bx
76	mov %bx, %ds
77	mov %bx, %es
78	mov %bx, %fs
79	mov %bx, %gs
80	mov %bx, %ss
81
82	/* restore MSR_GS_BASE */
83	wrmsr
84.endm
85
86prepare_64:
87	load_absolute_addr $gdt_descr, %edx
88	lgdtl (%edx)
89
90	setup_segments
91
92	xor %eax, %eax
93	mov %eax, %cr4
94
95enter_long_mode:
96	mov %cr4, %eax
97	bts $5, %eax  // pae
98	mov %eax, %cr4
99
100	/* Note, EFI doesn't yet support 5-level paging. */
101#ifdef CONFIG_EFI
102	load_absolute_addr $ptl4, %eax
103#else
104	mov pt_root, %eax
105#endif
106	mov %eax, %cr3
107
108efer = 0xc0000080
109	mov $efer, %ecx
110	rdmsr
111	bts $8, %eax
112	wrmsr
113
114	mov %cr0, %eax
115	bts $0, %eax
116	bts $31, %eax
117	mov %eax, %cr0
118	ret
119
120.globl ap_start32
121ap_start32:
122	setup_segments
123
124	load_absolute_addr $smp_stacktop, %edx
125	mov $-per_cpu_size, %esp
126	lock xaddl %esp, (%edx)
127
128	setup_percpu_area
129	call prepare_64
130
131	load_absolute_addr $ap_start64, %edx
132	pushl $KERNEL_CS
133	pushl %edx
134	lretl
135#endif
136