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.
64 cli # BIOS might have enabled interrupts
65 add eax, 1024 # Total kB memory
69 1: shr eax, 2 # Total 4 kB pages
72 #### Create temporary page directory and page table and set page
73 #### directory base register.
75 # Create page directory at 64 kB and fill with zeroes.
76 mov ax, LOADER_PD_BASE / 16
83 # Add a PDE mapping both virtual addresses 0 and LOADER_PHYS_BASE
85 # See [IA32-v3] section 3.7.6 for a description of the bits in eax.
87 mov eax, LOADER_PT_BASE / 16 + 7
89 mov es:[LOADER_PHYS_BASE / 1024 / 1024], eax
91 # Set up one-to-map linear to physical map for the first 4 MB of RAM.
92 # See [IA32-v3] section 3.7.6 for a description of the bits in eax.
94 mov ax, LOADER_PT_BASE / 16
102 # Set page directory base register.
104 mov eax, LOADER_PD_BASE
107 #### Switch to protected mode.
109 # Then we point the GDTR to our GDT. Protected mode requires a GDT.
110 # We need a data32 prefix to ensure that all 32 bits of the GDT
111 # descriptor are loaded (default is to load only 24 bits).
115 # Then we turn on the following bits in CR0:
116 # PE (Protect Enable): this turns on protected mode.
117 # PG (Paging): turns on paging.
118 # WP (Write Protect): if unset, ring 0 code ignores
119 # write-protect bits in page tables (!).
120 # EM (Emulation): forces floating-point instructions to trap.
121 # We don't support floating point.
123 #define CR0_PE 0x00000001
124 #define CR0_EM 0x00000004
125 #define CR0_PG 0x80000000
126 #define CR0_WP 0x00010000
129 or eax, CR0_PE + CR0_PG + CR0_WP + CR0_EM
132 # We're now in protected mode in a 16-bit segment. The CPU still has
133 # the real-mode code segment cached in cs's segment descriptor. We
134 # need to reload cs, and the easiest way is to use a far jump.
135 # Because we're not in a 32-bit segment the data32 prefix is needed to
136 # jump to a 32-bit offset.
138 data32 ljmp SEL_KCSEG, 1f + LOADER_PHYS_BASE
140 # We're now in protected mode in a 32-bit segment.
144 # Reload all the other segment registers and the stack pointer to
145 # point into our new GDT.
153 add esp, LOADER_PHYS_BASE
155 #### Jump to kernel entry point.
164 .quad 0x0000000000000000 # null seg
165 .quad 0x00cf9a000000ffff # code seg
166 .quad 0x00cf92000000ffff # data seg
169 .word 0x17 # sizeof (gdt) - 1
170 .long gdt + LOADER_PHYS_BASE # address gdt