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