1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
|
.code32
.extern begin_long_mode
.set KERNEL_VMA, 0xc0000000
.section .boot.text, "a"
.global _start
_start:
cli
mov $stack_top - KERNEL_VMA, %esp
pushl $0
pushl %eax
pushl $0
pushl %ebx
call check_multiboot
call check_cpuid
call check_long_mode
call setup_page_tables
call enable_paging
addl $KERNEL_VMA, %esp
mov $4f, %ecx
jmp *%ecx
check_multiboot:
cmp $0x36d76289, %eax
jne no_multiboot
ret
check_cpuid:
pushfl
pop %eax
mov %eax, %ecx
xor $1<<21, %eax
push %eax
popfl
pushfl
pop %eax
push %ecx
popfl
cmp %eax, %ecx
je no_cpuid
ret
check_long_mode:
mov $0x80000000, %eax
cpuid
cmp $0x80000001, %eax
jb no_long_mode
mov $0x80000001, %eax
cpuid
test $1<<29, %edx
jz no_long_mode
ret
setup_page_tables:
mov $pt_lvl3 - KERNEL_VMA, %eax
or $0x3, %eax
mov %eax, pt_lvl4 - KERNEL_VMA
// 0x00000000 - 0x00200000 : 0x00000000 - 0x00200000
mov $pt_lvl2 - KERNEL_VMA, %eax
or $0x3, %eax
mov %eax, pt_lvl3 - KERNEL_VMA
// 0xc0000000 - 0xc0200000 : 0xc0000000 - 0xc0200000
mov $pt_lvl2_hh - KERNEL_VMA, %eax
or $0x3, %eax
mov %eax, pt_lvl3 + 24 - KERNEL_VMA
// first 2mb
xor %ecx, %ecx
1:
movl $0x00200000, %eax
mul %ecx
or $0b10000011, %eax
movl $pt_lvl2 - KERNEL_VMA, %edx
leal (%edx, %ecx, 8), %edx
movl %eax, (%edx)
inc %ecx
cmp $1, %ecx
jne 1b
// first 2mb in higher_half
xor %ecx, %ecx
2:
movl $0x00200000, %eax
mul %ecx
or $0b10000011, %eax
movl $pt_lvl2_hh - KERNEL_VMA, %edx
leal (%edx, %ecx, 8), %edx
movl %eax, (%edx)
inc %ecx
cmp $1, %ecx
jne 2b
ret
enable_paging:
// enable PAE
mov %cr4, %edx
or $1<<5 ,%edx
mov %edx, %cr4
// set LME (long mode enable)
mov $0xC0000080, %ecx
rdmsr
or $1<<8, %eax
wrmsr
// pt_lvl4
mov $pt_lvl4 - KERNEL_VMA, %eax
mov %eax, %cr3
// enable paging (+ protected mode if not already enabled)
mov %cr0, %eax
or $1<<31 + 1<<0, %eax
mov %eax, %cr0
ret
no_multiboot:
hlt
no_cpuid:
hlt
no_long_mode:
hlt
.section .text
4:
lgdt gdtp
ljmp $0x08, $begin_long_mode
.section .bss
.align 4096
// stack
stack_bottom:
.skip 4096*16
stack_top:
// page tables
pt_lvl4:
.skip 4096
pt_lvl3:
.skip 4096
pt_lvl2:
.skip 4096
pt_lvl2_hh:
.skip 4096
// Access bits
.section .rodata
.set PRESENT, 1 << 7
.set NOT_SYS, 1 << 4
.set EXEC, 1 << 3
.set DC, 1 << 2
.set RW, 1 << 1
.set ACCESSED, 1 << 0
// Flags bits
.set GRAN_4K, 1 << 7
.set SZ_32, 1 << 6
.set LONG_MODE, 1 << 5
gdt:
gdt_null = . - gdt
.quad 0
gdt_code = . - gdt
.long 0xFFFF // Limit & Base (low, bits 0-15)
.byte 0 // Base (mid, bits 16-23)
.byte PRESENT | NOT_SYS | EXEC | RW // Access
.byte GRAN_4K | LONG_MODE | 0xF // Flags & Limit (high, bits 16-19)
.byte 0 // Base (high, bits 24-31)
gdt_data = . - gdt
.long 0xFFFF
.byte 0
.byte PRESENT | NOT_SYS | RW
.byte GRAN_4K | SZ_32 | 0xF
.byte 0
gdtp:
.word . - gdt - 1
.quad gdt
|