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
15Startup:
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
54Msg_Unsupported:
55    .ascii "This CPU is not supported.", CR, LF
56    .ascii "Press any key to reboot...", NUL
57
58Msg_Starting:
59    .ascii "Starting FreeLoader...", CR, LF, NUL
60
61Msg_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
103Msg_SwitchToLongMode:
104    .ascii "Switching to long mode....", CR, LF, NUL
105
106.align 8
107gdt:
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 */
117gdtptr:
118    .word HEX(37)       /* Limit */
119    .long offset gdt    /* Base Address */
120
121
122CheckFor64BitSupport:
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    /* Check whether extended functions are supported */
164    mov eax, HEX(80000000)
165    cpuid
166    cmp eax, HEX(80000000)  // Any function > 0x80000000 ?
167    jbe .NoLongMode         // If not, no long mode.
168    /* Check whether the CPU supports Long Mode */
169    xor edx, edx
170    mov eax, HEX(80000001)
171    cpuid
172    and edx, HEX(20000000)
173    test edx, edx
174    jnz .Success
175
176.NoLongMode:
177    mov si, offset .Msg_NoLongMode
178    call writestr
179    popad
180    xor al, al
181    ret
182
183.Msg_NoLongMode:
184    .ascii "Long mode is not supported.", CR, LF, NUL
185
186.Success:
187    popad
188    xor al, al
189    inc al
190    ret
191
192
193BuildPageTables:
194    pusha
195    push es
196
197    /* Get segment of the PML4 */
198    mov eax, PML4_ADDRESS / 16
199    mov es, ax
200    cld
201    xor di, di
202
203    /* One entry in the PML4 pointing to PDP */
204    mov eax, PDP_ADDRESS
205    or eax, HEX(0F)
206    stosd
207
208    /* clear rest */
209    xor eax, eax
210    mov cx, 1023
211    rep stosd
212
213    /* One entry in the PDP pointing to PD */
214    mov eax, PD_ADDRESS
215    or eax, HEX(0F)
216    stosd
217
218    /* clear rest */
219    xor eax, eax
220    mov ecx, 1023
221    rep stosd
222
223    /* 512 entries in the PD, each defining a 2MB page each */
224    mov ecx, 512
225    mov eax, HEX(008f)
226
227.Bpt2:
228    mov es:[di], eax
229    mov dword ptr es:[di + 4], 0
230    add eax, 512 * 4096 // add 512 4k pages
231    add di, 8
232
233    /* Loop all PDEs */
234    dec cx
235    jnz .Bpt2
236
237    /* Return */
238    pop es
239    popa
240    ret
241
242
243/******************************************************************************/
244
245#define MSR_EFER HEX(C0000080)
246#define LMODE_CS HEX(10)
247
248/* This is the entry point from long mode */
249RealModeEntryPoint:
250
251    /* Disable long mode */
252    mov ecx, MSR_EFER
253    rdmsr
254    and eax, HEX(0FFFFFEFF) // ~0100
255    wrmsr
256
257    /* Mask PAE and PGE out */
258    mov eax, cr4
259    and eax, HEX(0FFFFFF5F) // ~00A0
260    mov cr4, eax
261
262    /* Disable Protected Mode */
263    mov eax, cr0
264    and eax, HEX(0FFFFFFFE) // ~0x00000001
265    mov cr0, eax
266
267    /* Clear prefetch queue & correct CS */
268    ljmp16 0, offset InRealMode
269
270InRealMode:
271
272//    mov ax, HEX(0b800)
273//    mov es, ax
274//    mov word ptr es:[12], HEX(0e00) + '6'
275
276    /* Set real mode segments */
277    xor ax, ax
278    mov ds, ax
279    mov es, ax
280    mov fs, ax
281    mov gs, ax
282    mov ss, ax
283
284    /* Clear out the high 16-bits of ESP */
285    /* This is needed because I have one */
286    /* machine that hangs when booted to dos if */
287    /* anything other than 0x0000 is in the high */
288    /* 16-bits of ESP. Even though real-mode */
289    /* code should only use SP and not ESP. */
290    xor esp, esp
291
292    /* Restore real mode stack */
293    mov sp, word ptr ds:[stack16]
294
295    // sti /* These are ok now */
296
297    /* Do the callback, specified by bx */
298    shl bx, 1
299    call word ptr ds:CallbackTable[bx]
300
301ExitToLongMode:
302    /* Disable interrupts */
303    cli
304
305    /* Set correct segment registers */
306    xor ax,ax
307    mov ds,ax
308    mov es,ax
309    mov fs,ax
310    mov gs,ax
311    mov ss,ax
312
313    /* Save current stack pointer */
314    mov word ptr ds:[stack16], sp
315
316    /* Set PAE and PGE: 10100000b */
317    // mov eax, cr4
318    // or eax, HEX(00A0)
319    mov eax, HEX(00A0)
320    mov cr4, eax
321
322    /* Point cr3 at the PML4 */
323    mov eax, PML4_ADDRESS
324    mov cr3, eax
325
326    /* Enable long mode */
327    mov ecx, MSR_EFER
328    rdmsr
329    or eax, HEX(00000100)
330    wrmsr
331
332    /* Activate long mode by enabling paging and protection simultaneously,
333       skipping protected mode entirely */
334    mov eax, cr0
335    or eax, HEX(80000001)
336    mov cr0, eax
337
338    /* Clear prefetch queue & correct CS */
339    ljmp16 LMODE_CS, InLongMode
340InLongMode:
341    //DB 66h, 0B8h, 18h, 00h // mov ax, LMODE_DS
342    //DB 66h, 8Eh, 0D8h // mov ds, ax
343    //DB 66h, 66h, 0C7h, 04h, 25h, 00h, 80h, 0Bh, 00h, 31h, 0Eh
344    //mov word ptr [HEX(b8000)], HEX(0e00) + '1'
345
346    .byte HEX(0FF), HEX(25) // opcode of 64bit indirect jump
347    .long 1 // relative address of LongModeEntryPoint
348    nop
349LongModeEntryPoint:
350    .long 0, 0
351
352    int HEX(16)
353    jmp Reboot
354
355CallbackTable:
356    .word Int386
357    .word Reboot
358    .word Relocator16Boot
359    .word PxeCallApi
360    .word PnpBiosGetDeviceNodeCount
361    .word PnpBiosGetDeviceNode
362
363    /* 16-bit stack pointer */
364stack16:
365    .word STACK16ADDR
366
367
368#include "int386.inc"
369#include "helpers.inc"
370#include "pxe.inc"
371#include "pnp.inc"
372
373.org (FREELDR_PE_BASE - FREELDR_BASE - 1)
374.byte 0
375.endcode16
376
377END
378