bootentry.S 5.75 KB
Newer Older
Eddie Kohler's avatar
Eddie Kohler committed
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
###############################################################################
# BOOT ENTRY POINT
#
#   After the BIOS initializes the hardware on startup or system reset,
#   it loads the first 512-byte sector of the hard disk
#   into physical addresses 0x7C00-0x7DFF.
#   Then it jumps to address 0x7C00 and the OS starts running!
#
#   This file contains the code loaded at that address.
#   The `boot_start` routine switches the CPU out of compatibility mode,
#   then calls `boot` from `boot.cc` to finish the booting process.
#
#   There is no need to understand this code in detail!
#   (You may stop reading now.)
#
###############################################################################


###############################################################################
# For Your Information: COMPATIBILITY MODES
#
#   The Intel x86 architecture has many compatibility modes, going back to
#   the 8086, which supported only 16-bit addresses.  When the BIOS calls
#   into the OS, it is running in the "most compatible" mode, 16-bit real
#   mode. The machine acts like addresses are only 16 bits long,
#   there's no paging support, and there isn't even any support for
#   user-mode applications. The following weird magic transitions the
#   processor to 64-bit mode and sets up a page table suitable for initial
#   kernel execution.
#
###############################################################################

/* import constants from kernel.hh and x86-64.h */
#include "obj/k-asm.h"

.globl boot_start                               # Entry point
boot_start:
        .code16                         # This runs in real mode
        cli                             # Disable interrupts
        cld                             # String operations increment

        # All segments are initially 0.
        # Set up the stack pointer, growing downward from 0x7c00.
        movw    $boot_start, %sp

notify_bios64:
        # Notify the BIOS (the machine's firmware) to optimize itself
        # for x86-64 code. https://wiki.osdev.org/X86-64
        movw    $0xEC00, %ax
James Foster's avatar
James Foster committed
50
        mov     $2, %bl
Eddie Kohler's avatar
Eddie Kohler committed
51
52
53
54
55
56
57
58
59
60
61
        int     $0x15

init_boot_pagetable:
        # clear memory for boot page table
        .set BOOT_PAGETABLE,0x1000
        movl    $BOOT_PAGETABLE, %edi
        xorl    %eax, %eax
        movl    $(0x2000 / 4), %ecx
        rep stosl

        # set up boot page table
Eddie Kohler's avatar
Eddie Kohler committed
62
63
64
        # 0x1000: L4 page table; entries 0, 256, and 511 point to:
        # 0x2000: L3 page table; entries 0 and 510 map 1st 1GB of physmem
        # This is the minimal page table that maps all of
Eddie Kohler's avatar
Eddie Kohler committed
65
        # low-canonical, high-canonical, and kernel-text addresses to
James Foster's avatar
James Foster committed
66
67
        # the first 1GB (?) of physical memory.
        # (Not sure about the 1 GB number. Emperical tests show only 128 MB!)
Eddie Kohler's avatar
Eddie Kohler committed
68
        movl    $BOOT_PAGETABLE, %edi
James Foster's avatar
James Foster committed
69
        leal    0x1000 + PTE_P + PTE_W(%edi), %ecx      # combine address with flags
Eddie Kohler's avatar
Eddie Kohler committed
70
71
72
        movl    %ecx, (%edi)
        movl    %ecx, 0x800(%edi)
        movl    %ecx, 0xFF8(%edi)
James Foster's avatar
James Foster committed
73
         # the offsets below are adjusted by -3 to remove the flags!
Eddie Kohler's avatar
Eddie Kohler committed
74
75
76
        movl    $(PTE_P + PTE_W + PTE_PS), -3(%ecx)
        movl    $(PTE_P + PTE_W + PTE_PS), 0xFED(%ecx)

James Foster's avatar
James Foster committed
77
78
79
80
# Switch from real mode (16-bit) to long mode (64-bit):
#   Up until now, we have used direct physical memory addressing (real mode)
#   without explicitly telling the processor how to translate addresses.
#   When we enable paging, this is no longer true!
Eddie Kohler's avatar
Eddie Kohler committed
81
82
83
84
85
#   We need at least to set up some "segments" that tell the processor it's
#   OK to run code at any address, or write to any address.
#   The `gdt` and `gdtdesc` tables below define these segments.
#   This code loads them into the processor.
#   We need this setup to ensure the transition to protected mode is smooth.
James Foster's avatar
James Foster committed
86
#   See https://en.wikipedia.org/wiki/X86-64 for a nice diagram and description of processor modes.
Eddie Kohler's avatar
Eddie Kohler committed
87

James Foster's avatar
James Foster committed
88
real_to_long:
Eddie Kohler's avatar
Eddie Kohler committed
89
        movl    %cr4, %eax              # enable physical address extensions
James Foster's avatar
James Foster committed
90
        orl     $(CR4_PAE), %eax        # https://en.wikipedia.org/wiki/Physical_Address_Extension
Eddie Kohler's avatar
Eddie Kohler committed
91
92
        movl    %eax, %cr4

James Foster's avatar
James Foster committed
93
94
95
96
97
98
99
100
        movl    %edi, %cr3              # set the address of the page table

        movl    $MSR_IA32_EFER, %ecx    # enable long mode (https://wiki.osdev.org/Setting_Up_Long_Mode)

        rdmsr                           # read from the model-specific register into %eax
                                        #   IA32_EFER_LME is to enable 64-bit mode
                                        #   IA32_EFER_SCE is to enable syscall/sysret
                                        #   IA32_EFER_NXE is to allow PTE_XD (eXecute Disabled) in page table entries
Eddie Kohler's avatar
Eddie Kohler committed
101
        orl     $(IA32_EFER_LME | IA32_EFER_SCE | IA32_EFER_NXE), %eax
James Foster's avatar
James Foster committed
102
        wrmsr                           # write %eax to the model-specific register
Eddie Kohler's avatar
Eddie Kohler committed
103

James Foster's avatar
James Foster committed
104
105
106
107
        movl    %cr0, %eax              # turn on some other features
                                        #   CR0_PE sets Protection Enable
                                        #   CR0_WP sets Write Protect
                                        #   CR0_PG sets CR0_PG
Eddie Kohler's avatar
Eddie Kohler committed
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
        orl     $(CR0_PE | CR0_WP | CR0_PG), %eax
        movl    %eax, %cr0

        lgdt    gdtdesc + 6             # load GDT

        # CPU magic: jump to relocation, flush prefetch queue, and
        # reload %cs.  Has the effect of just jmp to the next
        # instruction, but simultaneously loads CS with
        # $SEGSEL_BOOT_CODE.
        ljmp    $SEGSEL_BOOT_CODE, $boot


# Segment descriptors
        .code32
        .p2align 3                           # force 8 byte alignment
gdt:    .word 0, 0, 0, 0                     # null
        .word 0, 0                           # kernel code segment
        .byte 0, 0x9A, 0x20, 0
gdtdesc:
        .word 0, 0, 0
        .word 0x0f                           # sizeof(gdt) - 1
        .long gdt                            # address gdt
        .long 0