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 if 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
186BuildPageTables:
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 */
242RealModeEntryPoint:
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
251InRealMode:
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
282ExitToLongMode:
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
319InLongMode:
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
328LongModeEntryPoint:
329    .long 0, 0
330
331    int HEX(16)
332    jmp Reboot
333
334CallbackTable:
335    .word Int386
336    .word Reboot
337    .word ChainLoadBiosBootSectorCode
338    .word PxeCallApi
339    .word PnpBiosGetDeviceNodeCount
340    .word PnpBiosGetDeviceNode
341    .word 0 // BootLinuxKernel
342
343    /* 16-bit stack pointer */
344stack16:
345    .word STACK16ADDR
346
347
348#include "int386.inc"
349#include "pxe.inc"
350#include "pnp.inc"
351#include "helpers.inc"
352
353.org (FREELDR_PE_BASE - FREELDR_BASE - 1)
354.byte 0
355.endcode16
356
357END
358