Small loader cleanups.
[pintos-anon] / src / threads / loader.S
1 #include "mmu.h"
2 #include "loader.h"
3         
4 ##############################################################################
5 # Kernel loader.
6 #
7 # This code should be stored in the first sector of the hard disk.  When the  
8 # BIOS runs, it loads this code at physical address 0x7c00-0x7e00 (512 bytes).
9 # Then it jumps to the beginning of it, in real mode.  
10 # This code switches into protected mode (32-bit mode) so that all of
11 # memory can accessed, loads the kernel into memory, and jumps to the
12 # first byte of the kernel, where start.S is linked.
13 ##############################################################################
14         
15 .globl start                            # Entry point   
16 start:  .code16                         # This runs in real mode
17         cli                             # Disable interrupts
18         cld                             # String ops inc
19         xorw %ax,%ax                    # Zero
20         movw %ax,%es                    # Address
21         movw %ax,%ds                    #  data
22         movw %ax,%ss                    # Set up
23         movw $start,%sp                 #  stack (grows down)
24         
25 #### Enable A20:
26 ####   Address line 20 is tied to low when the machine boots, 
27 ####   obviously this a bit of a drag, such as when trying to
28 ####   address memory above 1MB.  This code undoes this.
29         
30 1:      inb $0x64,%al           # Get status
31         testb $0x2,%al          # Busy?
32         jnz 1b                  # Yes
33         movb $0xd1,%al          # Command: Write
34         outb %al,$0x64          #  output port
35 2:      inb $0x64,%al           # Get status
36         testb $0x2,%al          # Busy?
37         jnz 2b                  # Yes
38         movb $0xdf,%al          # Enable
39         outb %al,$0x60          #  A20
40
41 #### Get memory size, via interrupt 15h function 88h.
42 #### Returns CF clear if successful, with AX = (kB of physical memory) - 1024.
43 #### This only works for memory sizes <= 65 MB, which should be fine for our purposes.
44         
45         movb $0x88,%ah
46         int $0x15
47         jc panic                # Carry flag set on error
48 2:      addl $1024,%eax         # Total kB
49         shrl $2,%eax            # Total 4 kB pages
50         movl %eax, ram_pages
51         
52 #### switch from real to protected mode 
53 ####     The segments in GDT allow all of physical memory to be accessed.
54 ####     Furthermore, the segments have base addresses of 0, so that the
55 ####     segment translation is a NOP, ie. virtual addresses are identical to
56 ####     their physical addresses.  With this setup, it appears to this code
57 ####     that it is running directly on physical memory.
58         
59         cli                     # Mandatory since we dont set up an IDT
60         lgdt gdtdesc            # load GDT -- mandatory in protected mode
61         movl %cr0, %eax         # turn on protected mode
62         orl $CR0_PE, %eax       # 
63         movl %eax, %cr0         # 
64         ### CPU magic: jump to relocation, flush prefetch queue, and
65         ### reload %cs Has the effect of just jmp to the next
66         ### instruction, but simultaneous loads CS with
67         ### $PROT_MODE_CSEG.
68         ljmp $SEL_KCSEG, $protcseg
69         
70 #### We are in protected mode in a 32-bit segment (hence the .code32)
71 protcseg:
72         .code32
73         movw $SEL_KDSEG, %ax    # set up data segment registers
74         movw %ax, %ds           
75         movw %ax, %es           
76         movw %ax, %fs           
77         movw %ax, %gs           
78         movw %ax, %ss
79
80 #### Load kernel starting at physical address LOADER_KERN_BASE by
81 #### frobbing the IDE controller directly.
82
83         movl $1, %ebx
84         movl $LOADER_KERN_BASE, %edi
85 read_sector:
86
87         ### Poll status register while controller busy.
88         movl $0x1f7, %edx
89 1:      inb %dx, %al
90         testb $0x80, %al
91         jnz 1b
92
93         ### Read a single sector.
94         movl $0x1f2, %edx
95         movb $1, %al
96         outb %al, %dx
97
98         ### Sector number to write in low 28 bits.
99         ### LBA mode, device 0 in top 4 bits.
100         movl %ebx, %eax
101         andl $0x0fffffff, %eax
102         orl $0xe0000000, %eax
103
104         ### Dump %eax to ports 0x1f3...0x1f6.
105         movl $4, %ecx
106 2:      incw %dx
107         outb %al, %dx
108         shrl $8, %eax
109         loop 2b
110
111         ### READ command to command register.
112         incw %dx
113         movb $0x20, %al
114         outb %al, %dx
115
116         ### Poll status register while controller busy.
117 1:      inb %dx, %al
118         testb $0x80, %al
119         jnz 1b
120
121         ### Poll status register until data ready.
122 1:      inb %dx, %al
123         testb $0x08, %al
124         jz 1b
125
126         ### Transfer sector.
127         movl $512 / 4, %ecx
128         movl $0x1f0, %edx
129         rep insl
130
131         ### Next sector.
132         incl %ebx
133         cmpl $KERNEL_LOAD_PAGES*8 + 1, %ebx
134         jnz read_sector
135
136 ##### Create temporary PDE and PTE, set page directory pointer, and turn
137 ##### on paging.
138 ##### FIXME? We could use a single 4 MB page instead of 1024 4 kB pages.
139
140         # Create PDE at 64 kB.
141         movl $0x10000, %edi             
142         movl %edi, %cr3
143
144         # Fill PDE with zeroes.
145         subl %eax, %eax
146         movl $0x400, %ecx
147         rep stosl
148
149         # Set PDE entries for 0 and LOADER_PHYS_BASE to point to the
150         # PTE.
151         movl $0x11000 | PG_U | PG_W | PG_P, %eax
152         movl %eax, 0x10000
153         movl %eax, 0x10000 | (LOADER_PHYS_BASE >> 20)
154
155         # Initialize PTE.
156         movl $PG_U | PG_W | PG_P, %eax
157         movl $0x400, %ecx
158 1:      stosl
159         addl $0x1000, %eax
160         loop 1b
161
162         # Enable paging.
163         movl %cr0, %eax
164         orl $CR0_PG, %eax
165         movl %eax, %cr0
166         jmp 1f
167 1:
168
169 ##### Turn on EM bit in CR0, forcing most floating-point instructions
170 ##### to trap.  We don't support floating-point or MMX.
171
172         movl %cr0, %eax
173         orl $CR0_EM, %eax
174         movl %eax, %cr0
175         
176 ##### Jump to kernel entry point.
177
178         movl $LOADER_PHYS_BASE + LOADER_BASE, %esp
179         movl $LOADER_PHYS_BASE + LOADER_KERN_BASE, %eax
180         jmp *%eax
181
182 ##### GDT
183
184 gdt:
185         .quad 0x0000000000000000        # null seg
186         .quad 0x00cf9a000000ffff        # code seg
187         .quad 0x00cf92000000ffff        # data seg
188         
189 gdtdesc:
190         .word   0x17                    # sizeof (gdt) - 1
191         .long   gdt                     # address gdt
192
193 ##### To panic, we print panicmsg (with help from the BIOS) and spin.
194 panic:  .code16                 # We only panic in real mode.
195         movw $panicmsg, %si
196         movb $0xe, %ah
197         xorb %bh, %bh
198 1:      lodsb
199         test %al, %al
200 2:      jz 2b                   # Spin.
201         int $0x10
202         jmp 1b
203
204 panicmsg:
205         .ascii "Loader panic!\r\n"
206         .byte 0
207
208 ##### Memory size in 4 kB pages.
209         .org LOADER_RAM_PAGES - LOADER_BASE
210 ram_pages:
211         .long 0
212
213 ##### Command-line arguments inserted by another utility.
214 ##### The loader doesn't use these, but we note their
215 ##### location here for easy reference.
216         .org LOADER_CMD_LINE - LOADER_BASE
217 cmd_line:
218         .fill 0x80, 1, 0
219
220 ##### Boot-sector signature for BIOS inspection.
221         .org LOADER_BIOS_SIG - LOADER_BASE
222         .word 0xaa55