%include "asm/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