1
2
3pub fn loader()->&'static str{
4 "
5 ;
6 ; 功能:清除屏幕,检测处理器是否支持64位模式;若支持,加载内核,设置临时4级页表,并开启IA32E模式
7 ; Author:fengchen Xie
8 ; Time:2022-04-20
9 ;
10
11 %include \"ospre/common/boot_loader_equ.asm\"
12 [bits 32]
13 org 0x7e00
14 section loader align=4
15 ;清除屏幕
16 call clear_screen
17
18 ;获取处理器支持的最大功能号,判断处理器是否支持IA32E模式
19 mov eax,0x80000000
20 cpuid
21 cmp eax,0x80000000
22 jle not_support_ia32e ;不支持则打印信息
23
24 mov eax,0x80000001
25 cpuid
26 bt edx,29
27 jnc not_support_ia32e ;不支持则打印信息
28
29 ;读取loader64位程序部分,加载到0x100000处
30 mov ecx,LOADER2_ALL_SECTORS
31 mov eax,LOADER2_START_SECTOR
32 mov ebx,LOADER2_CACHE_ADDRESS
33 read_loader2:
34 call read_one_sector
35 add ebx,512
36 inc eax
37 loop read_loader2
38
39 ;设置临时4级头表 、临时页目录指针表、临时页目录表
40 mov ebx,FOUR_PAGE_ADDRESS
41 mov ecx,1024*3
42 mov edi,0x00
43 .clear_tmp_page_space: ;清理4级头表、临时页目录指针表、临时页目录表所需空间
44 mov dword [ebx+edi*4],0x00
45 inc edi
46 loop .clear_tmp_page_space
47
48
49 mov eax,THRER_PAGE_ADDRESS_FIRST | 3
50 mov [FOUR_PAGE_ADDRESS+0x00],eax
51 mov dword [FOUR_PAGE_ADDRESS+0x04],0x00 ;将4级头表第一项指向临时页目录指针表
52
53 mov eax,TWO_PAGE_ADDRESS_FIRST | 3
54 mov [THRER_PAGE_ADDRESS_FIRST+0x00],eax
55 mov dword [THRER_PAGE_ADDRESS_FIRST+0x04],0x00 ;将临时页目录指针表的第一项指向临时页目录表
56
57 xor eax,eax
58 or eax,0x83
59 mov ebx,TWO_PAGE_ADDRESS_FIRST
60 mov ecx,5
61 shoot_ten_m:
62 mov dword [ebx+0x00],eax
63 mov dword [ebx+0x04],0x00 ;映射低端10M内存
64 add eax,0x200000
65 add ebx,8
66 loop shoot_ten_m
67
68 ;将4级头表物理地址写入cr3
69 mov eax,FOUR_PAGE_ADDRESS
70 mov cr3,eax
71
72 ;开启PAE物理地址扩展功能
73 mov eax,cr4
74 bts eax,5
75 mov cr4,eax
76
77 ;设置型号专属寄存器IA32_EFER的IA32E模式允许位(第8位)
78 mov ecx,0xc0000080 ;IA32_EFER寄存器编号
79 rdmsr
80 bts eax,8
81 wrmsr
82
83 ;开启分页功能,进入IA32E模式的兼容模式
84 mov eax,cr0
85 bts eax,31
86 mov cr0,eax
87 push dword 0x18
88 push dword LOADER2_CACHE_ADDRESS
89 retf ;使用retf进行远跳转,跳转到loader64位代码
90
91
92 ;-----------------------------当检测到处理器不支持ia32e模式时,提示not support ia32e ----------------
93 not_support_ia32e:
94 mov ebx,0xb8000
95 mov esi,0x00
96 mov edi,0x00
97 mov eax,0x0700
98
99 .print_error:
100 mov al,[not_support_msg+edi]
101 cmp al,0x00
102 je .stop_print
103 mov word [ebx+esi*2],ax
104 inc edi
105 inc esi
106 loop .print_error
107
108 .stop_print:
109 cli
110 hlt
111 jmp $
112
113 ;-----------------------------------------------清除屏幕例程---------------------------------------
114 clear_screen:
115 push eax
116 push ebx
117 push ecx
118 push edx
119
120 xor esi,esi
121 mov ebx,0xb8000
122 mov ecx,80*25
123 .clear:
124 mov word [ebx+esi*2],0x00
125 inc esi
126 loop .clear
127
128 pop edx
129 pop ecx
130 pop ebx
131 pop eax
132
133 ret
134
135 %include \"ospre/common/LBA28.asm\"
136
137 ;----------------------------------------------下面是存放数据的区域------------------------------
138 section data
139
140 ;不支持ia32e模式提示消息
141 not_support_msg:
142 db \"cpu does not support IA-32E\"
143
144 "
145}
146
147
148
149
150
151
152
153
154
155
156
157
158pub fn loader2()->&'static str{
159 "
160
161 %include \"ospre/common/boot_loader_equ.asm\"
162
163 TEMP_HEADER_ADDRESS equ 0x400000
164
165 [bits 64]
166
167 org 0x100000
168 section loader2
169 ;读取程序头部扇区,计算程序头表和elf64所占扇区数
170 xor rax,rbx
171 mov rbx,TEMP_HEADER_ADDRESS
172 mov eax,KERNEL_START_SECTOR
173 call read_one_sector
174
175 xor rcx,rcx
176 mov cx,[TEMP_HEADER_ADDRESS+56] ;程序头表项数量
177
178 xor rax,rax
179 mov ax,[TEMP_HEADER_ADDRESS+54] ;程序头表项大小
180
181 xor rdx,rdx
182 mul cx
183 shl edx,16
184 mov dx,ax
185 add edx,64 ;当前edx中为程序头表+elf头大小
186 add edx,512 ;填补误差
187 mov rax,rdx
188 shr rax,9
189 xor rcx,rcx
190 mov ecx,eax ;当前ecx中为程序头表+elf头所占扇区
191
192
193 ;读取程序头部扇区和程序头表,直接读取200个扇区,统计程序大小
194 xor rbx,rbx
195 mov rbx,TEMP_HEADER_ADDRESS
196 mov eax,KERNEL_START_SECTOR
197 ; mov rcx,200
198 read_header:
199 call read_one_sector
200 inc eax
201 add rbx,512
202 loop read_header
203
204 ;分析程序头表,统计程序大小
205 mov rbx,[TEMP_HEADER_ADDRESS+32]
206 mov rax,TEMP_HEADER_ADDRESS
207 add rbx,rax ;程序头表起始地址
208
209 xor rcx,rcx
210 mov cx,[TEMP_HEADER_ADDRESS+56] ;程序头表项数量
211
212 xor rax,rax
213 mov ax,[TEMP_HEADER_ADDRESS+54] ;程序头表项大小
214
215 xor rdi,rdi ;用于保存程序大小
216
217 calc_kernel_size:
218 mov rsi,[rbx+32]
219 add rdi,rsi
220 add rbx,rax
221 loop calc_kernel_size
222
223 xor rdx,rdx
224 mov cx,[TEMP_HEADER_ADDRESS+56] ;程序头表项数量
225 mul cx
226 shl edx,16
227 mov dx,ax
228
229 add rdi,rdx
230 add rdi,64 ;当前rdi=程序头+ELF头+程序段总大小
231
232 xor rax,rax
233 xor rcx,rcx
234 xor rdx,rdx
235 mov ax,[TEMP_HEADER_ADDRESS+58]
236 mov cx,[TEMP_HEADER_ADDRESS+60]
237
238 mul cx
239 shl edx,16
240 mov dx,ax
241
242 add rdi,rdx ;当前rdi=程序头+ELF头+程序段总大小+节头表
243
244 ;根据程序大小分配地址空间
245 add rdi,0x200000 ;多分配2M左右的空间
246 mov rax,rdi
247 xor rdx,rdx
248 mov rbx,0x200000
249 div rbx ;此时rax中就是内核映像需要分配的页数(按2M分页)
250
251 mov r8,rax
252 shl r8,21
253 add r8,KERNEL_CACHE_ADDRESS ;使用r8记录内核重定位时的相对地址
254
255 mov rcx,rax
256 shl rcx,1 ;由于需要存放内核映像以及加载内核,因此需要分配两倍内存
257 lea rbx,[TWO_PAGE_ADDRESS_FIRST+5*8]
258 lea rax,[0x200000*5]
259 or rax,0x83
260
261 alloc_mem:
262 mov [rbx],rax
263 add rbx,8
264 add rax,0x200000
265 loop alloc_mem
266
267 ;正式读取内核
268 mov rbx,KERNEL_CACHE_ADDRESS
269 xor rax,rax
270 mov eax,KERNEL_START_SECTOR
271 mov rcx,KERNEL_ALL_SECTORS
272 read_kernel:
273 call read_one_sector
274 add rbx,512
275 inc eax
276 loop read_kernel
277
278
279
280
281 ;重定位内核程序,相对地址在r8中
282
283 mov rbx,[KERNEL_CACHE_ADDRESS+32]
284 mov rax,KERNEL_CACHE_ADDRESS
285 add rbx,rax ;程序头表起始地址
286
287 xor rcx,rcx
288 mov cx,[KERNEL_CACHE_ADDRESS+56] ;程序头表项数量
289
290 xor rax,rax
291 mov ax,[KERNEL_CACHE_ADDRESS+54] ;程序头表项大小
292
293
294 relloc_kernel:
295 mov rdi,[rbx+16]
296 add rdi,r8 ;各个段被加载的目的地址
297
298 mov rsi,KERNEL_CACHE_ADDRESS
299 add rsi,[rbx+8] ;各个段的源地址
300
301 mov r10,rcx
302
303 mov rcx,[rbx+32] ;当前段所占字节数
304 call mem_copy
305
306 mov rcx,r10
307
308 add rbx,rax
309
310 loop relloc_kernel
311
312 mov rax,[KERNEL_CACHE_ADDRESS+24]
313 add rax,r8 ;内核入口
314
315 jmp rax
316
317
318
319 ;内存拷贝过程
320 mem_copy:
321 push rsi
322 push rdi
323 push rcx
324
325 cld
326 rep movsb
327
328 pop rcx
329 pop rdi
330 pop rsi
331
332 ret
333
334
335 ;参数说明:eax为起始扇区号,rbx为缓冲区地址
336 read_one_sector:
337 push rax
338 push rbx
339 push rcx
340 push rdx
341 push rsi
342
343 mov rsi,rax
344
345 mov al,1
346 mov dx,0x1f2 ;向端口0x1f2写入读取的扇区数
347 out dx,al
348
349 mov rax,rsi
350
351 inc dx
352 out dx,al ;向端口0x1f3写入起始扇区号0-7位
353
354 inc dx
355 shr eax,8
356 out dx,al ;向端口0x1f4写入起始扇区号8-15位
357
358 inc dx
359 shr eax,8
360 out dx,al ;向端口0x1f5写入起始扇区号16-23位
361
362 inc dx
363 shr eax,8
364 or al,0xe0
365 out dx,al ;向端口0x1f6写入起始扇区号24-31位
366
367 inc dx
368 mov al,0x20
369 out dx,al ;向端口0x1f7写入磁盘读取命令0x20
370
371 .waits:
372 in al,dx
373 and al,0x88
374 cmp al,0x08
375 jne .waits
376
377 xor rcx,rcx
378 mov cx,256
379 mov dx,0x1f0
380
381 .read_word:
382 in ax,dx ;循环读取512字节
383 mov [rbx],ax
384 add rbx,2
385 loop .read_word
386
387 pop rsi
388 pop rdx
389 pop rcx
390 pop rbx
391 pop rax
392
393 ret
394
395
396 "
397}