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