1 #### The loader needs to have some way to know the kernel's entry
2 #### point, that is, the address to which it should jump to start the
3 #### kernel. We handle this by writing the linker script kernel.lds.S
4 #### so that this module appears at the very beginning of the kernel
5 #### image, and then using that as the entry point.
7 #include "threads/loader.h"
12 # Code runs in real mode, which is a 16-bit segment.
19 # String instructions go upward.
24 # Set up data segments.
30 #### Enable A20. Address line 20 is tied to low when the machine
31 #### boots, which prevents addressing memory about 1 MB. This code
34 # Poll status register while busy.
40 # Send command for writing output port.
45 # Poll status register while busy.
56 #### Get memory size, via interrupt 15h function 88h, which returns CF
57 #### clear if successful, with AX = (kB of physical memory) - 1024.
58 #### This only works for memory sizes <= 65 MB, which should be fine
59 #### for our purposes. We only reserve enough memory for page tables
60 #### for 64 MB of RAM, so we cap it at that value.
65 cli # BIOS might have enabled interrupts
66 add eax, 1024 # Total kB memory
70 1: shr eax, 2 # Total 4 kB pages
73 #### Create temporary page directory and page table and set page
74 #### directory base register.
76 # Create page directory at 64 kB and fill with zeroes.
77 mov ax, LOADER_PD_BASE / 16
84 # Add a PDE mapping both virtual addresses 0 and LOADER_PHYS_BASE
86 # See [IA32-v3] section 3.7.6 for a description of the bits in eax.
88 mov eax, LOADER_PT_BASE / 16 + 7
90 mov es:[LOADER_PHYS_BASE / 1024 / 1024], eax
92 # Set up one-to-map linear to physical map for the first 4 MB of RAM.
93 # See [IA32-v3] section 3.7.6 for a description of the bits in eax.
95 mov ax, LOADER_PT_BASE / 16
103 # Set page directory base register.
105 mov eax, LOADER_PD_BASE
108 #### Switch to protected mode.
110 # Then we point the GDTR to our GDT. Protected mode requires a GDT.
111 # We need a data32 prefix to ensure that all 32 bits of the GDT
112 # descriptor are loaded (default is to load only 24 bits).
116 # Then we turn on the following bits in CR0:
117 # PE (Protect Enable): this turns on protected mode.
118 # PG (Paging): turns on paging.
119 # WP (Write Protect): if unset, ring 0 code ignores
120 # write-protect bits in page tables (!).
121 # EM (Emulation): forces floating-point instructions to trap.
122 # We don't support floating point.
124 #define CR0_PE 0x00000001
125 #define CR0_EM 0x00000004
126 #define CR0_PG 0x80000000
127 #define CR0_WP 0x00010000
130 or eax, CR0_PE + CR0_PG + CR0_WP + CR0_EM
133 # We're now in protected mode in a 16-bit segment. The CPU still has
134 # the real-mode code segment cached in cs's segment descriptor. We
135 # need to reload cs, and the easiest way is to use a far jump.
136 # Because we're not in a 32-bit segment the data32 prefix is needed to
137 # jump to a 32-bit offset.
139 data32 ljmp SEL_KCSEG, 1f + LOADER_PHYS_BASE
141 # We're now in protected mode in a 32-bit segment.
145 # Reload all the other segment registers and the stack pointer to
146 # point into our new GDT.
154 add esp, LOADER_PHYS_BASE
156 #### Jump to kernel entry point.
165 .quad 0x0000000000000000 # null seg
166 .quad 0x00cf9a000000ffff # code seg
167 .quad 0x00cf92000000ffff # data seg
170 .word 0x17 # sizeof (gdt) - 1
171 .long gdt + LOADER_PHYS_BASE # address gdt
174 #### Print panicmsg (with help from the BIOS) and spin.
176 panic: .code16 # We only panic in real mode.
177 mov si, offset panicmsg