summaryrefslogtreecommitdiffstats
path: root/threads/start.S
diff options
context:
space:
mode:
Diffstat (limited to 'threads/start.S')
-rw-r--r--threads/start.S204
1 files changed, 204 insertions, 0 deletions
diff --git a/threads/start.S b/threads/start.S
new file mode 100644
index 0000000..29ffa7a
--- /dev/null
+++ b/threads/start.S
@@ -0,0 +1,204 @@
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
23start:
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
471: 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
551: 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
661: 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
771: 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
1001: 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
1161: 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
1691: 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
1841: jmp 1b
185.endfunc
186
187#### GDT
188
189 .align 8
190gdt:
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
195gdtdesc:
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
202init_ram_pages:
203 .long 0
204