Serenity Operating System
at master 638 lines 14 kB view raw
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