]> pd.if.org Git - zos/blob - boot32.s
early bootstrap assembly code
[zos] / boot32.s
1 %macro zeropage32 1
2         mov edi, %1
3         xor eax, eax
4         mov ecx, 1024 ; 1024 times for 4 byte stosd
5         rep stosd
6 %endmacro
7
8 USE32
9
10 section .multiboot
11
12 global multiboot_magic:data
13 global multiboot_infoptr:data
14 global _multiboot_entry:function
15
16 _multiboot_entry:
17         jmp start32
18
19         ; multiboot header needs to be 4 byte aligned.
20         ; it would be anyway I think, but make it explicit
21         align 4
22
23 multiboot_magic:
24         dd 0x1BADB002 ; multiboot 1 magic
25         dd 0x00000007 ; flags: 4 KB aligned modules, memory and video informatino
26         dd -(0x1BADB002 + 0x00000007) ; checksum
27 multiboot_infoptr:
28         dd 0 ; a place to store the multiboot info pointer until the kernel gets it
29
30 section .text
31
32 extern kernel_bootstrap
33
34 PML4ADDR equ 0x10000
35
36 align 4
37 start32:
38         ; boot loader passes multiboot info in ebx and eax
39         mov [multiboot_infoptr], ebx
40         mov [multiboot_magic], eax
41
42         ; set up initial page tables
43         ; We could also just use 1GB pages for now and let the
44         ; actual memory manager handle it later, we just need
45         ; these tables set up so that we can run the kernel
46         ; in the higher half.
47
48         ; free memory at this point is pretty much everything normally free
49         ; below 1 MB
50         ; 0x1000 - 0x7ffff is all free, plus 0x80000 - bottom of EBDA
51
52         ; map the first four megabytes of physical memory twice
53         ; once to identity map it, once to map it into the high kernel
54         ; virtual memory area for a higher half kernel
55
56         ; 0x10000 PML4 (512 GB/entry)
57         ; 0x11000 PDPT identity map (1 GB/entry)
58         ; 0x12000 PDT  identity map (2 MB/entry)
59         ; 0x13000 PT   identity map 0-2 MB (4 KB/entry)
60         ; 0x14000 PT   identity map 2-4 MB
61         ; 0x15000 PDPT kernel map for higher half (1 GB/entry)
62         ; 0x16000 PDT  kernel map for higher half (2 MB/entry)
63         ; 0x17000 PT   kernel map 0-2 MB
64         ; 0x18000 PT   kernel map 2-4 MB
65         ; 0x19000 1GB PDPT physical low 512 GB map
66         ; 0x20000-0x2ffff 64KB initial stack
67
68         ; zero the initial stack
69
70         mov edi, 0x20000
71         xor eax, eax
72         mov ecx, PML4ADDR
73         rep stosd
74
75         mov esp, 0x30000 ; set to top of stack
76
77         ; The 3 at the end of these marks the page as present and read/write
78
79         ; PML4
80         zeropage32 PML4ADDR
81         mov edi, PML4ADDR ; address of initial PML4
82         mov [edi], dword 0x11003 ; first entry, covers virtual 0-512 GB 
83         ; higher half map
84         mov [edi + 511 * 8], dword 0x15003 ; last entry, covers upper virtual 512 GB
85
86         ; identity map PDPT
87         zeropage32 0x11000
88         mov edi, 0x11000
89         mov [edi], dword 0x12003 ; virtual 0-1 GB
90
91         ; higher half PDPT
92         zeropage32 0x15000
93         mov edi, 0x15000
94         ; 0xFFFFFFFF80000000 kernel is at here + 1 MB
95         mov [edi + 510 * 8], dword 0x16003 ; virtual -2 - -1 GB
96
97         ; page directory
98         zeropage32 0x12000
99         mov edi, 0x12000
100         mov [edi], dword 0x13003 ; first 2 MB
101         mov [edi+8], dword 0x14003 ; second 2 MB
102
103         ; higher half PDT
104         zeropage32 0x16000
105         mov edi, 0x16000
106         mov [edi], dword 0x017003
107         mov [edi+8], dword 0x018003
108
109         ; page table
110         ; map the first 4 MB
111
112         ; Don't need to zero-page these, because we're going
113         ; to fill them anyway
114         mov edi, 0x13000
115         mov eax, 0x17000
116         mov ebx, 0x3
117         mov ecx, 1024 ; i.e. two tables worth
118 .donext:
119         mov [edi], ebx
120         mov [eax], ebx
121         add ebx, 0x1000
122         add eax, 8
123         add edi, 8
124         loop .donext
125
126         mov eax, PML4ADDR
127         mov cr3, eax ; paging is off, so this is safe
128
129         ; enable PAE
130         mov eax, cr4
131         or eax, 0x20
132         mov cr4, eax
133
134         ; enable long mode
135         mov ecx, 0xC0000080
136         rdmsr
137         or eax, 0x100
138         wrmsr
139
140         ; enable paging and enter long mode (still 32-bit)
141         mov eax, cr0
142         or eax, 0x80000000
143         mov cr0, eax
144
145         ; Load the long mode GDT.
146         mov eax, GDT_load
147         lgdt [GDT_load]
148
149         ; set the SS register.  Probably not needed
150         mov ax, 0x18 ; 0x18 is 64 bit kernel data segment
151         mov ss, ax
152
153         ; jump to the 64 bit code segment
154         jmp 0x10:bootstrap64
155
156 USE64
157 align 8
158 bootstrap64:
159         ; TODO set up the higher half map here.  easier to do in 64 bit code
160
161         ; from the AMD manual...
162         ; System software must create at least one 64-bit TSS for use after
163         ; activating long mode, and it must execute the LTR instruction, in
164         ; 64-bit mode, to load the TR register with a pointer to the 64-bit TSS
165         ; that serves both 64-bit-mode programs and compatibility-mode
166         ; programs.
167
168         ; fill in the TSS selector with the address of the TSS
169         mov rax, qword TSS
170         mov rbx, qword TSS_selector
171         ; load up the address fragments
172         mov [rbx + 2], ax
173         shl rax, 16
174         mov [rbx + 4], al
175         shl rax, 8
176         mov [rbx + 7], al
177         shl rax, 8
178         mov [rbx + 8], eax
179
180         ; calculate the TSS segment offset
181         mov rax, GDT64
182         sub rbx, rax  ; rbx still holds the TSS_selector address
183         ; and load it
184         LTR bx
185
186         ; and jump to the start code
187         mov rax, qword kernel_bootstrap
188         jmp rax
189
190 USE32
191
192 section .data
193
194 global GDT64:data
195 global GDT_load:data
196 global GDT_tss:data
197
198 %define MAX_CPUS 2
199
200 ; 00 00 00 00
201 ; II GI A? II
202 ; G = -01- ---- so G = 2 always, unless you want to use the avl bit and make it 3
203 ; or G = 4 for a 32 bit segment
204 ; A = 1pl1 1CRA I don't think C matters, and RA should be ignored, so 8?
205 ; but in principle C == 1 for code? 
206 ; R == 1 (i.e +2 for read write).  Supposedly ignored, but should't hurt
207 ; so, 8 + 0 + 2 + 0 == non conforming read write
208 ; Thus 00 20 9A 00 for code
209 ;      00 20 F2 00 for user code
210
211 ; 00 L0 AC 00
212 ; L = 2 for 64 bit, 4 for 32 bit
213 ; A = 9 for kernel, F for user
214 ; C = A for code, 2 for data
215
216 ;summary
217 ; 0x00209A00 ; 64 bit kernel code
218 ; 0x00209200 ; 64 bit kernel data
219 ; 0x
220
221 GDT64:
222         dd      0,0
223         dd      0,0
224         dd      0x00000000, 0x00209A00  ; 0x10: 64-bit Kernel Code
225         dd      0x00000000, 0x00209200  ; 0x18: 64-bit Kernel Data
226         dd      0x00000000, 0x0040FA00  ; 0x20: 32-bit User Code, should be unused
227         dd      0x00000000, 0x0040F200  ; 0x28: 32-bit User Data
228         dd      0x00000000, 0x0020FA00  ; 0x30: 64-bit User Code
229         dd      0x00000000, 0x0000F200  ; 0x38: 64-bit User Data
230 TSS_selector:
231         times MAX_CPUS  dd      0, 0x00008900, 0, 0     ; 0x38+16*n: TSS 0
232 align 16
233 GDT_load:
234         dw GDT_load - GDT64 - 1 ; limit
235         dq GDT64                  ; base
236         dd 0                      ; pad
237
238 align 16
239 TSS: 
240         dd 0 ; ignored
241         dq 0 ; rsp for cpl 0
242         dq 0 ; rsp for cpl 1
243         dq 0 ; rsp for cpl 2
244         times 2 dd 0 ; ignored
245         times 7 dq 0 ; rsp for ist 1-7
246         times 2 dd 0 ; ignored
247         dd 0 ; ignored
248         dd 0 ; base address for io permission bitmap
249         
250 GDT_tss:
251         dq TSS_selector - GDT64
252