-
Notifications
You must be signed in to change notification settings - Fork 0
/
boot.asm
198 lines (172 loc) · 4.68 KB
/
boot.asm
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
195
196
197
198
global start
global gdt64.descriptor
global p4_table
global p3_table
global ist1_top
global ist2_top
global ist3_top
global ist4_top
global ist5_top
global ist6_top
global ist7_top
extern long_mode_start
section .rodata
gdt64:
dq 0 ; zero entry
.code: equ $ - gdt64 ; new
; Executable | Type | Present | 64-bit Code Segment
dq (1<<43) | (1<<44) | (1<<47) | (1<<53) ; code segment
.descriptor:
dw $ - gdt64 - 1
dq gdt64
section .bss
align 4096
p4_table: ; Page-Map Level-4 Table (PML4)
resb 4096
p3_table: ; Page-Directory Pointer Table (PDP)
resb 4096
p2_table: ; Page-Directory Table (PD)
resb 4096
p1_table: ; Page Table (PT)
resb 4096
stack_bottom:
resb 4096
stack_top:
ist1_bottom:
resb 4096
ist1_top:
ist2_bottom:
resb 4096
ist2_top:
ist3_bottom:
resb 4096
ist3_top:
ist4_bottom:
resb 4096
ist4_top:
ist5_bottom:
resb 4096
ist5_top:
ist6_bottom:
resb 4096
ist6_top:
ist7_bottom:
resb 4096
ist7_top:
section .text
bits 32
start:
mov esp, stack_top
mov edi, ebx ; Save multiboot2 tag pointer
; Checks and paging are written using code from
; https://os.phil-opp.com/entering-longmode/
; https://wiki.osdev.org/Setting_Up_Long_Mode#x86_or_x86-64
call check_multiboot
call check_cpuid
call check_long_mode
call setup_page_tables
call enable_paging
; print `OK` to screen
mov dword [0xb8000], 0x2f4b2f4f
lgdt [gdt64.descriptor] ; load the 64-bit GDT
jmp gdt64.code:long_mode_start
hlt
check_multiboot:
cmp eax, 0x36d76289
jne .no_multiboot
ret
.no_multiboot:
mov al, "0"
jmp error
check_cpuid:
; Check if CPUID is supported by attempting to flip the ID bit (bit 21)
; in the FLAGS register. If we can flip it, CPUID is available.
; Copy FLAGS in to EAX via stack
pushfd
pop eax
; Copy to ECX as well for comparing later on
mov ecx, eax
; Flip the ID bit
xor eax, 1 << 21
; Copy EAX to FLAGS via the stack
push eax
popfd
; Copy FLAGS back to EAX (with the flipped bit if CPUID is supported)
pushfd
pop eax
; Restore FLAGS from the old version stored in ECX (i.e. flipping the
; ID bit back if it was ever flipped).
push ecx
popfd
; Compare EAX and ECX. If they are equal then that means the bit
; wasn't flipped, and CPUID isn't supported.
cmp eax, ecx
je .no_cpuid
ret
.no_cpuid:
mov al, "1"
jmp error
check_long_mode:
; test if extended processor info in available
mov eax, 0x80000000 ; implicit argument for cpuid
cpuid ; get highest supported argument
cmp eax, 0x80000001 ; it needs to be at least 0x80000001
jb .no_long_mode ; if it's less, the CPU is too old for long mode
; use extended info to test if long mode is available
mov eax, 0x80000001 ; argument for extended processor info
cpuid ; returns various feature bits in ecx and edx
test edx, 1 << 29 ; test if the LM-bit is set in the D-register
jz .no_long_mode ; If it's not set, there is no long mode
ret
.no_long_mode:
mov al, "2"
jmp error
; Set up 1GiB identity mapped page
; 1 P4, P3, & P2 table with P2 Huge bit set => 512 2MiB pages
setup_page_tables:
; Map first P4 entry to P3 table
mov eax, p3_table
or eax, 0b11 ; present + writable
mov [p4_table], eax
; Map first P3 entry to P2 table
mov eax, p2_table
or eax, 0b11 ; present + writable
mov [p3_table], eax
; map each P2 entry to a huge 2MiB page
mov ecx, 0
.map_p2_table:
; map ecx-th P2 entry to a huge page that starts at address 2MiB*ecx
mov eax, 0x200000 ; 2MiB
mul ecx ; start address of ecx-th page
or eax, 0b10000011 ; present + writable + huge
mov [p2_table + ecx * 8], eax ; map ecx-th entry (each entry is 8 bytes)
inc ecx
cmp ecx, 512
jne .map_p2_table
ret
enable_paging:
; load P4 to cr3 register (cpu uses this to access the P4 table)
mov eax, p4_table
mov cr3, eax
; enable PAE-flag in cr4 (Physical Address Extension)
mov eax, cr4
or eax, 1 << 5
mov cr4, eax
; set the long mode bit in the EFER MSR (model specific register)
mov ecx, 0xC0000080
rdmsr
or eax, 1 << 8
wrmsr
; enable paging in the cr0 register
mov eax, cr0
or eax, 1 << 31
mov cr0, eax
ret
; Prints `ERR: ` and the given error code to the screen an d hangs.
; parameter: error code (in ascii) in al
error:
mov dword [0xb8000], 0x4f524f45
mov dword [0xb8004], 0x4f3a4f52
mov dword [0xb8008], 0x4f204f20
mov byte [0xb800a], al
hlt