pub fn loader()->&'static str{
"
;
; 功能:清除屏幕,检测处理器是否支持64位模式;若支持,加载内核,设置临时4级页表,并开启IA32E模式
; Author:fengchen Xie
; Time:2022-04-20
;
%include \"ospre/common/boot_loader_equ.asm\"
[bits 32]
org 0x7e00
section loader align=4
;清除屏幕
call clear_screen
;获取处理器支持的最大功能号,判断处理器是否支持IA32E模式
mov eax,0x80000000
cpuid
cmp eax,0x80000000
jle not_support_ia32e ;不支持则打印信息
mov eax,0x80000001
cpuid
bt edx,29
jnc not_support_ia32e ;不支持则打印信息
;读取loader64位程序部分,加载到0x100000处
mov ecx,LOADER2_ALL_SECTORS
mov eax,LOADER2_START_SECTOR
mov ebx,LOADER2_CACHE_ADDRESS
read_loader2:
call read_one_sector
add ebx,512
inc eax
loop read_loader2
;设置临时4级头表 、临时页目录指针表、临时页目录表
mov ebx,FOUR_PAGE_ADDRESS
mov ecx,1024*3
mov edi,0x00
.clear_tmp_page_space: ;清理4级头表、临时页目录指针表、临时页目录表所需空间
mov dword [ebx+edi*4],0x00
inc edi
loop .clear_tmp_page_space
mov eax,THRER_PAGE_ADDRESS_FIRST | 3
mov [FOUR_PAGE_ADDRESS+0x00],eax
mov dword [FOUR_PAGE_ADDRESS+0x04],0x00 ;将4级头表第一项指向临时页目录指针表
mov eax,TWO_PAGE_ADDRESS_FIRST | 3
mov [THRER_PAGE_ADDRESS_FIRST+0x00],eax
mov dword [THRER_PAGE_ADDRESS_FIRST+0x04],0x00 ;将临时页目录指针表的第一项指向临时页目录表
xor eax,eax
or eax,0x83
mov ebx,TWO_PAGE_ADDRESS_FIRST
mov ecx,5
shoot_ten_m:
mov dword [ebx+0x00],eax
mov dword [ebx+0x04],0x00 ;映射低端10M内存
add eax,0x200000
add ebx,8
loop shoot_ten_m
;将4级头表物理地址写入cr3
mov eax,FOUR_PAGE_ADDRESS
mov cr3,eax
;开启PAE物理地址扩展功能
mov eax,cr4
bts eax,5
mov cr4,eax
;设置型号专属寄存器IA32_EFER的IA32E模式允许位(第8位)
mov ecx,0xc0000080 ;IA32_EFER寄存器编号
rdmsr
bts eax,8
wrmsr
;开启分页功能,进入IA32E模式的兼容模式
mov eax,cr0
bts eax,31
mov cr0,eax
push dword 0x18
push dword LOADER2_CACHE_ADDRESS
retf ;使用retf进行远跳转,跳转到loader64位代码
;-----------------------------当检测到处理器不支持ia32e模式时,提示not support ia32e ----------------
not_support_ia32e:
mov ebx,0xb8000
mov esi,0x00
mov edi,0x00
mov eax,0x0700
.print_error:
mov al,[not_support_msg+edi]
cmp al,0x00
je .stop_print
mov word [ebx+esi*2],ax
inc edi
inc esi
loop .print_error
.stop_print:
cli
hlt
jmp $
;-----------------------------------------------清除屏幕例程---------------------------------------
clear_screen:
push eax
push ebx
push ecx
push edx
xor esi,esi
mov ebx,0xb8000
mov ecx,80*25
.clear:
mov word [ebx+esi*2],0x00
inc esi
loop .clear
pop edx
pop ecx
pop ebx
pop eax
ret
%include \"ospre/common/LBA28.asm\"
;----------------------------------------------下面是存放数据的区域------------------------------
section data
;不支持ia32e模式提示消息
not_support_msg:
db \"cpu does not support IA-32E\"
"
}
pub fn loader2()->&'static str{
"
%include \"ospre/common/boot_loader_equ.asm\"
TEMP_HEADER_ADDRESS equ 0x400000
[bits 64]
org 0x100000
section loader2
;读取程序头部扇区,计算程序头表和elf64所占扇区数
xor rax,rbx
mov rbx,TEMP_HEADER_ADDRESS
mov eax,KERNEL_START_SECTOR
call read_one_sector
xor rcx,rcx
mov cx,[TEMP_HEADER_ADDRESS+56] ;程序头表项数量
xor rax,rax
mov ax,[TEMP_HEADER_ADDRESS+54] ;程序头表项大小
xor rdx,rdx
mul cx
shl edx,16
mov dx,ax
add edx,64 ;当前edx中为程序头表+elf头大小
add edx,512 ;填补误差
mov rax,rdx
shr rax,9
xor rcx,rcx
mov ecx,eax ;当前ecx中为程序头表+elf头所占扇区
;读取程序头部扇区和程序头表,直接读取200个扇区,统计程序大小
xor rbx,rbx
mov rbx,TEMP_HEADER_ADDRESS
mov eax,KERNEL_START_SECTOR
; mov rcx,200
read_header:
call read_one_sector
inc eax
add rbx,512
loop read_header
;分析程序头表,统计程序大小
mov rbx,[TEMP_HEADER_ADDRESS+32]
mov rax,TEMP_HEADER_ADDRESS
add rbx,rax ;程序头表起始地址
xor rcx,rcx
mov cx,[TEMP_HEADER_ADDRESS+56] ;程序头表项数量
xor rax,rax
mov ax,[TEMP_HEADER_ADDRESS+54] ;程序头表项大小
xor rdi,rdi ;用于保存程序大小
calc_kernel_size:
mov rsi,[rbx+32]
add rdi,rsi
add rbx,rax
loop calc_kernel_size
xor rdx,rdx
mov cx,[TEMP_HEADER_ADDRESS+56] ;程序头表项数量
mul cx
shl edx,16
mov dx,ax
add rdi,rdx
add rdi,64 ;当前rdi=程序头+ELF头+程序段总大小
xor rax,rax
xor rcx,rcx
xor rdx,rdx
mov ax,[TEMP_HEADER_ADDRESS+58]
mov cx,[TEMP_HEADER_ADDRESS+60]
mul cx
shl edx,16
mov dx,ax
add rdi,rdx ;当前rdi=程序头+ELF头+程序段总大小+节头表
;根据程序大小分配地址空间
add rdi,0x200000 ;多分配2M左右的空间
mov rax,rdi
xor rdx,rdx
mov rbx,0x200000
div rbx ;此时rax中就是内核映像需要分配的页数(按2M分页)
mov r8,rax
shl r8,21
add r8,KERNEL_CACHE_ADDRESS ;使用r8记录内核重定位时的相对地址
mov rcx,rax
shl rcx,1 ;由于需要存放内核映像以及加载内核,因此需要分配两倍内存
lea rbx,[TWO_PAGE_ADDRESS_FIRST+5*8]
lea rax,[0x200000*5]
or rax,0x83
alloc_mem:
mov [rbx],rax
add rbx,8
add rax,0x200000
loop alloc_mem
;正式读取内核
mov rbx,KERNEL_CACHE_ADDRESS
xor rax,rax
mov eax,KERNEL_START_SECTOR
mov rcx,KERNEL_ALL_SECTORS
read_kernel:
call read_one_sector
add rbx,512
inc eax
loop read_kernel
;重定位内核程序,相对地址在r8中
mov rbx,[KERNEL_CACHE_ADDRESS+32]
mov rax,KERNEL_CACHE_ADDRESS
add rbx,rax ;程序头表起始地址
xor rcx,rcx
mov cx,[KERNEL_CACHE_ADDRESS+56] ;程序头表项数量
xor rax,rax
mov ax,[KERNEL_CACHE_ADDRESS+54] ;程序头表项大小
relloc_kernel:
mov rdi,[rbx+16]
add rdi,r8 ;各个段被加载的目的地址
mov rsi,KERNEL_CACHE_ADDRESS
add rsi,[rbx+8] ;各个段的源地址
mov r10,rcx
mov rcx,[rbx+32] ;当前段所占字节数
call mem_copy
mov rcx,r10
add rbx,rax
loop relloc_kernel
mov rax,[KERNEL_CACHE_ADDRESS+24]
add rax,r8 ;内核入口
jmp rax
;内存拷贝过程
mem_copy:
push rsi
push rdi
push rcx
cld
rep movsb
pop rcx
pop rdi
pop rsi
ret
;参数说明:eax为起始扇区号,rbx为缓冲区地址
read_one_sector:
push rax
push rbx
push rcx
push rdx
push rsi
mov rsi,rax
mov al,1
mov dx,0x1f2 ;向端口0x1f2写入读取的扇区数
out dx,al
mov rax,rsi
inc dx
out dx,al ;向端口0x1f3写入起始扇区号0-7位
inc dx
shr eax,8
out dx,al ;向端口0x1f4写入起始扇区号8-15位
inc dx
shr eax,8
out dx,al ;向端口0x1f5写入起始扇区号16-23位
inc dx
shr eax,8
or al,0xe0
out dx,al ;向端口0x1f6写入起始扇区号24-31位
inc dx
mov al,0x20
out dx,al ;向端口0x1f7写入磁盘读取命令0x20
.waits:
in al,dx
and al,0x88
cmp al,0x08
jne .waits
xor rcx,rcx
mov cx,256
mov dx,0x1f0
.read_word:
in ax,dx ;循环读取512字节
mov [rbx],ax
add rbx,2
loop .read_word
pop rsi
pop rdx
pop rcx
pop rbx
pop rax
ret
"
}