[FREELDR] Add support for loading Linux in x64 FreeLdr. Part 1/2: ASM code.
[reactos.git] / boot / freeldr / freeldr / arch / realmode / amd64.S
1
2 #include <asm.inc>
3 #include "../../include/arch/pc/x86common.h"
4
5 #define IMAGE_DOS_HEADER_e_lfanew 60
6 #define IMAGE_FILE_HEADER_SIZE 20
7 #define IMAGE_OPTIONAL_HEADER_AddressOfEntryPoint 16
8
9 .code16
10
11 /* FAT helper code */
12 #include "fathelp.inc"
13
14 .org 512
15 Startup:
16
17 cli
18
19 /* Setup real mode segment registers */
20 xor ax, ax
21 mov ds, ax
22 mov es, ax
23 mov fs, ax
24 mov gs, ax
25 mov ss, ax
26
27 /* Save the boot drive and partition */
28 mov byte ptr ds:[BSS_BootDrive], dl
29 mov byte ptr ds:[BSS_BootPartition], dh
30
31 /* Setup a real mode stack */
32 mov sp, word ptr ds:[stack16]
33
34 /* Output first status */
35 mov si, offset Msg_Starting
36 call writestr
37
38 /* Enable A20 address line */
39 call EnableA20
40
41 /* Check the CPU */
42 call CheckFor64BitSupport
43 test al, al
44 jnz .LongModeSupported
45
46 /* Output failure message */
47 mov si, offset Msg_Unsupported
48 call writestr
49
50 /* Wait for a keypress */
51 int HEX(16)
52 jmp Reboot
53
54 Msg_Unsupported:
55 .ascii "This CPU is not supported.", CR, LF
56 .ascii "Press any key to reboot...", NUL
57
58 Msg_Starting:
59 .ascii "Starting FreeLoader...", CR, LF, NUL
60
61 Msg_LongModeSupported:
62 .ascii "Long mode support detected.", CR, LF, NUL
63
64 .LongModeSupported:
65 /* Output status */
66 mov si, offset Msg_LongModeSupported
67 call writestr
68
69 /* Load the GDT */
70 lgdt fword ptr [gdtptr]
71
72 /* Build the startup page tables */
73 call BuildPageTables
74
75 /* Store real mode entry point in shared memory */
76 mov dword ptr ds:[BSS_RealModeEntry], offset RealModeEntryPoint
77
78 /* Address the image with es segment */
79 mov ax, FREELDR_PE_BASE / 16
80 mov es, ax
81
82 /* Get address of optional header */
83 mov eax, dword ptr es:[IMAGE_DOS_HEADER_e_lfanew]
84 add eax, 4 + IMAGE_FILE_HEADER_SIZE
85
86 /* Get address of entry point */
87 mov eax, dword ptr es:[eax + IMAGE_OPTIONAL_HEADER_AddressOfEntryPoint]
88 add eax, FREELDR_PE_BASE
89
90 /* Save entry point */
91 mov dword ptr ds:[LongModeEntryPoint], eax
92
93 /* Restore es */
94 xor ax, ax
95 mov es, ax
96
97 /* Output status */
98 mov si, offset Msg_SwitchToLongMode
99 call writestr
100
101 jmp ExitToLongMode
102
103 Msg_SwitchToLongMode:
104 .ascii "Switching to long mode....", CR, LF, NUL
105
106 .align 8
107 gdt:
108 .word HEX(0000), HEX(0000), HEX(0000), HEX(0000) /* 00: NULL descriptor */
109 .word HEX(0000), HEX(0000), HEX(0000), HEX(0000) /* 08: */
110 .word HEX(0000), HEX(0000), HEX(9800), HEX(0020) /* 10: long mode CS */
111 .word HEX(FFFF), HEX(0000), HEX(F300), HEX(00CF) /* 18: long mode DS */
112 .word HEX(FFFF), HEX(0000), HEX(9E00), HEX(0000) /* 20: 16-bit real mode CS */
113 .word HEX(FFFF), HEX(0000), HEX(9200), HEX(0000) /* 28: 16-bit real mode DS */
114 .word HEX(FFFF), HEX(0000), HEX(9B00), HEX(00CF) /* 30: compat mode CS */
115
116 /* GDT table pointer */
117 gdtptr:
118 .word HEX(37) /* Limit */
119 .long offset gdt /* Base Address */
120
121
122 CheckFor64BitSupport:
123 /* Check whether the CPU supports CPUID */
124 pushad
125 pushfd
126 pop eax
127 mov ebx, eax
128 xor eax, HEX(00200000)
129 push eax
130 popfd
131 pushfd
132 pop eax
133 cmp eax, ebx
134 jnz .CheckForPAE
135
136 mov si, offset .Msg_NoCpuidSupport
137 call writestr
138 popad
139 xor al, al
140 ret
141
142 .Msg_NoCpuidSupport:
143 .ascii "The system doesn't support CPUID.", CR, LF, NUL
144
145 .CheckForPAE:
146 /* CPUID support detected - getting the PAE/PGE */
147 mov eax, 1 // Fn0000_0001 - PAE in EDX[6]
148 cpuid
149 and edx, HEX(00A0)
150 cmp edx, HEX(00A0)
151 je .CheckForLongMode
152
153 mov si, offset .Msg_NoPAE
154 call writestr
155 popad
156 xor al, al
157 ret
158
159 .Msg_NoPAE:
160 .ascii "PAE or PGE not set.", CR, LF, NUL
161
162 .CheckForLongMode:
163 xor edx, edx
164 mov eax, HEX(80000001)
165 cpuid
166 and edx, HEX(20000000)
167 test edx, edx
168 jnz .Success
169
170 mov si, offset .Msg_NoLongMode
171 call writestr
172 popad
173 xor al, al
174 ret
175
176 .Msg_NoLongMode:
177 .ascii "Long mode is not supported.", CR, LF, NUL
178
179 .Success:
180 popad
181 xor al, al
182 inc al
183 ret
184
185
186 BuildPageTables:
187 pusha
188 push es
189
190 /* Get segment of the PML4 */
191 mov eax, PML4_ADDRESS / 16
192 mov es, ax
193 cld
194 xor di, di
195
196 /* One entry in the PML4 pointing to PDP */
197 mov eax, PDP_ADDRESS
198 or eax, HEX(0F)
199 stosd
200
201 /* clear rest */
202 xor eax, eax
203 mov cx, 1023
204 rep stosd
205
206 /* One entry in the PDP pointing to PD */
207 mov eax, PD_ADDRESS
208 or eax, HEX(0F)
209 stosd
210
211 /* clear rest */
212 xor eax, eax
213 mov ecx, 1023
214 rep stosd
215
216 /* 512 entries in the PD, each defining a 2MB page each */
217 mov ecx, 512
218 mov eax, HEX(008f)
219
220 .Bpt2:
221 mov es:[di], eax
222 mov dword ptr es:[di + 4], 0
223 add eax, 512 * 4096 // add 512 4k pages
224 add di, 8
225
226 /* Loop all PDEs */
227 dec cx
228 jnz .Bpt2
229
230 /* Return */
231 pop es
232 popa
233 ret
234
235
236 /******************************************************************************/
237
238 #define MSR_EFER HEX(C0000080)
239 #define LMODE_CS HEX(10)
240
241 /* This is the entry point from long mode */
242 RealModeEntryPoint:
243 /* Disable Protected Mode */
244 mov eax, cr0
245 and eax, HEX(0FFFFFFFE) // ~0x00000001
246 mov cr0, eax
247
248 /* Clear prefetch queue & correct CS */
249 ljmp16 0, offset InRealMode
250
251 InRealMode:
252
253 // mov ax, HEX(0b800)
254 // mov es, ax
255 // mov word ptr es:[12], HEX(0e00) + '6'
256
257 /* Set real mode segments */
258 xor ax, ax
259 mov ds, ax
260 mov es, ax
261 mov fs, ax
262 mov gs, ax
263 mov ss, ax
264
265 /* Clear out the high 16-bits of ESP */
266 /* This is needed because I have one */
267 /* machine that hangs when booted to dos if */
268 /* anything other than 0x0000 is in the high */
269 /* 16-bits of ESP. Even though real-mode */
270 /* code should only use SP and not ESP. */
271 xor esp, esp
272
273 /* Restore real mode stack */
274 mov sp, word ptr ds:[stack16]
275
276 // sti /* These are ok now */
277
278 /* Do the callback, specified by bx */
279 shl bx, 1
280 call word ptr ds:CallbackTable[bx]
281
282 ExitToLongMode:
283 /* Disable interrupts */
284 cli
285
286 /* Set correct segment registers */
287 xor ax,ax
288 mov ds,ax
289 mov es,ax
290 mov fs,ax
291 mov gs,ax
292 mov ss,ax
293
294 /* Save current stack pointer */
295 mov word ptr ds:[stack16], sp
296
297 /* Set PAE and PGE: 10100000b */
298 mov eax, HEX(00A0)
299 mov cr4, eax
300
301 /* Point cr3 at the PML4 */
302 mov eax, PML4_ADDRESS
303 mov cr3, eax
304
305 /* Enable long mode */
306 mov ecx, MSR_EFER
307 rdmsr
308 or eax, HEX(00000100)
309 wrmsr
310
311 /* Activate long mode by enabling paging and protection simultaneously,
312 skipping protected mode entirely */
313 mov eax, cr0
314 or eax, HEX(80000001)
315 mov cr0, eax
316
317 /* Clear prefetch queue & correct CS */
318 ljmp16 LMODE_CS, InLongMode
319 InLongMode:
320 //DB 66h, 0B8h, 18h, 00h // mov ax, LMODE_DS
321 //DB 66h, 8Eh, 0D8h // mov ds, ax
322 //DB 66h, 66h, 0C7h, 04h, 25h, 00h, 80h, 0Bh, 00h, 31h, 0Eh
323 //mov word ptr [HEX(b8000)], HEX(0e00) + '1'
324
325 .byte HEX(0FF), HEX(25) // opcode of 64bit indirect jump
326 .long 1 // relative address of LongModeEntryPoint
327 nop
328 LongModeEntryPoint:
329 .long 0, 0
330
331 int HEX(16)
332 jmp Reboot
333
334 CallbackTable:
335 .word Int386
336 .word Reboot
337 .word Relocator16Boot
338 .word PxeCallApi
339 .word PnpBiosGetDeviceNodeCount
340 .word PnpBiosGetDeviceNode
341
342 /* 16-bit stack pointer */
343 stack16:
344 .word STACK16ADDR
345
346
347 #include "int386.inc"
348 #include "helpers.inc"
349 #include "pxe.inc"
350 #include "pnp.inc"
351
352 .org (FREELDR_PE_BASE - FREELDR_BASE - 1)
353 .byte 0
354 .endcode16
355
356 END