1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
|
; vi: ft=nasm
; Contains code from from the OSC lab project OOStuBS @ TU Dresden
; stack for the main function (renamed to _entry())
STACKSIZE: equ 65536
; 512 GB maximum RAM size for page table
; DON'T MODIFY THIS UNLESS YOU UPDATE THE setup_paging accordingly
MAX_MEM: equ 512
; be careful with the extern and exported symbols when mapping a higher-half
; kernel: regardless where they are physically loaded
; 1) extern symbols may have 64 bit virtual addresses or values. Do not use them
; in the 32bit part of the startup code.
; 2) if the exported (global) symbols are mapped to low (virtual) addresses,
; they would be no longer accessable after the kernel switch to a higher half
; mapping. This is especially true for the multiboot info data.
; exported symbols
[GLOBAL startup]
[GLOBAL mb_magic]
[GLOBAL mb_info_addr]
; functions from other parts of rustubs
[EXTERN _entry]
[EXTERN ___BSS_START__]
[EXTERN ___BSS_END__]
[SECTION .text]
; symbols used in 32bit mode:
; mb_magic
; mab_info_addr
; gdt_80
; init_stack
[BITS 32]
startup:
cld
cli
; with multiboot specs, grub initialzes the registers:
; EAX: magic value 0x2BADB002
; EBX: 32-bit physical address of the multiboot information struct
; we store them in global variables for future uses in rust code.
mov dword [mb_magic], eax
mov dword [mb_info_addr], ebx
; setup GDT by loading GDT descriptor
; see docs/x86_gdt.txt
lgdt [gdt_80]
; use the 3rd gdt entry for protected mode segmentations
mov eax, 3 * 0x8
mov ds, ax
mov es, ax
mov fs, ax
mov gs, ax
; define stack
mov ss, ax
mov esp, init_stack+STACKSIZE
init_longmode:
; activate address extension (PAE)
mov eax, cr4
or eax, 1 << 5
mov cr4, eax
setup_paging:
; zero out the initial page tables (2 pages in total)
mov edi, pml4
clear_pt:
mov byte [edi], 0
inc edi
cmp edi, pt_end
jne clear_pt
; Provisional identical page mapping, using 1G huge page, therefore only 2
; table levels needed. see docs/x86_paging.txt
; PML4 (Page Map Level 4 / 1st level)
mov eax, pdp
or eax, 0xf
mov dword [pml4+0], eax
mov dword [pml4+4], 0
; PDPE flags
mov eax, 0x0 | 0x87 ; start-address bytes bit [30:31] + flags
mov ebx, 0 ; start-address bytes bit [32:38]
mov ecx, 0
fill_tables2:
; fill one single PDP table, with 1G pages, 512 PDPE maps to 512 GB
cmp ecx, MAX_MEM
je fill_tables2_done
mov dword [pdp + 8*ecx + 0], eax ; low bytes
mov dword [pdp + 8*ecx + 4], ebx ; high bytes
add eax, 0x40000000 ; 1G per page
adc ebx, 0 ; overflow? -> increment higher-order half of the address
inc ecx
ja fill_tables2
fill_tables2_done:
; set base pointer to PML4
mov eax, pml4
mov cr3, eax
activate_long_mode:
; activate Long Mode (for now in compatibility mode)
; select EFER (Extended Feature Enable Register)
mov ecx, 0x0C0000080
rdmsr
or eax, 1 << 8 ; LME (Long Mode Enable)
wrmsr
; activate paging
mov eax, cr0
or eax, 1 << 31
mov cr0, eax
; use the 2nd gdt entry (see definition below)
; jump to 64-bit code segment -> full activation of Long Mode
jmp 2 * 0x8 : longmode_start
[BITS 64]
; system start, part 2 (in 64-bit Long Mode)
longmode_start:
mov rdi, ___BSS_START__
clear_bss:
mov byte [rdi], 0
inc rdi
cmp rdi, ___BSS_END__
jne clear_bss
fninit ; activate FPU
init_sse:
; init SSE
; NOTE: must NOT use sse target features for rust compiler, if sse not enabled here.
;mov rax, cr0
;and rax, ~(1 << 2) ;clear coprocessor emulation CR0.EM
;or rax, 1 << 1 ;set coprocessor monitoring CR0.MP
;mov cr0, rax
;mov rax, cr4
;or rax, 3 << 9 ;set CR4.OSFXSR and CR4.OSXMMEXCPT at the same time
;mov cr4, rax
call _entry ; call the OS kernel's rust part.
cli ; Usually we should not get here.
hlt
[SECTION .data]
gdt:
; see docs/x86_gdt.txt
; GDT[0] should always be NULL descriptor
dw 0,0,0,0
; 32-bit code segment descriptor
; limit=0xFFFF, base=0
; Types: P|Ring0|Code/Data|Exec|NonConforming|Readable|NotAccessed
; Flags: 4K|32-bit|Not Long Mode
dw 0xFFFF
dw 0x0000
dw 0x9A00
dw 0x00CF
; 64-bit code segment descriptor
; limit=0xFFFF, base=0
; Types: P|Ring0|Code/Data|Exec|NonConforming|Readable|NotAccessed
; Flags: 4K|-|LongMode|-
dw 0xFFFF
dw 0x0000
dw 0x9A00
dw 0x00AF
; data segment descriptor
; limit=0xFFFF, base=0
; Types: Present|Ring0|Code/Data|NoExec|GrowUp|Writable|NotAccessed
; Flags: 4K|32-bit|Not Long Mode
dw 0xFFFF
dw 0x0000
dw 0x9200
dw 0x00CF
gdt_80:
dw 4*8 - 1 ; GDT limit=24, 4 GDT entries - 1
dq gdt ; GDT address
; multiboot info
mb_magic:
dd 0x00000000
mb_info_addr:
dd 0x00000000
[SECTION .bss]
global init_stack:data (init_stack.end - init_stack)
init_stack:
resb STACKSIZE
.end:
[SECTION .global_pagetable]
pml4:
resb 4096
alignb 4096
pdp:
resb 4096
alignb 4096
pt_end:
; reserve 8MiB for frame alloc.
; (see linker file)
[SECTION .global_free_page_stack]
free_page_stack:
resb 8388608
alignb 4096
|