4c6ed2f84a7a05c59af52d44f405d23c1c167965
[pintos-anon] / src / threads / start.S
1 #### The loader needs to have some way to know the kernel's entry
2 #### point, that is, the address to which it should jump to start the
3 #### kernel.  We handle this by writing the linker script kernel.lds.S
4 #### so that this module appears at the very beginning of the kernel
5 #### image, and then using that as the entry point.
6
7 #include "threads/loader.h"
8         .intel_syntax noprefix
9
10 .section .start
11         
12 # Code runs in real mode, which is a 16-bit segment.
13         .code16
14
15 .globl start
16 start:
17
18 # Disable interrupts.
19 # String instructions go upward.
20
21         cli
22         cld
23
24 # Set up data segments.
25
26         sub ax, ax
27         mov ds, ax
28         mov es, ax
29
30 #### Enable A20.  Address line 20 is tied to low when the machine
31 #### boots, which prevents addressing memory about 1 MB.  This code
32 #### fixes it.
33         
34 # Poll status register while busy.
35
36 1:      in al, 0x64
37         test al, 0x2
38         jnz 1b
39
40 # Send command for writing output port.
41
42         mov al, 0xd1
43         outb 0x64, al
44
45 # Poll status register while busy.
46
47 1:      in al, 0x64
48         test al, 0x2
49         jnz 1b
50
51 # Enable A20 line.
52
53         mov al, 0xdf
54         out 0x60, al
55
56 #### Get memory size, via interrupt 15h function 88h, which returns CF
57 #### clear if successful, with AX = (kB of physical memory) - 1024.
58 #### This only works for memory sizes <= 65 MB, which should be fine
59 #### for our purposes.  We only reserve enough memory for page tables
60 #### for 64 MB of RAM, so we cap it at that value.
61         
62         mov ah, 0x88
63         int 0x15
64         jc panic
65         cli                     # BIOS might have enabled interrupts
66         add eax, 1024           # Total kB memory
67         cmp eax, 64 * 1024
68         jbe 1f
69         mov eax, 64 * 1024
70 1:      shr eax, 2              # Total 4 kB pages
71         mov [ram_pages], eax 
72         
73 #### Create temporary page directory and page table and set page
74 #### directory base register.
75
76 # Create page directory at 64 kB and fill with zeroes.
77         mov ax, LOADER_PD_BASE / 16
78         mov es, ax
79         sub eax, eax
80         sub edi, edi
81         mov ecx, 0x400
82         rep stosd
83
84 # Add a PDE mapping both virtual addresses 0 and LOADER_PHYS_BASE
85 # to a single PTE.
86 # See [IA32-v3] section 3.7.6 for a description of the bits in eax.
87
88         mov eax, LOADER_PT_BASE / 16 + 7
89         mov es:[0], eax
90         mov es:[LOADER_PHYS_BASE / 1024 / 1024], eax
91
92 # Set up one-to-map linear to physical map for the first 4 MB of RAM.
93 # See [IA32-v3] section 3.7.6 for a description of the bits in eax.
94
95         mov ax, LOADER_PT_BASE / 16
96         mov es, ax
97         mov eax, 0x7
98         mov cx, 0x400
99 1:      stosd
100         add eax, 0x1000
101         loop 1b
102
103 # Set page directory base register.
104
105         mov eax, LOADER_PD_BASE
106         mov cr3, eax
107         
108 #### Switch to protected mode.
109
110 # Then we point the GDTR to our GDT.  Protected mode requires a GDT.
111 # We need a data32 prefix to ensure that all 32 bits of the GDT
112 # descriptor are loaded (default is to load only 24 bits).
113
114         data32 lgdt gdtdesc
115
116 # Then we turn on the following bits in CR0:
117 #    PE (Protect Enable): this turns on protected mode.
118 #    PG (Paging): turns on paging.
119 #    WP (Write Protect): if unset, ring 0 code ignores
120 #       write-protect bits in page tables (!).
121 #    EM (Emulation): forces floating-point instructions to trap.
122 #       We don't support floating point. 
123         
124 #define CR0_PE 0x00000001
125 #define CR0_EM 0x00000004
126 #define CR0_PG 0x80000000
127 #define CR0_WP 0x00010000
128         
129         mov eax, cr0
130         or eax, CR0_PE + CR0_PG + CR0_WP + CR0_EM
131         mov cr0, eax
132         
133 # We're now in protected mode in a 16-bit segment.  The CPU still has
134 # the real-mode code segment cached in cs's segment descriptor.  We
135 # need to reload cs, and the easiest way is to use a far jump.
136 # Because we're not in a 32-bit segment the data32 prefix is needed to
137 # jump to a 32-bit offset.
138
139         data32 ljmp SEL_KCSEG, 1f + LOADER_PHYS_BASE
140
141 # We're now in protected mode in a 32-bit segment.
142
143         .code32
144
145 # Reload all the other segment registers and the stack pointer to
146 # point into our new GDT.
147
148 1:      mov ax, SEL_KDSEG
149         mov ds, ax              
150         mov es, ax              
151         mov fs, ax              
152         mov gs, ax              
153         mov ss, ax
154         add esp, LOADER_PHYS_BASE
155
156 #### Jump to kernel entry point.
157
158         mov eax, main
159         call eax
160 1:      jmp 1b
161
162 #### GDT
163
164 gdt:
165         .quad 0x0000000000000000        # null seg
166         .quad 0x00cf9a000000ffff        # code seg
167         .quad 0x00cf92000000ffff        # data seg
168         
169 gdtdesc:
170         .word   0x17                    # sizeof (gdt) - 1
171         .long   gdt + LOADER_PHYS_BASE  # address gdt
172
173 #### Fatal error.
174 #### Print panicmsg (with help from the BIOS) and spin.
175
176 panic:  .code16                 # We only panic in real mode.
177         mov si, offset panicmsg
178         mov ah, 0xe
179         sub bh, bh
180 1:      lodsb
181         test al, al
182 2:      jz 2b                   # Spin.
183         int 0x10
184         jmp 1b
185
186 panicmsg:
187         .ascii "Panic!"
188         .byte 0