akaros/kern/arch/x86/entry64.S
<<
>>
Prefs
   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
  25multiboot_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
  79map_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
 103fill_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
 1161:
 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
 152map_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
 179fill_pml3:
 180        push    %ebx
 181        push    %ebp                    # scratch register
 182        movl    0xc(%esp), %ebx
 1831:
 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
 235fill_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
 2461:
 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
 265insert_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
 276insert_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
 312no_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)
 316post_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
 345err_no_long:
 346        mov     $no_long_string, %esi
 347        jmp     printstring
 348err_no_pml3ps:
 349        mov     $no_pml3ps_string, %esi
 350        jmp     printstring
 351printstring:
 352        mov     $0xb8a00, %edi          # assuming CGA buffer, 16 lines down
 353        mov     $0, %ecx
 3541:
 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
 3632:
 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
 373printdone:
 374        hlt
 375        jmp     printdone
 376
 377.code64
 378long_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.
 407spin:   jmp     spin
 408
 409.section .bootdata, "aw"
 410        .p2align        2               # force 4 byte alignment
 411.globl gdt64
 412gdt64:
 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
 422gdt64desc:
 423        .word   (gdt64desc - gdt64 - 1)         # sizeof(gdt64) - 1
 424        .long   gdt64   # HW 0-extends this to 64 bit when loading (i think)
 425no_long_string:
 426        .string "Unable to boot: long mode not supported"
 427no_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
 433boot_pml4:
 434        .space  PGSIZE * 2
 435boot_pml3_lo:
 436        .space  PGSIZE * 2
 437boot_pml3_hi:
 438        .space  PGSIZE * 2
 439boot_pml3_kb:
 440        .space  PGSIZE * 2
 441stack32:
 442        .space  PGSIZE
 443stack32top:
 444# Could make all of the no-jumbo stuff a config var
 445boot_pml2_lo:           # one pml2 (1GB in the lo pml3)
 446        .space  PGSIZE * 2
 447boot_pml2_hi:           # one pml2 (1GB in the hi pml3)
 448        .space  PGSIZE * 2
 449boot_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
 455get_boot_pml4:
 456        movabs  $(boot_pml4), %rax
 457        ret
 458        .globl get_gdt64
 459get_gdt64:
 460        movabs  $(gdt64), %rax
 461        ret
 462.section .bootbss, "w",@nobits
 463        .p2align        PGSHIFT         # force page alignment
 464        .globl          bootstack
 465bootstack:
 466        .space          KSTKSIZE
 467        .globl          bootstacktop
 468bootstacktop:
 469