Serenity Operating System
1#include <AK/Platform.h>
2#include <Kernel/Prekernel/Prekernel.h>
3
4.code32
5
6.section .stack, "aw", @nobits
7stack_bottom:
8.skip 32768
9stack_top:
10
11.global kernel_cmdline
12kernel_cmdline:
13.skip 4096
14
15.section .page_tables, "aw", @nobits
16.align 4096
17#if ARCH(X86_64)
18.global boot_pml4t
19boot_pml4t:
20.skip 4096
21#endif
22.global boot_pdpt
23boot_pdpt:
24.skip 4096
25.global boot_pd0
26boot_pd0:
27.skip 4096
28.global boot_pd0_pts
29boot_pd0_pts:
30.skip 4096 * (MAX_KERNEL_SIZE >> 21)
31.global boot_pd_kernel
32boot_pd_kernel:
33.skip 4096
34.global boot_pd_kernel_pt0
35boot_pd_kernel_pt0:
36.skip 4096
37.global boot_pd_kernel_image_pts
38boot_pd_kernel_image_pts:
39.skip 4096 * (MAX_KERNEL_SIZE >> 21)
40.global boot_pd_kernel_pt1023
41boot_pd_kernel_pt1023:
42.skip 4096
43
44.section .text
45
46.global start
47.type start, @function
48
49.extern init
50.type init, @function
51
52.global reload_cr3
53.type reload_cr3, @function
54
55.extern multiboot_info_ptr
56.type multiboot_info_ptr, @object
57
58/*
59 construct the following (64-bit PML4T) page table layout:
60 (the PML4T part is not used for 32-bit x86)
61
62pml4t:
63
64 0: pdpt (0-512GB)
65
66pdpt
67
68 0: boot_pd0 (0-1GB)
69 1: n/a (1-2GB)
70 2: n/a (2-3GB)
71 3: n/a (3-4GB)
72
73boot_pd0 : 512 PDEs
74
75 boot_pd0_pts (0MB - MAX_KERNEL_SIZE) (id 512 4KB pages)
76
77the page tables each contain 512 PTEs that map individual 4KB pages
78
79*/
80
81gdt64:
82 .quad 0
83gdt64code:
84 .quad (1<<43) | (1<<44) | (1<<47) | (1<<53) /* executable, code segment, present, 64-bit */
85.global gdt64ptr
86gdt64ptr:
87 .short . - gdt64 - 1
88 .quad gdt64
89
90.set code64_sel_value, gdt64code - gdt64
91
92.global code64_sel
93code64_sel:
94.short code64_sel_value
95
96start:
97 jmp real_start
98
99/*
100 param 1: pointer to C string
101 returns: Length of string (including null byte)
102*/
103print_no_halt:
104 pushl %ebp
105 movl %esp, %ebp
106
107 pushl %esi
108 pushl %ecx
109
110 movl 8(%ebp), %esi
111
112 mov $0xb8000, %ecx /* VRAM address. */
113 mov $0x07, %ah /* grey-on-black text. */
114
115.Lprint_str_loop:
116 lodsb /* Loads a byte from address at %esi into %al and increments %esi. */
117
118 test %al, %al
119 jz .Lprint_str_end
120
121 movw %ax, (%ecx)
122 add $2, %ecx
123
124 jmp .Lprint_str_loop
125.Lprint_str_end:
126
127 mov %esi, %eax
128 sub 8(%ebp), %eax
129
130 popl %ecx
131 popl %esi
132
133 movl %ebp, %esp
134 popl %ebp
135 ret
136
137
138
139/*
140 this function assumes that paging is disabled (or everything is mapped 1:1)
141 param 1: pointer to string ended with null terminator (C string)
142*/
143print_and_halt:
144
145/* from now on, we don't really care about booting because we are missing required CPU features such as PAE or long mode.
146 the flow from now is like so:
147 1. Copy all necessary parts to low memory section in RAM
148 2. Jump to that section
149 3. In that section we do:
150 a. exit protected mode to pure 16 bit real mode
151 b. load the "<missing feature> is not supported" String, call the BIOS print to screen service
152 c. halt
153*/
154
155.equ COPIED_STRING_LOCATION, 0x400
156.equ GDT_REAL_MODE_LOCATION, 0x45000
157.equ EXITING_PROTECTED_MODE_CODE_LOCATION, 0x10000
158.equ REAL_MODE_CODE, 0x500
159.equ PROTECTED_MODE_16_BIT_CODE, 0x600
160 movl %esp, %ebp
161 movl 4(%ebp), %esi
162
163 /* Print string using non-destructive methods */
164 pushl %esi
165 call print_no_halt
166 addl $4, %esp
167
168 /* print_no_halt returns the string length (including null byte) in eax. */
169 mov %eax, %ecx
170 movw %cx, (COPIED_STRING_LOCATION) /* Store string length for later use. */
171
172 /* Copy string into lower memory */
173 mov 4(%ebp), %esi
174 mov $COPIED_STRING_LOCATION + 2, %edi
175 rep movsb
176
177 /* Copy gdt_table_real_mode to low memory section */
178 movl $gdt_table_real_mode, %eax
179 movl $gdt_table_real_mode_end, %ebx
180
181 movl %ebx, %ecx
182 sub %eax, %ecx
183 mov %eax, %esi /* source address of the code */
184 mov $GDT_REAL_MODE_LOCATION, %edi /* destination address of the code */
185 rep movsb
186
187 /* Copy protected_mode_16_bit to real_mode to low memory section */
188 movl $protected_mode_16_bit, %eax
189 movl $real_mode, %ebx
190
191 movl %ebx, %ecx
192 sub %eax, %ecx
193 mov %eax, %esi /* source address of the code */
194 mov $PROTECTED_MODE_16_BIT_CODE, %edi /* destination address of the code */
195 rep movsb
196
197 /* Copy real_mode to end_of_print_and_halt_function to low memory section */
198 movl $real_mode, %eax
199 movl $end_of_print_and_halt_function, %ebx
200
201 movl %ebx, %ecx
202 sub %eax, %ecx
203 mov %eax, %esi /* source address of the code */
204 mov $REAL_MODE_CODE, %edi /* destination address of the code */
205 rep movsb
206
207
208 /* Copy all opcodes from exiting_real_mode label to protected_mode_16_bit label to low memory RAM */
209 movl $exiting_real_mode, %eax
210 movl $protected_mode_16_bit, %ebx
211
212 movl %ebx, %ecx
213 sub %eax, %ecx
214 mov %eax, %esi /* source address of the code */
215 mov $EXITING_PROTECTED_MODE_CODE_LOCATION, %edi /* destination address of the code */
216 pushl %edi
217 rep movsb
218 popl %edi
219 pushl %edi
220 ret
221
222gdt_table_real_mode:
223 .quad 0 /* Empty entry */
224
225 .short 0xffff
226 .short 0
227 .byte 0
228 .byte 0b10011010
229 .byte 0b00001111
230 .byte 0x0
231
232 .short 0xffff
233 .short 0
234 .byte 0
235 .byte 0b10010010
236 .byte 0b00001111
237 .byte 0x0
238gdt_table_real_mode_end:
239
240no_long_mode_string:
241 .asciz "Your computer does not support long mode (64-bit mode). Halting!"
242
243no_pae_string:
244 .asciz "Your computer does not support PAE. Halting!"
245
246pentium_m_forcepae_string:
247 .asciz "Intel Pentium M detected. Assuming present but unadvertised PAE support."
248
249kernel_image_too_big_string:
250 .asciz "Error: Kernel Image too big for memory slot. Halting!"
251
252/*
253 This part is completely standalone - it doesn't involve any location from this
254 near code. It uses arbitrary locations in the low memory section of the RAM.
255 We don't really worry about where are these locations, because we only want to quickly
256 print a string and halt.
257*/
258.code32
259exiting_real_mode:
260
261 /* Build IDT pointer and load it */
262 mov $0x50000, %eax
263 pushl %eax
264 movl $0x3ff, 0(%eax)
265 add $2, %eax
266 movl $0, 0(%eax)
267 popl %eax
268 lidt (%eax)
269
270 /* Build GDT pointer and load it */
271 mov $0x40000, %eax
272 pushl %eax
273 movl $32, 0(%eax)
274 add $2, %eax
275 movl $GDT_REAL_MODE_LOCATION, 0(%eax)
276 popl %eax
277 lgdt (%eax)
278
279 /* far jump to protected_mode_16_bit in 0x5000 */
280 pushw $8
281 push $PROTECTED_MODE_16_BIT_CODE
282 lret
283 hlt
284
285.code16
286protected_mode_16_bit:
287 xor %eax, %eax
288 movl $0x10, %eax
289 movw %ax, %ds
290 and $0xFE, %al /* switch to pure real mode */
291 mov %eax, %cr0
292 mov $0x10, %eax
293 movl %eax, %cr0
294
295 pushw $0
296 push $REAL_MODE_CODE
297 lret
298 hlt
299
300real_mode:
301 movw $0x7000, %ax
302 movl $0x0000, %esp
303 movw %ax, %ss
304
305 xor %ax, %ax
306 movw %ax, %ds
307 movw %ax, %es
308 movw %ax, %fs
309 movw %ax, %gs
310
311 mov $0x3, %ax
312 int $0x10
313
314 movb $0x13, %ah
315 movb $0x0, %bh
316 movb $0xf, %bl
317 movw (COPIED_STRING_LOCATION), %cx
318 movw $0, %dx
319 movw $COPIED_STRING_LOCATION + 2, %bp
320 int $0x10
321
322 movl $0xdeadcafe, %ebx
323 cli
324 hlt
325end_of_print_and_halt_function:
326
327.code32
328real_start:
329 cli
330 cld
331 mov $end_of_prekernel_image, %esi
332 cmp $MAX_KERNEL_SIZE, %esi
333 jbe kernel_not_too_large
334
335 movl $kernel_image_too_big_string, %esi
336 pushl %esi
337 call print_and_halt
338 /* We should not return, but just in case, halt */
339 hlt
340
341kernel_not_too_large:
342 /* test for PAE presence, save the most important registers from corruption */
343 pushl %eax
344 pushl %edx
345 pushl %ebx
346
347 movl $0x1, %eax /* PAE presence is in CPUID input 0x1 */
348 cpuid
349 testl $(1 << 6), %edx /* Test if the PAE-bit, which is bit 6, is set in the edx register. */
350 jnz pae_supported /* If the bit is not set, there is no PAE capability. */
351
352 /* We might have a Pentium M, which supports PAE but doesn't advertise it. */
353 call is_pentium_m_with_hidden_pae
354 test %eax, %eax
355 jz .Lskip_forcepae
356
357 /* Print a warning message, but continue. */
358 pushl $pentium_m_forcepae_string
359 call print_no_halt
360 subl $4, %esp
361 jmp pae_supported
362.Lskip_forcepae:
363
364 /* Since there is no PAE capability, halt with an error message */
365 movl $no_pae_string, %esi
366 pushl %esi
367 call print_and_halt
368 /* We should not return, but just in case, halt */
369 hlt
370
371pae_supported:
372 movl $0x80000001, %eax
373 cpuid
374 testl $(1 << 29), %edx /* Test if the LM-bit, which is bit 29, is set in the edx register. */
375 jnz long_mode_supported /* If LM-bit is not enabled, there is no long mode. */
376
377 /* Since there is no long mode, halt with an error message */
378 movl $no_long_mode_string, %esi
379 pushl %esi
380 call print_and_halt
381 /* We should not return, but just in case, halt */
382 hlt
383
384
385/* If both PAE and long mode is supported, continue with booting the system */
386
387long_mode_supported:
388 /* restore the pushed registers and continue with booting */
389 popl %ebx
390 popl %edx
391 popl %eax
392
393 /* We don't know where the bootloader might have put the command line.
394 * It might be at an inconvenient location that we're not about to map,
395 * so let's just copy it to a convenient location while we have the whole
396 * memory space identity-mapped anyway. :^)
397 */
398
399 movl %ebx, %esi
400 addl $16, %esi
401 movl (%esi), %esi
402 movl $1024, %ecx
403 movl $kernel_cmdline, %edi
404 rep movsl
405
406 /* clear pml4t */
407 movl $boot_pml4t, %edi
408 movl $1024, %ecx
409 xorl %eax, %eax
410 rep stosl
411
412 /* set up pml4t[0] */
413 movl $boot_pml4t, %edi
414 movl $boot_pdpt, 0(%edi)
415 /* R/W + Present */
416 orl $0x3, 0(%edi)
417
418 /* clear pdpt */
419 movl $boot_pdpt, %edi
420 movl $1024, %ecx
421 xorl %eax, %eax
422 rep stosl
423
424 /* set up pdpt[0] and pdpt[3] */
425 movl $boot_pdpt, %edi
426 movl $(boot_pd0 + 3), 0(%edi)
427
428 /* clear pd0 */
429 movl $boot_pd0, %edi
430 movl $1024, %ecx
431 xorl %eax, %eax
432 rep stosl
433
434 /* clear pd0's PTs */
435 movl $boot_pd0_pts, %edi
436 movl $(1024 * (MAX_KERNEL_SIZE >> 21)), %ecx
437 xorl %eax, %eax
438 rep stosl
439
440 /* add boot_pd0_pts to boot_pd0 */
441 movl $(MAX_KERNEL_SIZE >> 21), %ecx
442 movl $boot_pd0, %edi
443 movl $boot_pd0_pts, %eax
444
4451:
446 movl %eax, 0(%edi)
447 /* R/W + Present */
448 orl $0x3, 0(%edi)
449 addl $8, %edi
450 addl $4096, %eax
451 loop 1b
452
453 /* identity map the 0MB to MAX_KERNEL_SIZE range */
454 movl $(512 * (MAX_KERNEL_SIZE >> 21)), %ecx
455 movl $boot_pd0_pts, %edi
456 xorl %eax, %eax
457
4581:
459 movl %eax, 0(%edi)
460 /* R/W + Present */
461 orl $0x3, 0(%edi)
462 addl $8, %edi
463 addl $4096, %eax
464 loop 1b
465
466 /* point CR3 to PML4T */
467 movl $boot_pml4t, %eax
468
469 movl %eax, %cr3
470
471 /* enable PAE + PSE */
472 movl %cr4, %eax
473 orl $0x60, %eax
474 movl %eax, %cr4
475
4761:
477 /* Enter Long-mode! ref(https://wiki.osdev.org/Setting_Up_Long_Mode)*/
478 mov $0xC0000080, %ecx /* Set the C-register to 0xC0000080, which is the EFER MSR.*/
479 rdmsr /* Read from the model-specific register.*/
480 or $(1 << 8), %eax /* Set the LM-bit which is the 9th bit (bit 8).*/
481 wrmsr /* Write to the model-specific register.*/
482
483 /* enable PG */
484 movl %cr0, %eax
485 orl $0x80000000, %eax
486 movl %eax, %cr0
487
488 /* set up stack */
489 mov $stack_top, %esp
490 and $-16, %esp
491
492 /* Now we are in 32-bit compatibility mode, We still need to load a 64-bit GDT */
493 mov $gdt64ptr, %eax
494 lgdt (%eax)
495 ljmpl $code64_sel_value, $1f
496
497.code64
4981:
499 movl %ebx, %ebx
500 movq %rbx, multiboot_info_ptr
501
502 mov $0, %ax
503 mov %ax, %ss
504 mov %ax, %ds
505 mov %ax, %es
506 mov %ax, %fs
507 mov %ax, %gs
508
509 call reload_cr3
510 call init
511
512 cli
513loop:
514 hlt
515 jmp loop
516
517reload_cr3:
518 pushq %rax
519 mov %cr3, %rax
520 mov %rax, %cr3
521 popq %rax
522 ret
523
524
525.code32
526
527/* Determines if the CPU is made by Intel */
528is_intel_cpu:
529 pushl %ebp
530 movl %esp, %ebp
531
532 pushl %ebx
533
534 xorl %eax, %eax
535 cpuid
536
537 xorl %eax, %eax
538 cmpl $0x756e6547, %ebx /* "Genu" */
539 jnz .Lis_intel_cpu_end
540 cmpl $0x49656e69, %edx /* "ineI" */
541 jnz .Lis_intel_cpu_end
542 cmpl $0x6c65746e, %ecx /* "ntel" */
543 jnz .Lis_intel_cpu_end
544
545 movl $1, %eax
546
547.Lis_intel_cpu_end:
548 popl %ebx
549
550 movl %ebp, %esp
551 popl %ebp
552
553 ret
554
555/* Fetches the CPU family (eax) and model (edx) */
556get_cpu_model_family:
557 pushl %ebp
558 movl %esp, %ebp
559
560 pushl %ebx
561
562 movl $0x1, %eax
563 cpuid
564 movl %eax, %ebx
565 movl %eax, %ecx
566 movl %eax, %edx
567
568 /* Bit 8 - 11: Processor Family */
569 shrl $8, %eax
570 andl $0xf, %eax
571
572 /* Bit 4 - 7: Processor Model */
573 shrl $4, %edx
574 andl $0xf, %edx
575
576 /* Bit 16 - 19: Extended Model ID */
577 /* (only applies if Family is 6 or 15) */
578 cmpl $6, %eax
579 jz .Ldo_ext_model
580 cmpl $15, %eax
581 jz .Ldo_ext_model
582 jmp .Lskip_ext_model
583.Ldo_ext_model:
584 shrl $16, %ebx
585 andl $0xf, %ebx
586 shll $4, %ebx
587 addl %ebx, %edx
588.Lskip_ext_model:
589
590 /* Bit 20 - 27: Extended Family */
591 /* (only applies if Family is 15) */
592 cmpl $15, %eax
593 jnz .Lskip_ext_family
594 shrl $20, %ecx
595 andl $0xff, %ecx
596 addl %ecx, %eax
597.Lskip_ext_family:
598
599 popl %ebx
600
601 movl %ebp, %esp
602 popl %ebp
603
604 ret
605
606/* Determines if the CPU is an Intel Pentium M with hidden PAE flag. */
607is_pentium_m_with_hidden_pae:
608 pushl %ebp
609 movl %esp, %ebp
610
611 /* Check the manufacturer string. */
612 call is_intel_cpu
613 testl %eax, %eax
614 jz .Lis_pentium_m_end
615
616 /* Check the processor model. */
617 call get_cpu_model_family
618 movl %eax, %ecx /* Free up eax for the return value. */
619 xorl %eax, %eax
620
621 cmpl $6, %ecx /* Family 6: Big Cores */
622 jnz .Lis_pentium_m_end
623
624 cmpl $9, %edx /* Model 9: Pentium M (Banias) */
625 jz .Lpass_model_check
626 cmpl $13, %edx /* Model 13: Pentium M (Dothan) */
627 jz .Lpass_model_check
628 jmp .Lis_pentium_m_end
629.Lpass_model_check:
630
631 /* We are a Pentium M. */
632 movl $1, %eax
633
634.Lis_pentium_m_end:
635 movl %ebp, %esp
636 popl %ebp
637
638 ret