diff options
Diffstat (limited to 'pintos-progos/threads/start.S')
| -rw-r--r-- | pintos-progos/threads/start.S | 204 |
1 files changed, 204 insertions, 0 deletions
diff --git a/pintos-progos/threads/start.S b/pintos-progos/threads/start.S new file mode 100644 index 0000000..29ffa7a --- /dev/null +++ b/pintos-progos/threads/start.S | |||
| @@ -0,0 +1,204 @@ | |||
| 1 | #include "threads/loader.h" | ||
| 2 | |||
| 3 | #### Kernel startup code. | ||
| 4 | |||
| 5 | #### The loader (in loader.S) loads the kernel at physical address | ||
| 6 | #### 0x20000 (128 kB) and jumps to "start", defined here. This code | ||
| 7 | #### switches from real mode to 32-bit protected mode and calls | ||
| 8 | #### main(). | ||
| 9 | |||
| 10 | /* Flags in control register 0. */ | ||
| 11 | #define CR0_PE 0x00000001 /* Protection Enable. */ | ||
| 12 | #define CR0_EM 0x00000004 /* (Floating-point) Emulation. */ | ||
| 13 | #define CR0_PG 0x80000000 /* Paging. */ | ||
| 14 | #define CR0_WP 0x00010000 /* Write-Protect enable in kernel mode. */ | ||
| 15 | |||
| 16 | .section .start | ||
| 17 | |||
| 18 | # The following code runs in real mode, which is a 16-bit code segment. | ||
| 19 | .code16 | ||
| 20 | |||
| 21 | .func start | ||
| 22 | .globl start | ||
| 23 | start: | ||
| 24 | |||
| 25 | # The loader called into us with CS = 0x2000, SS = 0x0000, ESP = 0xf000, | ||
| 26 | # but we should initialize the other segment registers. | ||
| 27 | |||
| 28 | mov $0x2000, %ax | ||
| 29 | mov %ax, %ds | ||
| 30 | mov %ax, %es | ||
| 31 | |||
| 32 | # Set string instructions to go upward. | ||
| 33 | cld | ||
| 34 | |||
| 35 | #### Get memory size, via interrupt 15h function 88h (see [IntrList]), | ||
| 36 | #### which returns AX = (kB of physical memory) - 1024. This only | ||
| 37 | #### works for memory sizes <= 65 MB, which should be fine for our | ||
| 38 | #### purposes. We cap memory at 64 MB because that's all we prepare | ||
| 39 | #### page tables for, below. | ||
| 40 | |||
| 41 | movb $0x88, %ah | ||
| 42 | int $0x15 | ||
| 43 | addl $1024, %eax # Total kB memory | ||
| 44 | cmp $0x10000, %eax # Cap at 64 MB | ||
| 45 | jbe 1f | ||
| 46 | mov $0x10000, %eax | ||
| 47 | 1: shrl $2, %eax # Total 4 kB pages | ||
| 48 | addr32 movl %eax, init_ram_pages - LOADER_PHYS_BASE - 0x20000 | ||
| 49 | |||
| 50 | #### Enable A20. Address line 20 is tied low when the machine boots, | ||
| 51 | #### which prevents addressing memory about 1 MB. This code fixes it. | ||
| 52 | |||
| 53 | # Poll status register while busy. | ||
| 54 | |||
| 55 | 1: inb $0x64, %al | ||
| 56 | testb $0x2, %al | ||
| 57 | jnz 1b | ||
| 58 | |||
| 59 | # Send command for writing output port. | ||
| 60 | |||
| 61 | movb $0xd1, %al | ||
| 62 | outb %al, $0x64 | ||
| 63 | |||
| 64 | # Poll status register while busy. | ||
| 65 | |||
| 66 | 1: inb $0x64, %al | ||
| 67 | testb $0x2, %al | ||
| 68 | jnz 1b | ||
| 69 | |||
| 70 | # Enable A20 line. | ||
| 71 | |||
| 72 | movb $0xdf, %al | ||
| 73 | outb %al, $0x60 | ||
| 74 | |||
| 75 | # Poll status register while busy. | ||
| 76 | |||
| 77 | 1: inb $0x64, %al | ||
| 78 | testb $0x2, %al | ||
| 79 | jnz 1b | ||
| 80 | |||
| 81 | #### Create temporary page directory and page table and set page | ||
| 82 | #### directory base register. | ||
| 83 | |||
| 84 | # Create page directory at 0xf000 (60 kB) and fill with zeroes. | ||
| 85 | mov $0xf00, %ax | ||
| 86 | mov %ax, %es | ||
| 87 | subl %eax, %eax | ||
| 88 | subl %edi, %edi | ||
| 89 | movl $0x400, %ecx | ||
| 90 | rep stosl | ||
| 91 | |||
| 92 | # Add PDEs to point to page tables for the first 64 MB of RAM. | ||
| 93 | # Also add identical PDEs starting at LOADER_PHYS_BASE. | ||
| 94 | # See [IA32-v3a] section 3.7.6 "Page-Directory and Page-Table Entries" | ||
| 95 | # for a description of the bits in %eax. | ||
| 96 | |||
| 97 | movl $0x10007, %eax | ||
| 98 | movl $0x11, %ecx | ||
| 99 | subl %edi, %edi | ||
| 100 | 1: movl %eax, %es:(%di) | ||
| 101 | movl %eax, %es:LOADER_PHYS_BASE >> 20(%di) | ||
| 102 | addw $4, %di | ||
| 103 | addl $0x1000, %eax | ||
| 104 | loop 1b | ||
| 105 | |||
| 106 | # Set up page tables for one-to-map linear to physical map for the | ||
| 107 | # first 64 MB of RAM. | ||
| 108 | # See [IA32-v3a] section 3.7.6 "Page-Directory and Page-Table Entries" | ||
| 109 | # for a description of the bits in %eax. | ||
| 110 | |||
| 111 | movw $0x1000, %ax | ||
| 112 | movw %ax, %es | ||
| 113 | movl $0x7, %eax | ||
| 114 | movl $0x4000, %ecx | ||
| 115 | subl %edi, %edi | ||
| 116 | 1: movl %eax, %es:(%di) | ||
| 117 | addw $4, %di | ||
| 118 | addl $0x1000, %eax | ||
| 119 | loop 1b | ||
| 120 | |||
| 121 | # Set page directory base register. | ||
| 122 | |||
| 123 | movl $0xf000, %eax | ||
| 124 | movl %eax, %cr3 | ||
| 125 | |||
| 126 | #### Switch to protected mode. | ||
| 127 | |||
| 128 | # First, disable interrupts. We won't set up the IDT until we get | ||
| 129 | # into C code, so any interrupt would blow us away. | ||
| 130 | |||
| 131 | cli | ||
| 132 | |||
| 133 | # Protected mode requires a GDT, so point the GDTR to our GDT. | ||
| 134 | # We need a data32 prefix to ensure that all 32 bits of the GDT | ||
| 135 | # descriptor are loaded (default is to load only 24 bits). | ||
| 136 | # The CPU doesn't need an addr32 prefix but ELF doesn't do 16-bit | ||
| 137 | # relocations. | ||
| 138 | |||
| 139 | data32 addr32 lgdt gdtdesc - LOADER_PHYS_BASE - 0x20000 | ||
| 140 | |||
| 141 | # Then we turn on the following bits in CR0: | ||
| 142 | # PE (Protect Enable): this turns on protected mode. | ||
| 143 | # PG (Paging): turns on paging. | ||
| 144 | # WP (Write Protect): if unset, ring 0 code ignores | ||
| 145 | # write-protect bits in page tables (!). | ||
| 146 | # EM (Emulation): forces floating-point instructions to trap. | ||
| 147 | # We don't support floating point. | ||
| 148 | |||
| 149 | movl %cr0, %eax | ||
| 150 | orl $CR0_PE | CR0_PG | CR0_WP | CR0_EM, %eax | ||
| 151 | movl %eax, %cr0 | ||
| 152 | |||
| 153 | # We're now in protected mode in a 16-bit segment. The CPU still has | ||
| 154 | # the real-mode code segment cached in %cs's segment descriptor. We | ||
| 155 | # need to reload %cs, and the easiest way is to use a far jump. | ||
| 156 | # Because we're not running in a 32-bit segment the data32 prefix is | ||
| 157 | # needed to jump to a 32-bit offset in the target segment. | ||
| 158 | |||
| 159 | data32 ljmp $SEL_KCSEG, $1f | ||
| 160 | |||
| 161 | # We're now in protected mode in a 32-bit segment. | ||
| 162 | # Let the assembler know. | ||
| 163 | |||
| 164 | .code32 | ||
| 165 | |||
| 166 | # Reload all the other segment registers and the stack pointer to | ||
| 167 | # point into our new GDT. | ||
| 168 | |||
| 169 | 1: mov $SEL_KDSEG, %ax | ||
| 170 | mov %ax, %ds | ||
| 171 | mov %ax, %es | ||
| 172 | mov %ax, %fs | ||
| 173 | mov %ax, %gs | ||
| 174 | mov %ax, %ss | ||
| 175 | addl $LOADER_PHYS_BASE, %esp | ||
| 176 | movl $0, %ebp # Null-terminate main()'s backtrace | ||
| 177 | |||
| 178 | #### Call main(). | ||
| 179 | |||
| 180 | call main | ||
| 181 | |||
| 182 | # main() shouldn't ever return. If it does, spin. | ||
| 183 | |||
| 184 | 1: jmp 1b | ||
| 185 | .endfunc | ||
| 186 | |||
| 187 | #### GDT | ||
| 188 | |||
| 189 | .align 8 | ||
| 190 | gdt: | ||
| 191 | .quad 0x0000000000000000 # Null segment. Not used by CPU. | ||
| 192 | .quad 0x00cf9a000000ffff # System code, base 0, limit 4 GB. | ||
| 193 | .quad 0x00cf92000000ffff # System data, base 0, limit 4 GB. | ||
| 194 | |||
| 195 | gdtdesc: | ||
| 196 | .word gdtdesc - gdt - 1 # Size of the GDT, minus 1 byte. | ||
| 197 | .long gdt # Address of the GDT. | ||
| 198 | |||
| 199 | #### Physical memory size in 4 kB pages. This is exported to the rest | ||
| 200 | #### of the kernel. | ||
| 201 | .globl init_ram_pages | ||
| 202 | init_ram_pages: | ||
| 203 | .long 0 | ||
| 204 | |||
