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