Implement a proper block layer with partition support.
[pintos-anon] / src / threads / start.S
1         #include "threads/loader.h"
2
3 #### Kernel startup code.
4
5 #### The loader (in loader.S) loads the kernel at physical address
6 #### 0x20000 (128 kB) and jumps to "start", defined here.  This code
7 #### switches from real mode to 32-bit protected mode and calls
8 #### main().
9
10 /* Flags in control register 0. */
11 #define CR0_PE 0x00000001      /* Protection Enable. */
12 #define CR0_EM 0x00000004      /* (Floating-point) Emulation. */
13 #define CR0_PG 0x80000000      /* Paging. */
14 #define CR0_WP 0x00010000      /* Write-Protect enable in kernel mode. */
15
16         .section .start
17
18 # The following code runs in real mode, which is a 16-bit code segment.
19         .code16
20
21 .func start
22 .globl start
23 start:
24
25 # The loader called into us with CS = 0x2000, SS = 0x0000, ESP = 0xf000,
26 # but we should initialize the other segment registers.
27
28         mov $0x2000, %ax
29         mov %ax, %ds
30         mov %ax, %es
31
32 # Set string instructions to go upward.
33         cld
34
35 #### Get memory size, via interrupt 15h function 88h (see [IntrList]),
36 #### which returns AX = (kB of physical memory) - 1024.  This only
37 #### works for memory sizes <= 65 MB, which should be fine for our
38 #### purposes.  We cap memory at 64 MB because that's all we prepare
39 #### page tables for, below.
40
41         movb $0x88, %ah
42         int $0x15
43         addl $1024, %eax        # Total kB memory
44         cmp $0x10000, %eax      # Cap at 64 MB
45         jbe 1f
46         mov $0x10000, %eax
47 1:      shrl $2, %eax           # Total 4 kB pages
48         addr32 movl %eax, init_ram_pages - LOADER_PHYS_BASE - 0x20000
49
50 #### Enable A20.  Address line 20 is tied low when the machine boots,
51 #### which prevents addressing memory about 1 MB.  This code fixes it.
52
53 # Poll status register while busy.
54
55 1:      inb $0x64, %al
56         testb $0x2, %al
57         jnz 1b
58
59 # Send command for writing output port.
60
61         movb $0xd1, %al
62         outb %al, $0x64
63
64 # Poll status register while busy.
65
66 1:      inb $0x64, %al
67         testb $0x2, %al
68         jnz 1b
69
70 # Enable A20 line.
71
72         movb $0xdf, %al
73         outb %al, $0x60
74
75 # Poll status register while busy.
76
77 1:      inb $0x64, %al
78         testb $0x2, %al
79         jnz 1b
80
81 #### Create temporary page directory and page table and set page
82 #### directory base register.
83
84 # Create page directory at 0xf000 (60 kB) and fill with zeroes.
85         mov $0xf00, %ax
86         mov %ax, %es
87         subl %eax, %eax
88         subl %edi, %edi
89         movl $0x400, %ecx
90         rep stosl
91
92 # Add PDEs to point to page tables for the first 64 MB of RAM.
93 # Also add identical PDEs starting at LOADER_PHYS_BASE.
94 # See [IA32-v3a] section 3.7.6 "Page-Directory and Page-Table Entries"
95 # for a description of the bits in %eax.
96
97         movl $0x10007, %eax
98         movl $0x11, %ecx
99         subl %edi, %edi
100 1:      movl %eax, %es:(%di)
101         movl %eax, %es:LOADER_PHYS_BASE >> 20(%di)
102         addw $4, %di
103         addl $0x1000, %eax
104         loop 1b
105
106 # Set up page tables for one-to-map linear to physical map for the
107 # first 64 MB of RAM.
108 # See [IA32-v3a] section 3.7.6 "Page-Directory and Page-Table Entries"
109 # for a description of the bits in %eax.
110
111         movw $0x1000, %ax
112         movw %ax, %es
113         movl $0x7, %eax
114         movl $0x4000, %ecx
115         subl %edi, %edi
116 1:      movl %eax, %es:(%di)
117         addw $4, %di
118         addl $0x1000, %eax
119         loop 1b
120
121 # Set page directory base register.
122
123         movl $0xf000, %eax
124         movl %eax, %cr3
125
126 #### Switch to protected mode.
127
128 # First, disable interrupts.  We won't set up the IDT until we get
129 # into C code, so any interrupt would blow us away.
130
131         cli
132
133 # Protected mode requires a GDT, so point the GDTR to our GDT.
134 # We need a data32 prefix to ensure that all 32 bits of the GDT
135 # descriptor are loaded (default is to load only 24 bits).
136 # The CPU doesn't need an addr32 prefix but ELF doesn't do 16-bit
137 # relocations.
138
139         data32 addr32 lgdt gdtdesc - LOADER_PHYS_BASE - 0x20000
140
141 # Then we turn on the following bits in CR0:
142 #    PE (Protect Enable): this turns on protected mode.
143 #    PG (Paging): turns on paging.
144 #    WP (Write Protect): if unset, ring 0 code ignores
145 #       write-protect bits in page tables (!).
146 #    EM (Emulation): forces floating-point instructions to trap.
147 #       We don't support floating point.
148
149         movl %cr0, %eax
150         orl $CR0_PE | CR0_PG | CR0_WP | CR0_EM, %eax
151         movl %eax, %cr0
152
153 # We're now in protected mode in a 16-bit segment.  The CPU still has
154 # the real-mode code segment cached in %cs's segment descriptor.  We
155 # need to reload %cs, and the easiest way is to use a far jump.
156 # Because we're not running in a 32-bit segment the data32 prefix is
157 # needed to jump to a 32-bit offset in the target segment.
158
159         data32 ljmp $SEL_KCSEG, $1f
160
161 # We're now in protected mode in a 32-bit segment.
162 # Let the assembler know.
163
164         .code32
165
166 # Reload all the other segment registers and the stack pointer to
167 # point into our new GDT.
168
169 1:      mov $SEL_KDSEG, %ax
170         mov %ax, %ds
171         mov %ax, %es
172         mov %ax, %fs
173         mov %ax, %gs
174         mov %ax, %ss
175         addl $LOADER_PHYS_BASE, %esp
176         movl $0, %ebp                   # Null-terminate main()'s backtrace
177
178 #### Call main().
179
180         call main
181
182 # main() shouldn't ever return.  If it does, spin.
183
184 1:      jmp 1b
185 .endfunc
186
187 #### GDT
188
189         .align 8
190 gdt:
191         .quad 0x0000000000000000        # Null segment.  Not used by CPU.
192         .quad 0x00cf9a000000ffff        # System code, base 0, limit 4 GB.
193         .quad 0x00cf92000000ffff        # System data, base 0, limit 4 GB.
194
195 gdtdesc:
196         .word   gdtdesc - gdt - 1       # Size of the GDT, minus 1 byte.
197         .long   gdt                     # Address of the GDT.
198
199 #### Physical memory size in 4 kB pages.  This is exported to the rest
200 #### of the kernel.
201 .globl init_ram_pages
202 init_ram_pages:
203         .long 0
204