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