| 1 | #include "asm.h" |
| 2 | #include "memlayout.h" |
| 3 | #include "mmu.h" |
| 4 | |
| 5 | # Start the first CPU: switch to 32-bit protected mode, jump into C. |
| 6 | # The BIOS loads this code from the first sector of the hard disk into |
| 7 | # memory at physical address 0x7c00 and starts executing in real mode |
| 8 | # with %cs=0 %ip=7c00. |
| 9 | |
| 10 | .code16 # Assemble for 16-bit mode |
| 11 | .globl start |
| 12 | start: |
| 13 | cli # BIOS enabled interrupts; disable |
| 14 | |
| 15 | # Zero data segment registers DS, ES, and SS. |
| 16 | xorw %ax,%ax # Set %ax to zero |
| 17 | movw %ax,%ds # -> Data Segment |
| 18 | movw %ax,%es # -> Extra Segment |
| 19 | movw %ax,%ss # -> Stack Segment |
| 20 | |
| 21 | # Physical address line A20 is tied to zero so that the first PCs |
| 22 | # with 2 MB would run software that assumed 1 MB. Undo that. |
| 23 | seta20.1: |
| 24 | inb $0x64,%al # Wait for not busy |
| 25 | testb $0x2,%al |
| 26 | jnz seta20.1 |
| 27 | |
| 28 | movb $0xd1,%al # 0xd1 -> port 0x64 |
| 29 | outb %al,$0x64 |
| 30 | |
| 31 | seta20.2: |
| 32 | inb $0x64,%al # Wait for not busy |
| 33 | testb $0x2,%al |
| 34 | jnz seta20.2 |
| 35 | |
| 36 | movb $0xdf,%al # 0xdf -> port 0x60 |
| 37 | outb %al,$0x60 |
| 38 | |
| 39 | # Switch from real to protected mode. Use a bootstrap GDT that makes |
| 40 | # virtual addresses map directly to physical addresses so that the |
| 41 | # effective memory map doesn't change during the transition. |
| 42 | lgdt gdtdesc |
| 43 | movl %cr0, %eax |
| 44 | orl $CR0_PE, %eax |
| 45 | movl %eax, %cr0 |
| 46 | |
| 47 | //PAGEBREAK! |
| 48 | # Complete the transition to 32-bit protected mode by using a long jmp |
| 49 | # to reload %cs and %eip. The segment descriptors are set up with no |
| 50 | # translation, so that the mapping is still the identity mapping. |
| 51 | ljmp $(SEG_KCODE<<3), $start32 |
| 52 | |
| 53 | .code32 # Tell assembler to generate 32-bit code now. |
| 54 | start32: |
| 55 | # Set up the protected-mode data segment registers |
| 56 | movw $(SEG_KDATA<<3), %ax # Our data segment selector |
| 57 | movw %ax, %ds # -> DS: Data Segment |
| 58 | movw %ax, %es # -> ES: Extra Segment |
| 59 | movw %ax, %ss # -> SS: Stack Segment |
| 60 | movw $0, %ax # Zero segments not ready for use |
| 61 | movw %ax, %fs # -> FS |
| 62 | movw %ax, %gs # -> GS |
| 63 | |
| 64 | # Set up the stack pointer and call into C. |
| 65 | movl $start, %esp |
| 66 | call bootmain |
| 67 | |
| 68 | # If bootmain returns (it shouldn't), trigger a Bochs |
| 69 | # breakpoint if running under Bochs, then loop. |
| 70 | movw $0x8a00, %ax # 0x8a00 -> port 0x8a00 |
| 71 | movw %ax, %dx |
| 72 | outw %ax, %dx |
| 73 | movw $0x8ae0, %ax # 0x8ae0 -> port 0x8a00 |
| 74 | outw %ax, %dx |
| 75 | spin: |
| 76 | jmp spin |
| 77 | |
| 78 | # Bootstrap GDT |
| 79 | .p2align 2 # force 4 byte alignment |
| 80 | gdt: |
| 81 | SEG_NULLASM # null seg |
| 82 | SEG_ASM(STA_X|STA_R, 0x0, 0xffffffff) # code seg |
| 83 | SEG_ASM(STA_W, 0x0, 0xffffffff) # data seg |
| 84 | |
| 85 | gdtdesc: |
| 86 | .word (gdtdesc - gdt - 1) # sizeof(gdt) - 1 |
| 87 | .long gdt # address gdt |
| 88 | |
| 89 | |