aboutsummaryrefslogtreecommitdiff
path: root/arch/x86_64/src/boot/boot32.S
blob: 79b3ec7ff9e9830c166915f52bd2697e2b0caaf0 (plain)
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
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
#include "x86_64/boot/boot.hpp"

/**
 * @brief Uninitialized data for the bootstrapping process.
 */
.section .boot_bss, "aw", @nobits

/**
 * @brief Storage for the multiboot2 information pointer.
 */
.global multiboot_information_pointer
multiboot_information_pointer: .skip 8

.align 4096

page_maps_start:
page_map_level_4:      .skip 512 * 8
page_map_level_3_high: .skip 512 * 8
page_map_level_3_low:  .skip 512 * 8
page_map_level_2:      .skip 512 * 8
page_maps_end = .
page_maps_size = page_maps_end - page_maps_start

/**
 * @brief Storage for the bootstrap stack.
 */
.section .boot_stack, "aw", @nobits
.align 16

early_stack_bottom: .skip 1 << 8
early_stack_top:
early_stack_size = early_stack_top - early_stack_bottom

/**
 * @brief Constants for the bootstrapping process.
 */
.section .boot_rodata, "a", @progbits

.global global_descriptor_table_data

/**
 * @brief A basic GDT for long mode.
 */
global_descriptor_table:
global_descriptor_table_null = . - global_descriptor_table
.quad 0
global_descriptor_table_code = . - global_descriptor_table
.quad GDT_READ_WRITE | GDT_EXECUTABLE | GDT_DESCRIPTOR_TYPE | GDT_PRESENT | GDT_LONG_MODE | (1 << 55)
global_descriptor_table_data = . - global_descriptor_table
.quad GDT_READ_WRITE | GDT_DESCRIPTOR_TYPE | GDT_PRESENT | (1 << 54) | (1 << 55)
global_descriptor_table_end:

message_prefix_panic:                    .string  "Panic: "
message_not_loaded_by_multiboot2:        .string  "The operating system was not loaded by a Multiboot 2 loader."
message_cpuid_instruction_no_supported:  .string  "The 'cpuid' instruction is not supported on this platform."
message_long_mode_not_supported:         .string  "Long mode is not supported by this platform."
message_return_from_kernel_main:         .string  "Execution returned from kernel main."

/**
 * @brief Initialized data for the bootstrapping process.
 */
.section .boot_data, "aw", @progbits

/**
 * @brief A pointer to the current position within the VGA text buffer.
 */
.global vga_buffer_pointer
vga_buffer_pointer: .quad 0xb8000

/**
 * @brief Code for the bootstrapping process.
 */
.section .boot_text, "ax", @progbits
.align 16
.code32

.macro pie_base
    push %esi
    call 0f
    0:
    pop %esi
.endm

.macro function_start
    push %ebp
    mov  %esp, %ebp
.endm

.macro function_end
    leave
    ret
.endm

.macro pie_function_start
    function_start
    pie_base
.endm

.macro pie_function_end
    pop %esi
    function_end
.endm

/**
 * @brief Prepare the environment and start the kernel.
 *
 * This function performs all necessary checks to ensure the system was loaded
 * by the expected loader and supports all features required to run the kernel.
 * If successful, it prepares the system by setting up memory virtualization
 * and then start the kernel proper.
 *
 * @param %eax The Multiboot 2 magic marker.
 * @param %ebx The Multiboot 2 information pointer.
 * @return void This function does not return.
 */
.global _start
_start:
    call 0f
0:
    pop %esi

    lea (early_stack_top - 0b)(%esi), %ecx

    mov %ecx, %esp
    mov %esp, %ebp

    call _assert_loaded_by_multiboot2_loader
    call _save_multiboot_information_pointer

    call _assert_cpuid_instruction_is_supported
    call _assert_cpu_supports_long_mode

    push $HUGE_PAGES_TO_MAP
    call _prepare_page_maps
    add $4, %esp

    call _enable_paging
    call _enable_sse
    call _reload_gdt

    lea (_entry64 - 0b)(%esi), %eax
    pushl $global_descriptor_table_code
    pushl %eax
    lret

/**
 * @brief Halt the system.
 *
 * This function will instruct the CPU to halt. It will try to keep the CPU
 * halted, even if interrupts occur.
 *
 * @return This function never returns.
 */
_halt:
    function_start

1:
    hlt
    jmp 1b

    function_end

/**
 * @brief Print a message via the VGA text buffer.
 *
 * @param ebp+12 The message to print.
 * @param ebp+8  The color to print the message in.
 */
_print:
    pie_function_start

    push %edi
    push %ebx

    mov 8(%ebp), %al
    mov 12(%ebp), %edx
    mov $0, %ecx
    lea (vga_buffer_pointer - 0b)(%esi), %edi
    mov (%edi), %edi

1:
    mov (%edx, %ecx), %bl
    test %bl, %bl
    je 2f
    mov %bl, (%edi, %ecx, 2)
    mov %al, 1(%edi, %ecx, 2)
    inc %ecx
    jmp 1b

2:
    shl $1, %ecx
    add %ecx, %edi
    lea (vga_buffer_pointer - 0b)(%esi), %ecx
    mov %edi, (%ecx)

    pop %ebx
    pop %edi

    pie_function_end

/**
 * @brief Print a given panic message and then halt the machine as if by calling ::halt()
 *
 * @param ebp+4 A message to print.
 * @return This function does not return.
 */
_panic:
    pie_function_start

    lea (message_prefix_panic - 0b)(%esi), %eax

    push %eax
    push $0x4f
    call _print

    mov  16(%ebp), %eax
    mov  %eax,     8(%ebp)
    call _print
    add $8, %esp

    call _halt

    pie_function_end

/**
 * Assert that we were loaded by a Multiboot 2 compliant bootloader.
 *
 * This assertion will panic the system if the magic signature was not found.
 * If we were loaded my an appropriate bootloader, this function also saves
 * the provided MBI pointer to `multiboot_information_pointer`.
 */
_assert_loaded_by_multiboot2_loader:
    pie_function_start

    cmp $MULTIBOOT2_MAGIC, %eax
    je 1f
    lea (message_not_loaded_by_multiboot2 - 0b)(%esi), %eax
    push %eax
    call _panic
1:
    pie_function_end

/**
 * @brief Store the multiboot 2 information pointer in the global memory.
 *
 * @return void
 */
_save_multiboot_information_pointer:
    pie_function_start

    lea (multiboot_information_pointer - 0b)(%esi), %eax
    mov %ebx, (%eax)

    pie_function_end

/**
 * @brief Assert that the CPU supports the CPUID instruction.
 *
 * The primary way to check for support of the instruction is to flip the ID
 * bin in EFLAGS and then check if this changed was accepted. If so, the CPU
 * supports the CPUID instruction, otherwise it most-likely doesn't.
 */
_assert_cpuid_instruction_is_supported:
    pie_function_start

    pushfl
    pop %eax
    mov %eax, %ecx

    xor $(1 << 21), %eax  /* Flip the ID bit */
    push %eax             /* Move the new bitset on the stack for loading */
    popfl                 /* Load the flags with ID set back into EFLAGS */
    pushfl                /* Copy the flags back onto the stack */
    pop %eax              /* Load the flags for further checking */

    push %ecx
    popfl

    cmp %ecx, %eax
    jne 1f
    lea (message_cpuid_instruction_no_supported - 0b)(%esi), %eax
    push %eax
    call _panic

1:
    pie_function_end

/**
 * @brief Assert that the CPU supports going into long mode.
 */
_assert_cpu_supports_long_mode:
    pie_function_start

    mov $0x80000000, %eax
    cpuid
    cmp $0x80000001, %eax
    jb 1f

    mov $0x80000001, %eax
    cpuid
    test $(1 << 29), %edx
    jnz 2f
1:
    lea (message_long_mode_not_supported - 0b)(%esi), %eax
    push %eax
    call _panic
2:
    pie_function_end

/**
 * @brief Prepare a recursive page map hierarchy
 *
 * @param ebp+8 The number of huge pages to map
 * @return void
 */
_prepare_page_maps:
    pie_function_start

    push %edi

    call _clear_page_map_memory

    lea (page_map_level_4 - 0b)(%esi), %edi
    mov %edi, %eax
    or $0b11, %eax
    mov %eax, (510 * 8)(%edi)

    lea (page_map_level_3_low - 0b)(%esi), %eax
    or $0b11, %eax
    mov %eax, (%edi)

    lea (page_map_level_3_high - 0b)(%esi), %eax
    or $0b11, %eax
    mov %eax, (511 * 8)(%edi)

    lea (page_map_level_3_low - 0b)(%esi), %edi
    lea (page_map_level_2 - 0b)(%esi), %eax
    or $0b11, %eax
    mov %eax, (%edi)

    lea (page_map_level_3_high - 0b)(%esi), %edi
    lea (page_map_level_2 - 0b)(%esi), %eax
    or $0b11, %eax
    mov %eax, (510 * 8)(%edi)

    lea (page_map_level_2 - 0b)(%esi), %edi
    mov 8(%ebp), %ecx

1:
    dec %ecx
    mov $(1 << 21), %eax
    mul %ecx
    or $0b10000011, %eax
    mov %eax, (%edi, %ecx, 8)

    test %ecx, %ecx
    jnz 1b

    pop %edi

    pie_function_end

/**
 * @brief Clear all page map memory by filling it with 0s.
 *
 * @return void
 */
_clear_page_map_memory:
    pie_function_start

    push %edi

    xor %eax, %eax
    mov $page_maps_size, %ecx
    shr $2, %ecx
    lea (page_maps_start - 0b)(%esi), %edi
    rep stosl

    pop %edi

    pie_function_end

/**
 * @p Enable memory virtualization via paging.
 *
 * Note: This routine expects for there to be a valid set of page maps already
 * set up for use.
 *
 * @return void
 */
_enable_paging:
    pie_function_start

    lea (page_map_level_4 -