1/*
2 * Copyright (c) 2009 Corey Tabaka
3 * Copyright (c) 2015 Intel Corporation
4 * Copyright (c) 2016 Travis Geiselbrecht
5 *
6 * Use of this source code is governed by a MIT-style
7 * license that can be found in the LICENSE file or at
8 * https://opensource.org/licenses/MIT
9 */
10#include <lk/asm.h>
11#include <arch/x86/descriptor.h>
12#include <arch/x86/mmu.h>
13
14/* The magic number for the Multiboot header. */
15#define MULTIBOOT_HEADER_MAGIC 0x1BADB002
16
17/* The flags for the Multiboot header. */
18#if defined(__ELF__) && 0
19#define MULTIBOOT_HEADER_FLAGS 0x00000002
20#else
21#define MULTIBOOT_HEADER_FLAGS 0x00010002
22#endif
23
24/* The magic number passed by a Multiboot-compliant boot loader. */
25#define MULTIBOOT_BOOTLOADER_MAGIC 0x2BADB002
26
27#define PHYS_LOAD_ADDRESS (MEMBASE + KERNEL_LOAD_OFFSET)
28#define PHYS_ADDR_DELTA (KERNEL_BASE + KERNEL_LOAD_OFFSET - PHYS_LOAD_ADDRESS)
29#define PHYS(x) ((x) - PHYS_ADDR_DELTA)
30
31.section ".text.boot"
32.global _start
33_start:
34    jmp real_start
35
36.align 4
37
38.type multiboot_header,STT_OBJECT
39multiboot_header:
40    /* magic */
41    .int MULTIBOOT_HEADER_MAGIC
42    /* flags */
43    .int MULTIBOOT_HEADER_FLAGS
44    /* checksum */
45    .int -(MULTIBOOT_HEADER_MAGIC + MULTIBOOT_HEADER_FLAGS)
46
47#if !defined(__ELF__) || 1
48    /* header_addr */
49    .int PHYS(multiboot_header)
50    /* load_addr */
51    .int PHYS(_start)
52    /* load_end_addr */
53    .int PHYS(__data_end)
54    /* bss_end_addr */
55    .int PHYS(__bss_end)
56    /* entry_addr */
57    .int PHYS(real_start)
58#endif
59
60real_start:
61    cmpl $MULTIBOOT_BOOTLOADER_MAGIC, %eax
62    jne 0f
63    movl %ebx, PHYS(_multiboot_info)
64
650:
66    /* load our new gdt by physical pointer */
67    lgdt PHYS(_gdtr_phys)
68
69    movw $DATA_SELECTOR, %ax
70    movw %ax, %ds
71    movw %ax, %es
72    movw %ax, %fs
73    movw %ax, %ss
74    movw %ax, %gs
75    movw %ax, %ss
76
77    /* load initial stack pointer */
78    movl $PHYS(_kstack + 4096), %esp
79
80    /*We jumped here in protected mode in a code segment that migh not longer
81      be valid , do a long jump to our code segment, we use retf instead of
82      ljmp to be able to use relative labels */
83    pushl $CODE_SELECTOR     /*Pushing our code segment */
84    pushl $PHYS(.Lfarjump)   /*and jump address */
85    retf    /*This instruction will jump to codesel:farjump */
86
87.Lfarjump:
88
89    /* zero the bss section */
90bss_setup:
91    movl $PHYS(__bss_start), %edi /* starting address of the bss */
92    movl $PHYS(__bss_end), %ecx   /* find the length of the bss in bytes */
93    subl %edi, %ecx
94    shrl $2, %ecx       /* convert to 32 bit words, since the bss is aligned anyway */
952:
96    movl $0, (%edi)
97    addl $4, %edi
98    loop 2b
99
100paging_setup:
101#ifdef PAE_MODE_ENABLED
102#error broken for now
103    /* Preparing PAE paging, we will use 2MB pages covering 1GB
104    for initial bootstrap, this page table will be 1 to 1 */
105
106    /* Setting the First PDPTE with a PD table reference*/
107    movl $pdp,   %eax
108    orl  $0x01, %eax
109    movl %eax, (pdpt)
110
111    movl $pdp, %esi
112    movl $0x1ff, %ecx
113
114fill_pdp:
115    movl $0x1ff, %eax
116    subl %ecx, %eax
117    shll $21,%eax
118    orl  $0x83, %eax
119    movl %eax, (%esi)
120    addl $8,%esi
121    loop fill_pdp
122
123    /* Set PDPT in CR3 */
124    movl $pdpt, %eax
125    mov %eax, %cr3
126
127    /* Enabling PAE*/
128    mov %cr4, %eax
129    btsl $(5), %eax
130    mov %eax, %cr4
131
132    /* Enabling Paging and from this point we are in
133    32 bit compatibility mode */
134    mov %cr0,  %eax
135    btsl $(31), %eax
136    mov %eax,  %cr0
137
138#elif X86_LEGACY
139    /* map the first 16MB 1:1 with 4KB pages and again at 0x8000.0000 */
140
141    /* set up 4 page tables worth of entries */
142    movl $PHYS(kernel_pt), %edi
143    movl $1024*4,%ecx
144    movl $X86_KERNEL_PT_FLAGS, %eax
145
146.Lfill_pt:
147    movl %eax, (%edi)
148    addl $4, %edi
149    addl $4096, %eax
150    loop .Lfill_pt
151
152    /* set up the page dir with 4 entries at 0 and 0x8000.0000 pointing
153     * to 4 page tables that will map physical address 0 - 16MB
154     */
155    movl $PHYS(kernel_pd), %esi
156    movl $PHYS(kernel_pd) + 512*4, %edi
157    movl $PHYS(kernel_pt) + X86_KERNEL_PT_FLAGS, %eax
158    movl %eax, (%esi)
159    movl %eax, (%edi)
160    addl $4096, %eax
161    movl %eax, 4(%esi)
162    movl %eax, 4(%edi)
163    addl $4096, %eax
164    movl %eax, 8(%esi)
165    movl %eax, 8(%edi)
166    addl $4096, %eax
167    movl %eax, 12(%esi)
168    movl %eax, 12(%edi)
169
170    /* Set PD in CR3 */
171    movl $PHYS(kernel_pd), %eax
172    mov %eax, %cr3
173
174    /* Enabling Paging and from this point we are in */
175    mov %cr0,  %eax
176    btsl $(31), %eax
177    mov %eax, %cr0
178#else
179    /* map the first 1GB 1:1 using 4MB pages */
180    movl $PHYS(kernel_pd), %esi
181    movl $0x100, %ecx
182    xor  %eax, %eax
183
184.Lfill_pd:
185    mov  %eax, %edx
186    orl  $X86_KERNEL_PD_LP_FLAGS, %edx
187    movl %edx, (%esi)
188    addl $4, %esi
189    addl $0x00400000, %eax
190    loop .Lfill_pd
191
192    /* map the first 1GB to KERNEL_ASPACE_BASE */
193    movl $(PHYS(kernel_pd) + 0x800), %esi
194    movl $0x100, %ecx
195    xor  %eax, %eax
196
197.Lfill_pd2:
198    mov  %eax, %edx
199    orl  $X86_KERNEL_PD_LP_FLAGS, %edx
200    movl %edx, (%esi)
201    addl $4, %esi
202    addl $0x00400000, %eax
203    loop .Lfill_pd2
204
205    /* Set PD in CR3 */
206    movl $PHYS(kernel_pd), %eax
207    mov %eax, %cr3
208
209    /* Enabling Paging and from this point we are in */
210    mov %cr4, %eax
211    orl $0x10, %eax
212    mov %eax, %cr4
213
214    mov %cr0,  %eax
215    btsl $(31), %eax
216    mov %eax, %cr0
217#endif
218
219    /* load the high kernel stack */
220    movl $(_kstack + 4096), %esp
221
222    /* reload the high gdtr */
223    lgdt PHYS(_gdtr)
224
225    /* branch to the high address */
226    movl $main_lk, %eax
227    jmp *%eax
228
229main_lk:
230    /* set up the idt */
231    call setup_idt
232
233    /* call the main module */
234    call lk_main
2350:                          /* just sit around waiting for interrupts */
236    hlt                     /* interrupts will unhalt the processor */
237    pause
238    jmp 0b                  /* so jump back to halt to conserve power */
239