ospre/bootloader/loader/
mod.rs

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}