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#include <hw/multiboot.h>
14
15#define PHYS_LOAD_ADDRESS (MEMBASE + KERNEL_LOAD_OFFSET)
16#define PHYS_ADDR_DELTA (KERNEL_BASE + KERNEL_LOAD_OFFSET - PHYS_LOAD_ADDRESS)
17#define PHYS(x) ((x) - PHYS_ADDR_DELTA)
18
19.section ".text.boot"
20.global _start
21_start:
22    jmp real_start
23
24.align 4
25
26/* flags for multiboot header */
27#define MULTIBOOT_HEADER_FLAGS (MULTIBOOT_PAGE_ALIGN | MULTIBOOT_MEMORY_INFO | MULTIBOOT_AOUT_KLUDGE)
28
29.type multiboot_header,STT_OBJECT
30multiboot_header:
31    /* magic */
32    .int MULTIBOOT_HEADER_MAGIC
33    /* flags */
34    .int MULTIBOOT_HEADER_FLAGS
35    /* checksum */
36    .int -(MULTIBOOT_HEADER_MAGIC + MULTIBOOT_HEADER_FLAGS)
37
38    /* header_addr */
39    .int PHYS(multiboot_header)
40    /* load_addr */
41    .int PHYS(_start)
42    /* load_end_addr */
43    .int PHYS(__data_end)
44    /* bss_end_addr */
45    .int PHYS(__bss_end)
46    /* entry_addr */
47    .int PHYS(real_start)
48
49real_start:
50    cmpl $MULTIBOOT_BOOTLOADER_MAGIC, %eax
51    jne 0f
52    movl %ebx, PHYS(_multiboot_info)
53
540:
55    /* load our new gdt by physical pointer */
56    lgdt PHYS(_gdtr_phys)
57
58    movw $DATA_SELECTOR, %ax
59    movw %ax, %ds
60    movw %ax, %es
61    movw %ax, %fs
62    movw %ax, %ss
63    movw %ax, %gs
64    movw %ax, %ss
65
66    /* load initial stack pointer */
67    movl $PHYS(_kstack + 4096), %esp
68
69    /*We jumped here in protected mode in a code segment that migh not longer
70      be valid , do a long jump to our code segment, we use retf instead of
71      ljmp to be able to use relative labels */
72    pushl $CODE_SELECTOR     /*Pushing our code segment */
73    pushl $PHYS(.Lfarjump)   /*and jump address */
74    retf    /*This instruction will jump to codesel:farjump */
75
76.Lfarjump:
77
78    /* zero the bss section */
79bss_setup:
80    movl $PHYS(__bss_start), %edi /* starting address of the bss */
81    movl $PHYS(__bss_end), %ecx   /* find the length of the bss in bytes */
82    subl %edi, %ecx
83    shrl $2, %ecx       /* convert to 32 bit words, since the bss is aligned anyway */
842:
85    movl $0, (%edi)
86    addl $4, %edi
87    loop 2b
88
89paging_setup:
90#if X86_LEGACY
91    /* map the first 16MB 1:1 with 4KB pages and again at 0x8000.0000 */
92
93    /* set up 4 page tables worth of entries */
94    movl $PHYS(kernel_pt), %edi
95    movl $1024*4,%ecx
96    movl $X86_KERNEL_PT_FLAGS, %eax
97
98.Lfill_pt:
99    movl %eax, (%edi)
100    addl $4, %edi
101    addl $4096, %eax
102    loop .Lfill_pt
103
104    /* set up the page dir with 4 entries at 0 and 0x8000.0000 pointing
105     * to 4 page tables that will map physical address 0 - 16MB
106     */
107    movl $PHYS(kernel_pd), %esi
108    movl $PHYS(kernel_pd) + 512*4, %edi
109    movl $PHYS(kernel_pt) + X86_KERNEL_PT_FLAGS, %eax
110    movl %eax, (%esi)
111    movl %eax, (%edi)
112    addl $4096, %eax
113    movl %eax, 4(%esi)
114    movl %eax, 4(%edi)
115    addl $4096, %eax
116    movl %eax, 8(%esi)
117    movl %eax, 8(%edi)
118    addl $4096, %eax
119    movl %eax, 12(%esi)
120    movl %eax, 12(%edi)
121#else
122    /* map the first 1GB 1:1 using 4MB pages */
123    movl $PHYS(kernel_pd), %esi
124    movl $0x100, %ecx
125    xor  %eax, %eax
126
127.Lfill_pd:
128    mov  %eax, %edx
129    orl  $X86_KERNEL_PD_LP_FLAGS, %edx
130    movl %edx, (%esi)
131    addl $4, %esi
132    addl $0x00400000, %eax
133    loop .Lfill_pd
134
135    /* map the first 1GB to KERNEL_ASPACE_BASE */
136    movl $(PHYS(kernel_pd) + 0x800), %esi
137    movl $0x100, %ecx
138    xor  %eax, %eax
139
140.Lfill_pd2:
141    mov  %eax, %edx
142    orl  $X86_KERNEL_PD_LP_FLAGS, %edx
143    movl %edx, (%esi)
144    addl $4, %esi
145    addl $0x00400000, %eax
146    loop .Lfill_pd2
147
148    /* enable PSE (4MB pages) */
149    mov %cr4, %eax
150    orl $(1<<4), %eax
151    mov %eax, %cr4
152#endif
153
154    /* Set PD in CR3 */
155    movl $PHYS(kernel_pd), %eax
156    mov %eax, %cr3
157
158    /* save a copy of the address of the kernel page directory */
159    movl %eax, PHYS(kernel_pd_phys)
160
161    /* Enabling Paging and from this point we are in */
162    mov %cr0,  %eax
163    btsl $(31), %eax
164    mov %eax, %cr0
165
166    /* load the high kernel stack */
167    movl $(_kstack + 4096), %esp
168
169    /* reload the high gdtr */
170    lgdt PHYS(_gdtr)
171
172    /* branch to the high address */
173    movl $main_lk, %eax
174    jmp *%eax
175
176main_lk:
177    /* set up the idt */
178    call setup_idt
179
180    /* set up the percpu data structure pointer for the boot cpu */
181    pushl $0
182    pushl $0
183    call x86_configure_percpu_early
184
185    /* call the main module */
186    call lk_main
1870:                          /* just sit around waiting for interrupts */
188    hlt                     /* interrupts will unhalt the processor */
189    pause
190    jmp 0b                  /* so jump back to halt to conserve power */
191