edb09999b2d300c593d857e63fd16358cb07b705
[akaros.git] / kern / arch / x86 / entry64.S
1 /* Copyright (c) 2013 The Regents of the University of California
2  * Barret Rhoden <brho@cs.berkeley.edu>
3  * See LICENSE for details. */
4
5 #include <arch/mmu.h>
6 #include <arch/trap.h>
7 #include <arch/x86.h>
8 #include <ros/memlayout.h>
9
10 #define MULTIBOOT_PAGE_ALIGN  (1<<0)
11 #define MULTIBOOT_MEMORY_INFO (1<<1)
12 #define MULTIBOOT_HEADER_MAGIC (0x1BADB002)
13 #define MULTIBOOT_HEADER_FLAGS (MULTIBOOT_MEMORY_INFO | MULTIBOOT_PAGE_ALIGN)
14 #define CHECKSUM (-(MULTIBOOT_HEADER_MAGIC + MULTIBOOT_HEADER_FLAGS))
15
16 # The kernel bootstrap (this code) is linked and loaded at physical address
17 # 0x00100000 (1MB), which is the start of extended memory.  (See kernel.ld)
18
19 # Flagging boottext to be text.  Check out:
20 # http://sourceware.org/binutils/docs/as/Section.html
21 .section .boottext, "awx"
22
23 .code32
24 .align 4
25 multiboot_header:
26 .long MULTIBOOT_HEADER_MAGIC
27 .long MULTIBOOT_HEADER_FLAGS
28 .long CHECKSUM
29
30 /* Helper: creates count mappings in the PML3 for 1GB jumbo pages for the given
31  * vaddr to paddr range in physical memory.  Then it puts that PML3's addr in
32  * the PML4's appropriate slot.  A few notes:
33  *      - PML3 is responsible for the 9 bits from 30-38, hence the >> 30 and mask
34  *      - PML4 is responsible for the 9 bits from 47-39, hence the >> 39 and mask
35  *      - We use the jumbo PTE_PS flag only on PML3 - can't do it for PML4.
36  *      - PTEs are 8 bytes each, hence the scale = 8 in the indirect addressing
37  *      - The top half of all of PML4's PTEs are set to 0.  This includes the top 20
38  *      bits of the physical address of the page tables - which are 0 in our case.
39  *      - The paddr for the PML3 PTEs is split across two 32-byte halves of the PTE.
40  *      We drop off the lower 30 bits, since we're dealing with 1GB pages.  The 2
41  *      LSBs go at the top of the first half of the PTE, and the remaining 30 are
42  *      the lower 30 of the top half.
43  *      - In general, I use eax as the offset in a PML (the entry is determined by
44  *      the vaddr), edx for the entry I'm writing, and ecx for the value I'm
45  *      writing.  For PML3's mappings, esi tracks which paddr we are on.  Finally,
46  *      I'm using edi for loop control. */
47 #define MAP_GB_PAGES(pml3, vaddr, paddr, count)                                \
48         movl    $(pml3), %edx;                                                     \
49         movl    $(((vaddr) >> 30) & 0x1ff), %eax;                                  \
50         movl    $((paddr) >> 30), %esi;                                            \
51         movl    $(count), %edi;                                                    \
52 1:;                                                                            \
53         movl    %esi, %ecx;                                                        \
54         shll    $30, %ecx;                       /* lower part of PTE ADDR */      \
55         orl             $(PTE_P | PTE_W | PTE_PS), %ecx;                                   \
56         movl    %ecx, (%edx, %eax, 8);                                             \
57         movl    %esi, %ecx;                                                        \
58         shrl    $2, %ecx;                        /* upper part of PTE ADDR */      \
59         movl    %ecx, 4(%edx, %eax, 8);                                            \
60         /* prep for next loop */;                                                  \
61         incl    %eax;                                                              \
62         incl    %esi;                                                              \
63         /* could test eax against count, if we're short on regs */;                \
64         decl    %edi;                                                              \
65         jnz             1b;                                                                \
66         /* now insert the PML3 into pml4 */;                                       \
67         movl    $(((vaddr) >> 39) & 0x1ff), %eax;                                  \
68         movl    $boot_pml4, %edx;                                                  \
69         movl    $(pml3), %ecx;                                                     \
70         orl             $(PTE_P | PTE_W), %ecx;                                            \
71         movl    %ecx, (%edx, %eax, 8);                                             \
72         movl    $0x0, 4(%edx, %eax, 8)
73
74 .globl          _start
75 _start:
76         movw    $0x1234,0x472                   # warm boot
77         movl    $0x80000001, %eax
78         # some machines / VMs might not support long mode or (more likely) PML3
79         # jumbos (which we use)
80         cpuid
81         test    $(1 << 29), %edx
82         jz              err_no_long
83         test    $(1 << 26), %edx
84         jz              err_no_pml3ps
85         # build page table.  need mappings for
86         #       - current code/data at 0x00100000 -> 0x00100000
87         #       - kernel load location: 0xffffffffc0000000 -> 0x0000000000000000
88         #       - kernbase: 0xffff80000000 -> 0x0000000000000000
89         # we'll need one table for the PML4, and three PML3 (PDPE)'s.  1GB will
90         # suffice for lo and hi (til we do the VPT and LAPIC mappings).  For
91         # kernbase, we'll do all 512 PML3 entries (covers 512GB)
92         MAP_GB_PAGES(boot_pml3_lo,       0x0000000000000000, 0x0, 1)
93         MAP_GB_PAGES(boot_pml3_hi,       0xffffffffc0000000, 0x0, 1)
94         MAP_GB_PAGES(boot_pml3_kernbase, 0xffff800000000000, 0x0, 512)
95         # load cr3 - note that in long mode, cr3 is 64 bits wide.  our boot pml4 is
96         # in lower memory, so it'll be fine if the HW 0 extends.
97         movl    $boot_pml4, %eax
98         movl    %eax, %cr3
99         # turn on paging option in cr4.  note we assume PSE support.  if we didn't
100         # have it, then our jumbo page mappings are going to fail.  we also want
101         # global pages (for performance).  PAE is the basics needed for long paging
102         movl    %cr4, %eax
103         orl             $(CR4_PSE | CR4_PGE | CR4_PAE), %eax
104         movl    %eax, %cr4
105         # Turn on the IA32E enabled bit.
106         # rd/wrmsr use ecx for the addr, and eax as the in/out register.
107         movl    $IA32_EFER_MSR, %ecx
108         rdmsr
109         orl             $IA32_EFER_IA32E_EN, %eax
110         wrmsr
111         # Setup cr0.  PE and PG are critical for now.  The others are similar to
112         # what we want in general (-AM with 64 bit, it's useless).
113         movl    %cr0, %eax
114         orl             $(CR0_PE | CR0_PG | CR0_WP | CR0_NE | CR0_MP), %eax  
115         andl    $(~(CR0_AM | CR0_TS | CR0_EM | CR0_CD | CR0_NW)), %eax  
116         movl    %eax, %cr0
117         # load the 64bit GDT and jump to long mode
118         lgdt    gdt64desc
119         ljmp    $0x08, $long_mode
120         # these are error handlers, we're jumping over these
121 err_no_long:
122         mov             $no_long_string, %esi
123         jmp             printstring
124 err_no_pml3ps:
125         mov             $no_pml3ps_string, %esi
126         jmp             printstring
127 printstring:
128         mov             $0xb8a00, %edi          # assuming CGA buffer, 16 lines down
129         mov             $0, %ecx
130 1:
131         movb    (%esi, %ecx), %bl
132         test    %bl, %bl
133         je              printdone
134         # print to the console (0x07 is white letters on black background)
135         mov             $0x07, %bh
136         mov             %bx, (%edi, %ecx, 2)
137         # print to serial
138         mov             $(0x3f8 + 5), %edx      # assuming COM1
139 2:
140         inb             %dx, %al
141         test    $0x20, %al                      # ready check
142         jz              2b
143         mov             $0x3f8, %edx            # assuming COM1
144         mov             %bl, %al
145         outb    %al, %dx
146         # advance the loop
147         inc             %ecx
148         jmp             1b
149 printdone:
150         hlt
151         jmp             printdone
152
153 .code64
154 long_mode:
155         # zero the data segments.  Not sure if this is legit or not.
156         xor             %rax, %rax
157         mov             %ax, %ds
158         mov             %ax, %es
159         mov             %ax, %ss
160         mov             %ax, %fs
161         mov             %ax, %gs
162         lldt    %ax
163         # paging is on, and our code is still running at 0x00100000.
164         # do some miscellaneous OS setup.  the coreid stuff is so we can call
165         # core_id() before smp_boot. 
166         movabs  $(os_coreid_lookup), %rax
167         movl    $0x0, (%rax)
168         movabs  $(hw_coreid_lookup), %rax
169         movl    $0x0, (%rax)
170         # set up gs to point to our pcpu info (both GS base and KERN GS base)
171         movabs  $(per_cpu_info), %rdx
172         movq    %rdx, %rax
173         shrq    $32, %rdx
174         andl    $0xffffffff, %eax
175         movl    $MSR_GS_BASE, %ecx
176         wrmsr
177         movl    $MSR_KERN_GS_BASE, %ecx
178         wrmsr
179         # Clear the frame pointer for proper backtraces
180         movq    $0x0, %rbp
181         movabs  $(bootstacktop), %rsp
182         movabs  $(num_cpus), %rax
183         movl    $0x1, (%rax)
184         # Pass multiboot info to kernel_init (%rdi == arg1)
185         movq    %rbx, %rdi
186         movabs  $(kernel_init), %rax
187         call    *%rax
188         # Should never get here, but in case we do, just spin.
189 spin:   jmp     spin
190
191 .section .bootdata, "aw"
192         .p2align        2               # force 4 byte alignment
193 .globl gdt64
194 gdt64:
195         # keep the number of these in sync with SEG_COUNT
196         SEG_NULL
197         SEG_CODE_64(0)          # kernel code segment
198         SEG_DATA_64(0)          # kernel data segment
199         SEG_DATA_64(3)          # user data segment
200         SEG_CODE_64(3)          # user code segment
201         SEG_NULL                        # these two nulls are a placeholder for the TSS
202         SEG_NULL                        # these two nulls are a placeholder for the TSS
203 .globl gdt64desc
204 gdt64desc:
205         .word   (gdt64desc - gdt64 - 1)         # sizeof(gdt64) - 1
206         .long   gdt64           # HW 0-extends this to 64 bit when loading (i think)
207 no_long_string:
208         .string "Unable to boot: long mode not supported"
209 no_pml3ps_string:
210         .string "Unable to boot: 1 GB pages not supported"
211 # boot page tables
212         .align PGSIZE
213 .globl boot_pml4
214 boot_pml4:
215         .space  PGSIZE
216 boot_pml3_lo:
217         .space  PGSIZE
218 boot_pml3_hi:
219         .space  PGSIZE
220 boot_pml3_kernbase:
221         .space  PGSIZE
222
223 # From here down is linked for KERNBASE
224 .data
225         .p2align        PGSHIFT         # force page alignment
226         .globl          bootstack
227 bootstack:
228         .space          KSTKSIZE
229         .globl          bootstacktop   
230 bootstacktop: