9740273c718b87dcec93ac44f3c6ccb4a23ff2d0
[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 <kstack.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 # Calling convention for internal functions:
31 #
32 # my convention:
33 #       callee saved ebp, ebx
34 #       caller saves eax ecx edx esi edi
35 #       args: a0 edi, a1 esi, a2 edx, a3 ecx, a4 eax, a5+ stack
36 #       ret eax
37 #
38 # for reference, the normal convention:
39 #       callee saved: esi, edi, ebp, ebx
40 #       caller saved: eax, ecx, edx
41 #       args on stack
42 #       ret eax
43
44 /* Helper: creates count mappings in the PML3 for 1GB jumbo pages for the given
45  * vaddr to paddr range in physical memory.  Then it puts that PML3's addr in
46  * the PML4's appropriate slot.  Using a macro mostly to help with 64 bit
47  * argument marshalling.
48  *
49  * This will clobber ax, dx, cx, di, si.
50  *
51  * A few notes about the jumbo GB mapping:
52  * - PML3 is responsible for the 9 bits from 30-38, hence the >> 30 and mask
53  * - PML4 is responsible for the 9 bits from 47-39, hence the >> 39 and mask
54  * - We use the jumbo PTE_PS flag only on PML3 - can't do it for PML4.
55  * - PTEs are 8 bytes each, hence the scale = 8 in the indirect addressing
56  * - The top half of all of PML4's PTEs are set to 0.  This includes the top 20
57  * bits of the physical address of the page tables - which are 0 in our case.
58  * - The paddr for the PML3 PTEs is split across two 32-byte halves of the PTE.
59  * We drop off the lower 30 bits, since we're dealing with 1GB pages.  The 2
60  * LSBs go at the top of the first half of the PTE, and the remaining 30 are
61  * the lower 30 of the top half. */
62 #define MAP_GB_PAGES(pml3, vaddr, paddr, count)                                \
63         movl    $(boot_pml4), %eax;                                            \
64         push    %eax;                                                          \
65         movl    $(count), %eax;                                                \
66         push    %eax;                                                          \
67         movl    $(pml3), %edi;                                                 \
68         movl    $(vaddr >> 32), %esi;                                          \
69         movl    $(vaddr & 0xffffffff), %edx;                                   \
70         movl    $(paddr >> 32), %ecx;                                          \
71         movl    $(paddr & 0xffffffff), %eax;                                   \
72         call    map_gb_pages;                                                  \
73         add     $0x8, %esp
74
75 # Maps count GBs (up to 512) of vaddr -> paddr using pml3 and pml4 in 1GB pages
76 #
77 # edi pml3, esi vaddr_hi, edx vaddr_lo, ecx paddr_hi, eax paddr_lo,
78 # stack: count, pml4
79 map_gb_pages:
80         push    %ebx
81         movl    0x8(%esp), %ebx
82         # save these 3, need them for the next call
83         push    %edi
84         push    %esi
85         push    %edx
86         # arg5 on stack.  other args already in regs.
87         push    %ebx
88         call    fill_jpml3
89         add     $0x4, %esp              # pop arg5 frame
90         # restore our regs/args for next call
91         pop     %edx
92         pop     %esi
93         pop     %edi
94         movl    0xc(%esp), %ecx
95         call    insert_pml3
96         pop     %ebx
97         ret
98
99 # Fills pml3 with "count" jumbo entries, mapping from vaddr -> paddr.
100 # pml3s are responsible for bits 38..30 of vaddr space and 30 bit paddr entries
101 #
102 # edi pml3, esi vaddr_hi, edx vaddr_lo, ecx paddr_hi, eax paddr_lo, stack count
103 fill_jpml3:
104         push    %ebx
105         movl    0x8(%esp), %ebx
106         # want (vaddr >> 30) & 0x1ff into esi.  append upper 2 bits of edx to
107         # esi.
108         shll    $2, %esi
109         shrl    $30, %edx
110         orl     %edx, %esi
111         andl    $0x1ff, %esi
112         # want (paddr >> 30) into ecx.
113         shll    $2, %ecx
114         shrl    $30, %eax
115         orl     %eax, %ecx
116 1:
117         movl    %ecx, %eax
118         shll    $30, %eax               # lower part of PTE ADDR
119         orl     $(PTE_P | PTE_W | PTE_PS | PTE_G), %eax
120         movl    %eax, (%edi, %esi, 8)
121         movl    %ecx, %eax
122         shrl    $2, %eax                # upper part of PTE ADDR
123         movl    %eax, 4(%edi, %esi, 8)
124         # prep for next loop
125         incl    %esi
126         incl    %ecx
127         decl    %ebx
128         jnz     1b
129         pop     %ebx
130         ret
131
132 #define MAP_2MB_PAGES(pml3, vaddr, paddr, count, pml2base)                     \
133         movl    $(pml2base), %eax;                                             \
134         push    %eax;                                                          \
135         movl    $(boot_pml4), %eax;                                            \
136         push    %eax;                                                          \
137         movl    $(count), %eax;                                                \
138         push    %eax;                                                          \
139         movl    $(pml3), %edi;                                                 \
140         movl    $(vaddr >> 32), %esi;                                          \
141         movl    $(vaddr & 0xffffffff), %edx;                                   \
142         movl    $(paddr >> 32), %ecx;                                          \
143         movl    $(paddr & 0xffffffff), %eax;                                   \
144         call    map_2mb_pages;                                                 \
145         add     $0xc, %esp
146
147 # Maps count GBs (up to 512) of vaddr -> paddr using pml3, pml4, and an array of
148 # pml2s in 2MB pages
149 #
150 # edi pml3, esi vaddr_hi, edx vaddr_lo, ecx paddr_hi, eax paddr_lo,
151 # stack: count, pml4, pml2_base
152 map_2mb_pages:
153         push    %ebx
154         # save these 3, need them for the next call
155         push    %edi
156         push    %esi
157         push    %edx
158         # arg5 and 7 on stack.  other args already in regs.
159         movl    0x1c(%esp), %ebx        # arg7: 4 pushes, 1 retaddr, arg 5, arg6
160         push    %ebx
161         movl    0x18(%esp), %ebx        # arg6: 5 pushes, 1 retaddr
162         push    %ebx
163         call    fill_pml3
164         add     $0x8, %esp              # pop args frame
165         # restore our regs/args for next call
166         pop     %edx
167         pop     %esi
168         pop     %edi
169         movl    0xc(%esp), %ecx
170         call    insert_pml3
171         pop     %ebx
172         ret
173
174 # Fills pml3 with "count" pml2 entries, mapping from vaddr -> paddr.
175 # pml3s are responsible for bits 38..30 of vaddr space and 30 bit paddr entries
176 #
177 # edi pml3, esi vaddr_hi, edx vaddr_lo, ecx paddr_hi, eax paddr_lo,
178 # stack count, pml2base
179 fill_pml3:
180         push    %ebx
181         push    %ebp                    # scratch register
182         movl    0xc(%esp), %ebx
183 1:
184         push    %edi                    # save edi = pml3
185         push    %esi
186         push    %edx
187         push    %ecx
188         push    %eax
189         movl    $512, %ebp              # count = 512 for PML2 (map it all)
190         push    %ebp
191         # compute pml2 (pml2base + (total count - current count) * PGSIZE * 2)
192         movl    0x28(%esp), %ebp        # pml2base (8 push, 1 ret, arg5)
193         movl    0x24(%esp), %edi        # total count
194         subl    %ebx, %edi
195         shll    $13, %edi
196         addl    %edi, %ebp
197         movl    %ebp, %edi              # arg0 for the func call
198         call    fill_jpml2
199         add     $0x4, %esp
200         pop     %eax
201         pop     %ecx
202         pop     %edx
203         pop     %esi
204         pop     %edi
205         # re-save our register frame
206         push    %edi
207         push    %esi
208         push    %edx
209         push    %ecx
210         push    %eax
211         # prep call to insert (ecx = pml3, edi = pml2)
212         movl    %edi, %ecx
213         movl    %ebp, %edi
214         call    insert_pml2
215         pop     %eax
216         pop     %ecx
217         pop     %edx
218         pop     %esi
219         pop     %edi
220         # prep for next loop.  need to advance vaddr and paddr by 1GB
221         addl    $(1 << 30), %edx
222         adcl    $0, %esi
223         addl    $(1 << 30), %eax
224         adcl    $0, %ecx
225         decl    %ebx
226         jnz     1b
227         pop     %ebp
228         pop     %ebx
229         ret
230
231 # Fills pml2 with "count" jumbo entries, mapping from vaddr -> paddr
232 # pml2s are responsible for bits 29..21 of vaddr space and 21 bit paddr entries
233 #
234 # edi pml2, esi vaddr_hi, edx vaddr_lo, ecx paddr_hi, eax paddr_lo, stack count
235 fill_jpml2:
236         push    %ebx
237         movl    0x8(%esp), %ebx
238         # want (vaddr >> 21) & 0x1ff into esi.
239         shrl    $21, %edx
240         movl    %edx, %esi
241         andl    $0x1ff, %esi
242         # want (paddr >> 21) into ecx.
243         shll    $11, %ecx
244         shrl    $21, %eax
245         orl     %eax, %ecx
246 1:
247         movl    %ecx, %eax
248         shll    $21, %eax                       # lower part of PTE ADDR
249         orl     $(PTE_P | PTE_W | PTE_PS | PTE_G), %eax
250         movl    %eax, (%edi, %esi, 8)
251         movl    %ecx, %eax
252         shrl    $11, %eax                       # upper part of PTE ADDR
253         movl    %eax, 4(%edi, %esi, 8)
254         # prep for next loop
255         incl    %esi
256         incl    %ecx
257         decl    %ebx
258         jnz     1b
259         pop     %ebx
260         ret
261
262 # Inserts a pml3 into pml4, so that it handles mapping for vaddr
263 #
264 # edi pml3, esi vaddr_hi, edx vaddr_lo, ecx pml4
265 insert_pml3:
266         shrl    $7, %esi        # want to shift vaddr >> 39
267         andl    $0x1ff, %esi
268         orl     $(PTE_P | PTE_W | PTE_G), %edi
269         movl    %edi, (%ecx, %esi, 8)
270         movl    $0x0, 4(%ecx, %esi, 8)  # being clever, i know upper bits are 0
271         ret
272
273 # Inserts a pml2 into pml3, so that it handles mapping for vaddr
274 #
275 # edi pml2, esi vaddr_hi, edx vaddr_lo, ecx pml3
276 insert_pml2:
277         # want (vaddr >> 30) & 0x1ff into esi.  append upper 2 bits of edx to
278         # esi.
279         shll    $2, %esi
280         shrl    $30, %edx
281         orl     %edx, %esi
282         andl    $0x1ff, %esi
283         orl     $(PTE_P | PTE_W | PTE_G), %edi
284         movl    %edi, (%ecx, %esi, 8)
285         movl    $0x0, 4(%ecx, %esi, 8)  # being clever, i know upper bits are 0
286         ret
287
288 .globl _start
289 _start:
290         movl    $stack32top, %esp
291         push    %ebx                            # save mulitboot info
292         movw    $0x1234,0x472                   # warm boot
293         movl    $0x80000001, %eax
294         # some machines / VMs might not support long mode
295         cpuid
296         test    $(1 << 29), %edx
297         jz      err_no_long
298         # others don't support 1GB jumbo pages, which is a shame
299         test    $(1 << 26), %edx
300         jz      no_pml3ps
301         # build page table.  need mappings for
302         # - current code/data at 0x00100000 -> 0x00100000
303         # - kernel load location: 0xffffffffc0000000 -> 0x0000000000000000
304         # - kernbase: 0xffff80000000 -> 0x0000000000000000
305         # we'll need one table for the PML4, and three PML3 (PDPE)'s.  1GB will
306         # suffice for lo and hi (til we do the VPT and LAPIC mappings).  For
307         # kernbase, we'll do all 512 PML3 entries (covers 512GB)
308         MAP_GB_PAGES(boot_pml3_lo, 0x0000000000000000, 0x0, 1)
309         MAP_GB_PAGES(boot_pml3_hi, 0xffffffffc0000000, 0x0, 1)
310         MAP_GB_PAGES(boot_pml3_kb, 0xffff800000000000, 0x0, 512)
311         jmp             post_mapping
312 no_pml3ps:
313         MAP_2MB_PAGES(boot_pml3_lo, 0x0000000000000000, 0x0,   1, boot_pml2_lo)
314         MAP_2MB_PAGES(boot_pml3_hi, 0xffffffffc0000000, 0x0,   1, boot_pml2_hi)
315         MAP_2MB_PAGES(boot_pml3_kb, 0xffff800000000000, 0x0, 512, boot_pml2_kb)
316 post_mapping:
317         # load cr3 - note that in long mode, cr3 is 64 bits wide.  our boot
318         # pml4 is in lower memory, so it'll be fine if the HW 0 extends.
319         movl    $boot_pml4, %eax
320         movl    %eax, %cr3
321         # turn on paging option in cr4.  note we assume PSE support.  if we
322         # didn't have it, then our jumbo page mappings are going to fail.  we
323         # also want global pages (for performance).  PAE is the basics needed
324         # for long paging
325         movl    %cr4, %eax
326         orl     $(CR4_PSE | CR4_PGE | CR4_PAE), %eax
327         movl    %eax, %cr4
328         # Turn on the IA32E enabled bit.
329         # rd/wrmsr use ecx for the addr, and eax as the in/out register.
330         movl    $IA32_EFER_MSR, %ecx
331         rdmsr
332         orl     $IA32_EFER_IA32E_EN, %eax
333         wrmsr
334         # Setup cr0.  PE and PG are critical for now.  The others are similar to
335         # what we want in general (-AM with 64 bit, it's useless).
336         movl    %cr0, %eax
337         orl     $(CR0_PE | CR0_PG | CR0_WP | CR0_NE | CR0_MP), %eax
338         andl    $(~(CR0_AM | CR0_TS | CR0_EM | CR0_CD | CR0_NW)), %eax
339         movl    %eax, %cr0
340         pop     %ebx                            # restore multiboot info
341         # load the 64bit GDT and jump to long mode
342         lgdt    gdt64desc
343         ljmp    $0x08, $long_mode
344         # these are error handlers, we're jumping over these
345 err_no_long:
346         mov     $no_long_string, %esi
347         jmp     printstring
348 err_no_pml3ps:
349         mov     $no_pml3ps_string, %esi
350         jmp     printstring
351 printstring:
352         mov     $0xb8a00, %edi          # assuming CGA buffer, 16 lines down
353         mov     $0, %ecx
354 1:
355         movb    (%esi, %ecx), %bl
356         test    %bl, %bl
357         je      printdone
358         # print to the console (0x07 is white letters on black background)
359         mov     $0x07, %bh
360         mov     %bx, (%edi, %ecx, 2)
361         # print to serial
362         mov     $(0x3f8 + 5), %edx      # assuming COM1
363 2:
364         inb     %dx, %al
365         test    $0x20, %al                      # ready check
366         jz      2b
367         mov     $0x3f8, %edx            # assuming COM1
368         mov     %bl, %al
369         outb    %al, %dx
370         # advance the loop
371         inc     %ecx
372         jmp     1b
373 printdone:
374         hlt
375         jmp     printdone
376
377 .code64
378 long_mode:
379         # zero the data segments.  Not sure if this is legit or not.
380         xor     %rax, %rax
381         mov     %ax, %ds
382         mov     %ax, %es
383         mov     %ax, %ss
384         mov     %ax, %fs
385         mov     %ax, %gs
386         lldt    %ax
387         # paging is on, and our code is still running at 0x00100000.
388         # do some miscellaneous OS setup.
389         # set up gs to point to our pcpu info (both GS base and KERN GS base)
390         movabs  $(per_cpu_info), %rdx
391         movq    %rdx, %rax
392         shrq    $32, %rdx
393         andl    $0xffffffff, %eax
394         movl    $MSR_GS_BASE, %ecx
395         wrmsr
396         movl    $MSR_KERN_GS_BASE, %ecx
397         wrmsr
398         # Clear the frame pointer for proper backtraces
399         movq    $0x0, %rbp
400         # We can use the bootstack from the BSS, but we need the KERNBASE addr
401         movabs  $(bootstacktop + KERNBASE), %rsp
402         # Pass multiboot info to kernel_init (%rdi == arg1)
403         movq    %rbx, %rdi
404         movabs  $(kernel_init), %rax
405         call    *%rax
406         # Should never get here, but in case we do, just spin.
407 spin:   jmp     spin
408
409 .section .bootdata, "aw"
410         .p2align        2               # force 4 byte alignment
411 .globl gdt64
412 gdt64:
413         # keep the number of these in sync with SEG_COUNT
414         SEG_NULL
415         SEG_CODE_64(0)          # kernel code segment
416         SEG_DATA_64(0)          # kernel data segment
417         SEG_DATA_64(3)          # user data segment
418         SEG_CODE_64(3)          # user code segment
419         SEG_NULL                # these two nulls are a placeholder for the TSS
420         SEG_NULL                # these two nulls are a placeholder for the TSS
421 .globl gdt64desc
422 gdt64desc:
423         .word   (gdt64desc - gdt64 - 1)         # sizeof(gdt64) - 1
424         .long   gdt64   # HW 0-extends this to 64 bit when loading (i think)
425 no_long_string:
426         .string "Unable to boot: long mode not supported"
427 no_pml3ps_string:
428         .string "Unable to boot: 1 GB pages not supported"
429 # boot page tables
430 .section .bootbss, "w",@nobits
431         .align PGSIZE
432 .globl boot_pml4
433 boot_pml4:
434         .space  PGSIZE * 2
435 boot_pml3_lo:
436         .space  PGSIZE * 2
437 boot_pml3_hi:
438         .space  PGSIZE * 2
439 boot_pml3_kb:
440         .space  PGSIZE * 2
441 stack32:
442         .space  PGSIZE
443 stack32top:
444 # Could make all of the no-jumbo stuff a config var
445 boot_pml2_lo:           # one pml2 (1GB in the lo pml3)
446         .space  PGSIZE * 2
447 boot_pml2_hi:           # one pml2 (1GB in the hi pml3)
448         .space  PGSIZE * 2
449 boot_pml2_kb:           # 512 pml2s (+ epts) in the kb pml3
450         .space  PGSIZE * 512 * 2
451
452 # From here down is linked for KERNBASE
453 .text
454         .globl get_boot_pml4
455 get_boot_pml4:
456         movabs  $(boot_pml4), %rax
457         ret
458         .globl get_gdt64
459 get_gdt64:
460         movabs  $(gdt64), %rax
461         ret
462 .section .bootbss, "w",@nobits
463         .p2align        PGSHIFT         # force page alignment
464         .globl          bootstack
465 bootstack:
466         .space          KSTKSIZE
467         .globl          bootstacktop
468 bootstacktop: